Skip to content

bug(goal): explicit 模式下启动或恢复 Goal 后未开放结束目标所需工具 #464

Description

@tt-a1i

问题

默认 explicit discovery 下,/goal <objective> 创建 Goal、或恢复已有 active Goal 后,Runtime 可以开始续跑,但模型完成或报告阻塞所需的 get_goal / update_goal 仍可能被 capability gate 隐藏。

这使 Goal 的执行状态与模型可用工具脱节:模型已经收到“完成后调用 update_goal”的续跑指令,却没有这个工具。

价值

让用户已经明确启动的 Goal 能正常查看状态、报告阻塞并完成收尾,避免工作完成后仍继续消耗回合;普通新 Session 继续保持零常驻 OpenPI 工具。

审计边界

  • 检查日期:2026-09-08。
  • 固定源码:ed9dbc1018f890fd54375f5371990ddfee8af5df
  • 已验证:静态调用链,以及实际共享工具面、续跑模板和意图分类器的纯函数组合复现。
  • 未验证:安装后的 Pi TUI/RPC、真实 provider 请求和模型续跑表现;本文不声称观察到了实际失控或无限调用。

触发方式

  1. 使用默认 capabilities.discovery = explicit,进入未加载 session 能力组的新 Session。
  2. 执行 /goal 修复 README 的一个拼写错误;或者重新打开具有相同 objective 的 active Goal Session。
  3. Goal 创建/恢复后启动 continuation;session 能力组仍未加载,get_goal / update_goal 不在活动工具面。

如果当前 Session 已经通过自然语言请求或 gateway 加载 session 组,这个前提不成立;adaptive 下模型也可能通过 gateway 自行恢复工具面。

根因证据

最小机制复现

在上述源码 checkout 运行,不需要安装依赖或调用模型:

node --experimental-strip-types --input-type=module <<'JS'
import { resetOpenPiToolSurface, patchOwnedTools,
  getLoadedOpenPiCapabilities, OPENPI_TOOL_SURFACE
} from './extensions/shared/tool-surface.ts';
import { capabilitiesRequestedByPrompt } from './extensions/shared/capability-intent.ts';
import { continuationPrompt } from './extensions/goal/prompts.ts';
const listeners = new Map();
const registered = ['read','bash','edit','write','openpi_load_tools',
  'create_goal','get_goal','update_goal'];
let active = [...registered];
const pi = {
  events: {
    on(name, fn) { const all = listeners.get(name) ?? []; all.push(fn); listeners.set(name, all); },
    emit(name, data) { for (const fn of listeners.get(name) ?? []) fn(data); }
  },
  getActiveTools: () => [...active],
  getAllTools: () => registered.map(name => ({ name })),
  setActiveTools: next => { active = [...next]; }
};
resetOpenPiToolSurface(pi);
patchOwnedTools(pi, 'goal', { disable: OPENPI_TOOL_SURFACE.goal.deferred });
patchOwnedTools(pi, 'goal', { enable: OPENPI_TOOL_SURFACE.goal.deferred });
const prompt = continuationPrompt({
  objective:'修复 README 的一个拼写错误', tokensUsed:0, timeUsedSeconds:0
});
console.log(JSON.stringify({ loaded:getLoadedOpenPiCapabilities(pi), active,
  continuationClassified:capabilitiesRequestedByPrompt(prompt),
  canComplete:active.includes('update_goal') }, null, 2));
JS

已得到:loaded=[]active=[read,bash,edit,write]continuationClassified=[]canComplete=false

这证明两道工具门的组合结果;不把该纯机制复现当作完整 Pi 集成验收。

为什么当前测试没有拦住

Goal 的 extensionHarness 没有提供 pi.events
共享工具面的 stateFor fallback 在缺少 event bus 时把所有组视为已加载,所以单独测试 Goal extension 无法覆盖真实 capability gate。

已有保护与建议范围

用户仍可通过 /goal pause / /goal clear 停止目标;token budget(若设置)与 1000 次 emergency continuation breaker 仍有效。问题是模型正常完成/阻塞路径不可达,并非这些保护失效。

建议从 Goal owner 的已授权生命周期恢复正确工具投影,保留 explicit 默认和普通 Session 的零工具面。不要通过在 continuation 文案中塞入某个匹配词来绕过 gate。

验收

  • 同时加载 capabilities + goal 的真实 Pi fixture 覆盖新 Session /goal <objective>
  • 首个 continuation 的 provider snapshot 包含 get_goal / update_goal,不依赖 objective 命中词法规则。
  • reload/resume 已存在 active Goal 同样恢复所需工具;fork 仍遵守原有延迟续跑约定。
  • 调用 update_goal({status:"complete"}) 后不再追加 continuation;blocked 路径仍保留三回合审计。
  • 未创建/恢复 Goal、未加载任何能力的新 Session 仍没有常驻 OpenPI 模型工具。
  • 测试显式包含 event bus,不再依靠“缺少 events 则所有组可见”的 fallback 验证集成行为。

Related: #192 Goal 生命周期文档#194 模型上下文边界。本 Issue 跟踪具体运行时缺陷,不替代这两项文档工作。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions