feat: add _session/stop_tasks to stop background sub-agents - #995
Open
jstrunk wants to merge 1 commit into
Open
Conversation
Closes agentclientprotocol#994 session/cancel aborts the turn, but the SDK's background tasks (sub-agents) survive it by design — and in the settled-dispatch shape there is no turn to cancel at all, so a client's stop button is otherwise a no-op while the sub-agents run on invisibly. There was no ACP surface to stop them. Adds a request extension method mirroring _session/steering: _session/stop_tasks { sessionId, toolUseIds?: string[] } -> { stopped: string[], notFound: string[] } (parent tool_use ids) With toolUseIds, stop the live tasks whose spawning Agent/Task call id matches; without, stop every live isSubagent task (background Bash spared, matching the discriminator used across the liveBackgroundTasks registry). Each stop is session.query.stopTask(taskId); a per-task failure degrades to notFound rather than failing the batch. The SDK emits a task_notification with status 'stopped', which the existing terminal path turns into a card close. Capability advertised at initialize via _meta.stopTasks.supported. Adds unit tests covering: stop by explicit toolUseIds, stop-all sub-agents with Bash spared, per-task stopTask failure degrading to notFound without failing the batch, and the capability advertisement at initialize.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #994
What
Adds a request extension method that lets a client stop the background tasks (sub-agents) a session has dispatched. Mirrors the existing
_session/steeringext-method exactly (underscore-prefixed method const + param validator, handler on the agent class,.onRequestregistration beside steering, capability advertised in theinitialize_meta).(both arrays are parent
tool_useids — the card ids a client already renders.)Why
session/cancelaborts the turn, but the SDK's background tasks survive it by design. In the settled-dispatch shape there is no turn left to cancel at all, so a client's stop button is a full no-op while the sub-agents run on invisibly and unkillably. There was previously no ACP surface to stop them (nostopTaskreference in the adapter).Semantics
toolUseIds: stop the live tasks whose spawning Agent/Tasktool_useid matches.toolUseIds: stop every live task markedisSubagent. Background Bash jobs are deliberately spared — they are notisSubagent, matching the discriminator used across theliveBackgroundTasksregistry (a shell can outlive every turn; its contract is a wake-on-exit notification, not a turn-scoped stop).session.query.stopTask(taskId), awaited so the response reports what the SDK actually accepted. A per-task failure (the task settled between snapshot and stop, or the SDK rejects an unknown id) degrades that id tonotFoundrather than failing the batch.liveBackgroundTasksis safe here: a stoppable task has not settled, so its entry is live by definition. A task that finishes in the snapshot→stop window simply lands innotFound— the honest answer.initializevia_meta.stopTasks.supported: true, mirroring_meta.steering.supported. This lets a client gate the call; an unpatched agent answering-32601would otherwise be silently swallowed by fire-and-forget senders.Downstream
stopTaskcauses the SDK to emit atask_notificationwith status'stopped', which flows through the existingtask_notificationhandling — no new terminal path is introduced here.Testing
npm run build(tsc) is clean;npm run lintandprettier --checkare clean. New unit tests insrc/tests/acp-agent.test.tsunderdescribe("stop background tasks (_session/stop_tasks)"), all passing:stops the tasks named by explicit toolUseIds and returns them instopped``reports an unmatched explicit toolUseId innotFound``without toolUseIds stops every live sub-agent and spares background Basha stopTask that throws lands the id innotFoundand does not fail the batchadvertises the stopTasks capability at initializerejects when the session is unknownrejects when the query stream has already closedFull suite: 771 passed, 2 failed — both failures are pre-existing on clean
main(context-window-seeding assertions increate-session-options.test.ts), unrelated to this change and reproduced with these changes stashed.Behavioral probing against a running adapter (in a downstream integration) showed the expected early settle + tool card close + no result streamed for the stopped sub-agent. This does not claim OS process-level kill verification — only the observable ACP-level outcome.