fix(subagent): address codex review round 3

- Make host-user authority unforgeable. `{ kind: 'user' }` was a bare
  discriminant, so any plugin holding `ctx.subagents` — including
  model-generated cordis_mount code, which the advanced ACP composition ships
  alongside continuable subagents — could construct it and skip the
  direct-parent check for any known child id. It now carries an opaque grant
  that only SubagentService.userAuthority() mints, which composition hands to
  trusted host adapters; a model-facing tool uses parent authority from its own
  execution context.
- Reconcile a delivery discarded inside its own admission window. An enqueue
  listener that cancels fires the discard before followup() returns, so the
  discard listener could not clear an id it had not seen; submit() retained it
  and residency stayed `running` until an explicit drain.
- Recheck the caller signal after materialization. An abort landing between
  publication and inbox acceptance still submitted the prompt and returned both
  ids; it now rolls the child back.
- Stop promising the model transcript access that no shipped continuable config
  mounts. The tools now state only that a background child does not report back.
- Restate the implemented note as shipped state rather than a proposal, so it
  works as current authority.
This commit is contained in:
Dudu-0223
2026-07-30 16:48:42 +08:00
committed by Tianyi Cui
parent cbaceb73a9
commit 7428cdf41e
29 changed files with 297 additions and 141 deletions

View File

@@ -26,8 +26,8 @@ export function apply(ctx: Context): void {
description:
'Send a message to a background subagent by its subagent id, continuing the same conversation. It '
+ 'becomes the subagent\'s next turn: if it is still working, the message waits until its current turn '
+ 'finishes, so it cannot redirect work already underway. The subagent does not reply to you — read its '
+ 'transcript by its id to see what it did. A failure means the message was NOT delivered.',
+ 'finishes, so it cannot redirect work already underway. The subagent does not reply to you, so use '
+ 'this only to give it more work. A failure means the message was NOT delivered.',
parameters: {
subagent_id: {
type: 'string',