fix(tui,tool-tasks): finish removing dispose-ness from delivery

The TUI's referenced-prompt snapshot now rides the prompt's own
admission transaction instead of a pre-admission inject: while idle, a
one-shot prepended agent/prompt-submit wrapper appends the snapshot to
the allow decision's additionalContexts, so a blocking hook discards
the prompt and its attached context together instead of stranding the
snapshot in history for the next unrelated prompt. A prompt discarded
before admission releases the wrapper; steering keeps the inject path
since it bypasses admission and drains at the same boundary. The
session-reference snapshot adapter pinned the old context-before-prompt
order; the branch-wide order (prompt first, its contexts after) is now
asserted and the fixture re-recorded.

tool-tasks drops the last consumer of the removed thrown-disposed
contract: completion notices now inject unconditionally, which is
well-defined during owner teardown — the loop treats disposal like any
cancel, so the notice appends as durable idle context (persisted for
resume while the session is attached, dropped with the detached log
after). README pair and the owner-disposal tests state the new
delivery contract.
This commit is contained in:
_Kerman
2026-07-26 22:08:35 +08:00
parent 04f0435cc9
commit 225552fb32
9 changed files with 196 additions and 52 deletions

View File

@@ -218,21 +218,20 @@ export function apply(ctx: Context, config: Config): void {
})
// Use the exact lifecycle owner; reusable ids could resolve to a replacement.
// Delivery into a tearing-down owner is well-defined: the loop treats
// disposal like any cancel, so the notice appends as durable idle context
// (still attached and persisted during owner cleanup, presented on resume);
// after detach it lands in an unreferenced in-memory log and is dropped
// with it.
ctx.tasks.onTaskDone((snapshot, owner) => {
if (snapshot.reported || owner === undefined) return
try {
owner.inject({
content: [{
type: 'text',
text: fitCompletionNotice(snapshot),
}],
source: { kind: 'plugin', plugin: 'tool-tasks' },
})
} catch (error: unknown) {
// Disposal may win the race after settlement; other injection failures surface.
if (error instanceof Error && error.message.includes('is disposed')) return
throw error
}
owner.inject({
content: [{
type: 'text',
text: fitCompletionNotice(snapshot),
}],
source: { kind: 'plugin', plugin: 'tool-tasks' },
})
})
ctx.tools.register(defineTool({