qwen-code/packages
Shaojin Wen 1b9e8ec45d
feat(core): wire background shells into the task_stop tool (#3687)
* feat(core): wire background shells into the task_stop tool

Phase B follow-up #1 from #3634, unblocked by #3471 (control plane) merging
in. The model can now cancel a managed background shell with the same
`task_stop` tool it uses for subagents — no more falling back to
`kill <pid>` via BashTool.

Lookup order: subagent registry first (existing behavior), then the
background shell registry as a fallback. Agent IDs follow
`<subagentName>-<suffix>` and shell IDs follow `bg_<8 hex chars>`, so the
two namespaces cannot collide in practice; the order is fixed for
determinism (a defensive test pins agent-wins-over-shell).

The shell cancel path resolves through the entry's own AbortController
(which `BackgroundShellRegistry.cancel` triggers); the child process
exit handler then settles the registry to `cancelled` and the on-disk
output file is preserved for inspection via `/tasks` or a direct `Read`.
This matches Phase B's "registry's own AbortController is the
cancellation source of truth" design without needing the in-flight
notification framework that subagents use.

Tests: 7 task-stop tests (was 4) — added cancel-shell happy path,
NOT_RUNNING for already-exited shell, and a defensive
agent-takes-precedence-on-id-collision case.

* fix(core): defer shell terminal transition until spawn handler settles

@doudouOUC noticed that the previous task_stop path called
`BackgroundShellRegistry.cancel(id, Date.now())`, which marked the entry
`cancelled` immediately. The spawn handler's settle path only records
real exit info via cancel/complete/fail when the entry is still
`running`, so the cancel-vs-exit race could permanently hide a real
completed/failed result and `/tasks` would show a terminal endTime
while the process was still draining.

Add a `requestCancel(id)` method to `BackgroundShellRegistry` that
triggers the entry's AbortController only; status stays `running` until
the settle path observes the abort and records the real terminal state.
The immediate-mark `cancel(id, endTime)` is reserved for `abortAll()` /
shutdown, where the CLI process is tearing down anyway and there is no
settle handler to wait for.

Tests updated:
- `task-stop.test.ts` cancel-shell happy path now asserts the entry
  stays `running` with `endTime` undefined post-stop, and the abort
  signal fires (the settle path's contract, not task_stop's, is the
  one that flips status).
- 3 new `requestCancel` tests in `backgroundShellRegistry.test.ts`:
  running → abort+still-running, terminal entry no-op, unknown id no-op.

---------

Co-authored-by: wenshao <wenshao@U-K7F6PQY3-2157.local>
2026-04-29 10:10:41 +08:00
..
channels chore(release): bump version to 0.15.3 (#3708) 2026-04-28 21:04:52 +08:00
cli fix(cli): refresh static header on model switch (#3667) 2026-04-29 07:16:26 +08:00
core feat(core): wire background shells into the task_stop tool (#3687) 2026-04-29 10:10:41 +08:00
sdk-java fix(sdk-java): pass custom env to CLI process (#3543) 2026-04-24 10:37:52 +08:00
sdk-python feat(SDK) Add Python SDK implementation for #3010 (#3494) 2026-04-25 07:02:58 +08:00
sdk-typescript chore(release): sdk-typescript v0.1.7 (#3688) 2026-04-28 14:59:13 +08:00
vscode-ide-companion chore(release): bump version to 0.15.3 (#3708) 2026-04-28 21:04:52 +08:00
web-templates chore(release): bump version to 0.15.3 (#3708) 2026-04-28 21:04:52 +08:00
webui chore(release): bump version to 0.15.3 (#3708) 2026-04-28 21:04:52 +08:00
zed-extension chore(zed-extension): update package version to 0.10.0 2026-02-06 14:26:01 +08:00