feat(agent-core-v2): add a fork parameter to the Agent tool - #3007
feat(agent-core-v2): add a fork parameter to the Agent tool#30077Sageer wants to merge 11 commits into
Conversation
|
@codex review |
🦋 Changeset detectedLatest commit: 65af0ca The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6916cb4847
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const requestedProfileName = args.subagent_type?.length | ||
| ? args.subagent_type | ||
| : fork | ||
| ? (own.profileName ?? DEFAULT_PROFILE_NAME) | ||
| : DEFAULT_PROFILE_NAME; |
There was a problem hiding this comment.
Preserve the caller's live binding when forking
When the caller's profile has changed after its initial bind, this reconstructs the child from the catalog profile name rather than copying profile.data(). For example, TowerInitTool enables the remaining Tower tools via addActiveTool, but a subsequent fork: true child is rebound to the base agent profile and loses those tools; prompt/profile snapshots can similarly diverge after catalog changes. Use the existing snapshot semantics in IAgentLifecycleService.fork/applyBindingSnapshot, while trimming the inherited history, so the advertised profile and tool-set inheritance is real.
Useful? React with 👍 / 👎.
| const requestedProfileName = args.subagent_type?.length | ||
| ? args.subagent_type | ||
| : DEFAULT_PROFILE_NAME; | ||
| const fork = args.fork === true; |
There was a problem hiding this comment.
Match fork approvals against the caller profile
When fork: true is passed without subagent_type—the documented form—the earlier profileNameForDisplay calculation still falls back to coder, even though this branch actually launches the caller's profile (normally agent). The activity UI is therefore mislabeled, and more importantly matchesRule evaluates permission rules against coder, so an Agent(coder) rule can approve launching a different profile. Resolve the fork's display and rule subject from this.profile.data().profileName.
Useful? React with 👍 / 👎.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c96eaa17a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const requestedProfileName = args.subagent_type?.length | ||
| ? args.subagent_type | ||
| : fork | ||
| ? (own.profileName ?? DEFAULT_PROFILE_NAME) | ||
| : DEFAULT_PROFILE_NAME; |
There was a problem hiding this comment.
Allow forks after the source profile is withdrawn
When a workspace, user, or plugin profile is withdrawn after the caller has bound it, fork: true derives that profile name here but the later unconditional catalog.get(requestedProfileName) returns undefined, so the fork fails with PROFILE_UNKNOWN even though the caller still has the complete live binding snapshot. Fresh evidence after the earlier review is that the snapshot overlay was added, but the catalog lookup remains a prerequisite; create the child unbound and apply the snapshot, or otherwise avoid requiring the old catalog entry for forks.
AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L5-L7
Useful? React with 👍 / 👎.
| const binding = fork | ||
| ? { model: own.modelAlias, thinking: own.thinkingLevel } |
There was a problem hiding this comment.
Record the source agent on forked children
For every fork: true launch, this path still creates the child without passing forkedFrom, so registerAgent persists forkedFrom: undefined; session metadata consumers therefore cannot distinguish this fork from an ordinary subagent spawn. The lifecycle's existing fork() path records the source ID specifically as durable provenance, so this create call should likewise pass the caller agent ID when fork is set.
Useful? React with 👍 / 👎.
Spawning with fork: true starts the subagent from a one-time snapshot of the calling agent's completed conversation history — same profile, tool set, and model — instead of zero context. The seed trims the trailing open tool exchange (the in-flight Agent call itself) before appending into the child's context memory, and the first prompt carries an inheritance notice framing the seeded history as reference material. Fork rejects resume, a different subagent_type, or a model override as tool errors, and skips the subagents allowlist since a self-inheritance is not a delegation.
…gent Subagents share the session todo list but no longer receive the stale-todo nudge — the reminder injector now registers only on the main agent, so delegated and forked agents are not prompted to maintain a list they do not own.
…launches correctly Review follow-ups for the Agent tool fork mode: - overlay the caller's live profile.data() via applyBindingSnapshot after the catalog re-bind, so ephemeral addActiveTool deltas, the rendered system prompt, and runtime model/subagents updates survive the fork; skip the profile prompt prefix since the caller's prefixed first prompt is already part of the seeded history - resolve the fork activity label and approval-rule subject from the caller's own profile instead of falling back to the default subagent type, so an Agent(<other profile>) rule cannot approve a fork
…rimming them Fork seeding now answers the source's trailing open tool calls with a synthetic in-flight result instead of cutting the whole trailing exchange: the seeded history stays protocol-valid, keeps the source's final step visible as reference, and no longer confuses side-question (btw) agents forked while the main agent is mid-turn. The close helper is shared by the Agent tool fork and IAgentLifecycleService.fork. Fork launches also stop requiring the caller's profile to still exist in the session catalog: the child is created unbound and overlaid with the caller's live binding snapshot, matching the lifecycle fork path, and now records forkedFrom provenance.
|
@codex review |
commit: |
|
Codex Review: Didn't find any major issues. 🎉 Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
# Conflicts: # packages/agent-core-v2/test/agent/fullCompaction/fullCompaction.test.ts # packages/agent-core-v2/test/agent/loop/loop.test.ts # packages/agent-core-v2/test/tool/tool.test.ts
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4b8095d64d
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| created = plan.fork | ||
| ? await this.agentLifecycle.fork(opts.callerAgentId, { labels: opts.labels }) |
There was a problem hiding this comment.
Bind the fork profile before activating child tools
When the caller uses a restrictive profile, this fork path creates the child unbound: AgentLifecycleService.create() activates tool contributions while activeToolNames and disallowedTools are unset, which registers every available tool, and the subsequent applyBindingSnapshot() does not deactivate those registrations. Consequently a fork of a read-only or otherwise restricted agent can gain tools such as Write or Bash instead of inheriting the caller's tool set. Fresh evidence beyond the earlier live-binding comment is the finalized delegation to this existing lifecycle path, whose activation ordering leaves already-registered tools intact; seed the snapshot before activation or explicitly reconcile/deactivate tools afterward.
Useful? React with 👍 / 👎.
# Conflicts: # packages/agent-core-v2/test/features/swarm/swarm.test.ts
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Related Issue
No linked issue — the problem is explained below.
Problem
The Agent tool always spawns subagents with zero context: the entire background for a task must be re-briefed into the prompt. For work that builds on the current conversation (continuing an exploration, letting a specialized profile act on what was already established), the subagent cannot see any of it, and re-briefing is both lossy and token-expensive.
What changed
forkparameter (default false, current behavior unchanged). Withfork: truethe subagent starts from a one-time snapshot of the calling agent's conversation history — seeded into the child's context memory as ordinary, replayablecontext.append_messagerecords — then receives the task prompt prefixed with an inheritance notice framing the seeded messages as reference material, not its own experience.trimTrailingOpenToolExchangesemantics this PR started with), the seed now closes it: each unanswered trailing tool call gets a synthetic result marking it as still executing with an unknown outcome. The seeded conversation stays protocol-valid, keeps the parent's final step visible as reference, and partially answered parallel batches keep their completed results.IAgentLifecycleService.fork(extended with label passthrough), which already backs side-question (btw) agents — so the close helper also fixes btw agents forked while the main agent is mid-turn reporting the in-flight tool call's result as lost.resume, a differentsubagent_type, or amodeloverride is rejected as a tool error, and the subagents allowlist is skipped since self-inheritance is not a delegation. Because the child is overlaid with the caller's live binding snapshot rather than re-resolved from the catalog, a fork works even after the caller's profile leaves the session catalog, and recordsforkedFromprovenance in the session metadata.when, so a forked child silently lost CreateGoal/GetGoal/SetGoalBudget/UpdateGoal from its tool section — breaking the identical-tool-surface promise above and invalidating the inherited prompt-cache prefix (tools serialize ahead of messages). Registration is now profile-driven like every other tool, while subagent authority stays enforced at execution time by the goal service's existing agent check: a forked child advertises the tools (cache parity) and gets a stablegoal.unsupported_agentrejection if it calls one. The convention — the registration surface must be a pure function of session-shared facts, capability differences live at execution time — is documented onAgentToolContributionOptions.whenand in the package AGENTS.md.ISessionSubagentService.planSpawn/spawn, shared by the Agent tool and AgentSwarm instead of being near-verbatim copies in three places. One visible consequence: spawn configuration errors (unknown agent type, unresolvable model) now fail upfront at plan time instead of surfacing as identical per-task failures across a swarm.forkparameter: every item-spawned subagent starts from the caller's snapshot (labels keep theirswarmItem), and fork combined withresume_agent_ids,subagent_type, ormodelis rejected. The tool description states when fork is appropriate and when independent tasks should stay zero-context.fork: true.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.