Repository navigation
refactor(agent): gate sustained goals behind explicit /goal - #4844
Conversation
chengyongru
left a comment
There was a problem hiding this comment.
Automated review from nanobot
Clean refactor of the sustained-goal surface into runtime-gated create_goal/update_goal with a per-run ToolRegistryView. The within-run lifecycle (create -> update -> complete hiding goal tools again) is well covered by test_goal_tool_visibility_changes_within_runner_turn, and backward compatibility with existing active goals is preserved (same GOAL_STATE_KEY / status == "active" contract).
Two minor notes below; neither blocks merge.
- The repo-level
AGENTS.mdstill describes the old surface: line 40 referenceslong-running tasks / sustained goals (`long_task.py`)(nowgoal.py) and line 49 lists thelong-goalskill (removed by this PR). Worth updating in this PR so the architecture doc stays accurate. - Inline comment on the goal runtime section: it can go stale relative to the tool view within a single turn.
| return None | ||
| objective_guidance = render_template("agent/goal_objective.md", strip=True) | ||
| template = "agent/goal_active.md" if mode == "active" else "agent/goal_create.md" | ||
| return render_template( |
There was a problem hiding this comment.
The goal runtime section is rendered once here at message-build time (in _state_build), but the ToolRegistryView returned by _goal_filtered_tools is re-evaluated every LLM round against the live session metadata. So within a single /goal turn, after create_goal fires the system prompt still carries the goal_create.md guidance ("call create_goal") while create_goal is already hidden and only update_goal is visible. The mismatch is low-impact (the create_goal tool result and the visible update_goal tool steer the model correctly, and the section is rebuilt correctly on the next turn / continuation), but if you want the prompt and tool list to stay in lockstep mid-turn, the section would need to be recomputed per round like the tool view is.
22828d6 to
23561ce
Compare
23561ce to
8a79b88
Compare
chengyongru
left a comment
There was a problem hiding this comment.
Automated review from nanobot
I found two actionable runtime-gating issues. The initial clarification path drops out of goal creation entirely, and filtered registry errors can reveal goal tools that are meant to stay hidden on normal turns.
|
|
||
| If the objective is clear enough, call `create_goal` with a concise, durable | ||
| objective before doing substantial execution work. If it is too ambiguous to form | ||
| a safe objective, ask a clarifying question instead of creating a goal. |
There was a problem hiding this comment.
This clarification branch cannot resume goal creation. create mode is derived only from the current inbound message metadata; because no goal or pending-create state is persisted when the assistant asks this question, the user's next ordinary reply is classified as normal and exposes neither goal tool. The user therefore has to know to repeat /goal, even though this prompt asks them only to answer the clarification, so ambiguous /goal requests fall out of the lifecycle.
| f"Error: Tool '{name}' not found. Available: {', '.join(self.tool_names)}" | ||
| ) | ||
| ) | ||
| return self._registry.prepare_call(name, params) |
There was a problem hiding this comment.
Delegating unknown names to the underlying registry bypasses the view's filtered error surface. On a normal turn, a common near miss such as createGoal passes _allowed because only the exact create_goal name is gated; the underlying registry then suggests create_goal and lists both hidden goal tools as available. They still cannot execute through the view, but the error teaches the model about capabilities this view is meant to hide and can trigger pointless retries.
|
Pushed two maintainer fixes for the review findings:
Verification: focused goal/context/runner/registry suite ( |
|
Reworked the PR to the conservative opt-in design in
Focused cross-module verification passes locally ( |
5c5f472 to
af9b0be
Compare
5f9604f to
1884d99
Compare
64dad04 to
f6573d8
Compare
Re-bin
left a comment
There was a problem hiding this comment.
Automated review generated by Codex.
One small follow-up: internal continuations currently retain goal_requested / original_command, so a synthetic continuation still receives fresh /goal creation guidance even though it has no mutation scope. The permission guard prevents an unauthorized state change, but this can still prompt redundant create_goal attempts and extra token use. Could we strip those command-only markers when building continuation metadata and add a regression test for that path?
f6573d8 to
87b0a0c
Compare
87b0a0c to
7e9269f
Compare
Replace the legacy long-goal skill contract with command-scoped goal tools and runtime guidance. Keep goal state durable across continuations while restricting create and replace mutations to explicit user /goal turns.
7e9269f to
5fd6853
Compare
Re-bin
left a comment
There was a problem hiding this comment.
LGTM. This now lands as one coherent change: sustained goals are explicitly gated behind /goal, while the dependent runtime-context layer keeps ordinary prompts stable and only opts into model-visible state for active goals and attached CLI Apps.
The follow-up review closed the lifecycle boundaries around internal context: continuations cannot inherit fresh /goal authority, forks do not carry source-session runtime blocks, consolidation/Dream and raw archives do not absorb host-only guidance, and SDK restore validates before mutation.
The combined exact head passed the full GitHub Actions matrix on Ubuntu and Windows (Python 3.13/3.14) plus WebUI. Focused local tests, WebUI tests, and the production build also passed.
Summary
This PR refactors the existing sustained-goal workflow without adding a new top-level capability.
long_task/complete_goalcontract and built-inlong-goalskill withcreate_goal/update_goalplus a compact runtime prompt template./goal <task>turn before goal creation or replacement; ordinary messages, automation turns, internal continuations, and stale history cannot grant that permission.complete,cancel, orblock, while allowing deliberate replacement during the active/goalrun.Architecture
/goalcommand adds a goal-owned permission context through the genericturn_scopesseam.AgentLooponly enters and restores generic turn scopes; goal-specific permission state stays outside the core request-context module.RequestContextfor routing and session ownership.Compatibility
thread_goalmetadata remains readable and is migrated togoal_stateon mutation.Verification
uv run ruff checkpassed for the changed Python surfaces.main.main.