feat(agent): add durable agent invocations and spawning - #1065
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
2f3b985 to
b3aea1f
Compare
|
Review exercise result: the current recovery path still has one merge blocker. An empty resume wake sets |
Empty resume wakes never become final delivery attempts, so stranded running children with no model or resumable boundary must fail their session immediately and project onto the invocation instead of throwing and requeueing forever. Co-Authored-By: David Cramer <david@sentry.io>
Resolve conflicts with current main while keeping spawn/agent-invocation wiring, provider conversation bindings, scheduled-task heartbeats, and destination-aware slack work. Renumber the agent invocation schema migration to 0020_sour_vargas after main's newer conversation history and reporting migrations. Co-Authored-By: David Cramer <david@sentry.io>
|
Addressed the stranded-invocation merge blocker and brought the branch current with Fix
Main integration
Local integration verification could not run here (no Docker/Postgres). Relying on CI for |
Main renamed juniorToolResultSchema to juniorToolOutputSchema and dropped ok/status envelope fields. Update spawnAgent and its unit expectations, and pass the required Slack destination after requireSlackDestination. Co-Authored-By: David Cramer <david@sentry.io>
authorizationFlowMode was only interactive vs disabled. Model it as disabledFeatures: ["interactive-auth"] with the same default-on behavior, and leave a TODO that child runs may later need a way to force auth. Co-Authored-By: David Cramer <david@sentry.io>
There was a problem hiding this comment.
Second pass after the design lock. This now matches the intended model:
- named and unnamed are the same work shape; a name only keeps the child conversation id / history
- bindings are history handles only (no sticky reasoning/model policy)
- child runs pin
disabledFeatures: ["handoff", "interactive-auth", "subagents"]and cannot hand off or recursively spawn - stranded unrecoverable children terminalize instead of requeueing forever
- migration/purge/idempotency/busy locking look coherent, and the integration coverage hits the important paths
Known follow-ups are already tracked outside this slice (getAgentResult / parent delivery on #880, forced child interactive-auth on #881/#883). One residual local gap: CLI wires spawn + the in-process worker but not the production mailbox-append heartbeat repair, so a crash between create and append can leave a named agent busy across local runs. Not a production blocker; worth a small follow-up if local spawn durability matters.
Looks good to ship for the #879 cut.
Keep the model-facing subagent tool off by default so the durable invocation runtime can ship without exposing incomplete spawn UX. Enable with JUNIOR_SUBAGENTS_ENABLED=true when ready to roll out. Co-Authored-By: David Cramer <david@sentry.io>
|
Gate the model-facing Change
Local |
Production keeps spawnAgent off via botConfig. Opt the junior and junior-evals test runners in so agent-invocation coverage exercises the real wiring path without per-test env setup. Co-Authored-By: David Cramer <david@sentry.io>
Replace the one-off JUNIOR_SUBAGENTS_ENABLED flag with a Next.js-style
createApp({ experimental }) surface so unstable features share one
rollout path. Keep JUNIOR_EXPERIMENTAL as a comma-separated env fallback
for CLI/tests, and gate spawnAgent on experimental.subagents.
Co-Authored-By: David Cramer <david@sentry.io>
|
Replaced the one-off subagents flag with a standard experimental opt-in bag. Pattern (Next.js-style): await createApp({
experimental: {
subagents: true,
},
});Details
Local |
Drop JUNIOR_EXPERIMENTAL. Opt-in is only createApp({ experimental }).
Test suites enable subagents through the same flag via setupFiles.
Co-Authored-By: David Cramer <david@sentry.io>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f3c9278. Configure here.
Local chat wires spawn + child workers outside createApp. Opt into experimental.subagents there so the model-facing tool matches that path.
Bring the branch current with main before rechecking the agent-invocation migration chain. Co-Authored-By: David Cramer <david@sentry.io>
Main added this unit fixture without the PR's ConversationStore.createChild method. Keep the mock complete after merging main. Co-Authored-By: David Cramer <david@sentry.io>
|
Brought the branch current with Note Follow-up
|
Reject new spawnAgent work once a parent already has 8 non-terminal children in flight so prompt-driven fan-out cannot enqueue unbounded paid child turns. Named busy-locking is unchanged; idempotent replay still bypasses the count.

Adds durable agent invocations and a
spawnAgenttool so a parent agent can schedule named or one-off child work through Junior's existing conversation mailbox and lease worker.Named and unnamed children are the same work shape. A name only keeps the same child conversation id so later inputs continue that child's history. Unnamed invocations get an invocation-scoped child. Different named agents can run concurrently, repeated tool calls are idempotent, overlapping work for one name is rejected, and one child's failure does not affect its siblings.
Child execution policy
Child runs pin optional capabilities with one list:
That replaces one-off disable flags /
authorizationFlowMode.interactive-authis the old interactive-vs-disabled auth pause switch: omit it to allow OAuth pause links, include it to hard-fail instead. TODO: children may later need a way to force interactive auth when a delegated tool requires credentials. Optionalreasoning_levelis per-invocation only; bindings do not store sticky reasoning or model profile.The model supplies only the task, optional name, and optional reasoning level. The runtime keeps parent identity, actor, credentials, routing, and idempotency bound to the active run, and the tool returns only a durable invocation handle. Child creation shares the root conversation mutation lock with retention purge, and bindings and invocations are deleted with the conversation tree.
Child work uses the existing provider-neutral mailbox worker. Local chat processes child wakes in-process, while production continues to use Vercel Queues. The schema change is migration
0021_first_warhawkafter main's0020_restrict_task_execution_kinds.Unrecoverable stranded running children (missing model or no resumable boundary) fail the session immediately and project onto the invocation, so empty resume wakes cannot requeue forever or permanently occupy a named agent.
SQL-backed regression coverage exercises isolated and named agents, history reuse with per-task reasoning, concurrent replay, overlap rejection, sibling failure isolation, validation failure, recovery (including unrecoverable stranded sessions), and retention cleanup. This slice returns invocation handles; parent result delivery, deterministic result recovery, cancellation, and recursive children remain follow-up work on #880.
Fixes #879
Refs #880