docs(subagent): update package READMEs for the activation lifecycle
Rewrites the service API table, authority-versus-provenance contract, residency routing, and deferred-work list; scopes the in-process driver README to one-shot runs; and restates both model-facing tools' outputs, which no longer carry a task id.
This commit is contained in:
@@ -217,9 +217,11 @@ const SCENARIOS: Scenario[] = [
|
||||
{ name: 'subagent-fork', hasModelTurn: true, recorded: true },
|
||||
{ name: 'subagent-mixed', hasModelTurn: true, recorded: true },
|
||||
// Authored continuable-subagent transcript: a background delegation returns
|
||||
// both the durable subagent id and its task id, a failed final durability
|
||||
// confirmation reaches task_output with its diagnosis, and send_message to
|
||||
// an unknown subagent id starts a follow-up task that settles unavailable.
|
||||
// only the durable subagent id, two send_message calls queue as later FIFO
|
||||
// turns on that same child (the parent is never woken with their output),
|
||||
// send_message to an unknown subagent id fails without delivering, and the
|
||||
// child's retained handle is disposed child-first at teardown despite a
|
||||
// failed final durability confirmation.
|
||||
{
|
||||
name: 'subagent-continuable',
|
||||
hasModelTurn: true,
|
||||
|
||||
@@ -1,45 +1,34 @@
|
||||
import type { Context } from 'cordis'
|
||||
|
||||
export const name = 'subagent-durability-failure'
|
||||
export const inject = ['sessionPersistence', 'tasks']
|
||||
export const inject = ['sessionPersistence']
|
||||
|
||||
const UNKNOWN_CHILD_ID = '22222222-2222-4222-8222-222222222222'
|
||||
const FOLLOW_UP_TASK_ID = 'subagent-2'
|
||||
|
||||
/** Fail the child checkpoint and stabilize the authored follow-up failure ordering. */
|
||||
export function apply(ctx: Context): void {
|
||||
const thirdStepEnded = Promise.withResolvers<undefined>()
|
||||
const followUpSettled = Promise.withResolvers<undefined>()
|
||||
const followupsAccepted = Promise.withResolvers<undefined>()
|
||||
const persistence = ctx.sessionPersistence
|
||||
const load = persistence.load.bind(persistence)
|
||||
|
||||
// The unavailable-child lookup is real asynchronous I/O. Fence it between
|
||||
// the authored step boundaries so runner speed cannot reorder the exact log.
|
||||
// The unavailable-child lookup is real asynchronous I/O. Fence it behind both
|
||||
// authored follow-ups so runner speed cannot reorder the exact log.
|
||||
persistence.load = async (id) => {
|
||||
if (id === UNKNOWN_CHILD_ID) await thirdStepEnded.promise
|
||||
if (id === UNKNOWN_CHILD_ID) await followupsAccepted.promise
|
||||
return load.call(persistence, id)
|
||||
}
|
||||
ctx.effect(() => () => {
|
||||
persistence.load = load
|
||||
thirdStepEnded.resolve(undefined)
|
||||
followUpSettled.resolve(undefined)
|
||||
followupsAccepted.resolve(undefined)
|
||||
}, 'subagent snapshot ordering')
|
||||
|
||||
ctx.on('session/event', (session, event) => {
|
||||
if (session.header.parentSession === undefined
|
||||
&& event.type === 'step/end'
|
||||
&& event.data.turn === 1
|
||||
&& event.data.step === 3) {
|
||||
thirdStepEnded.resolve(undefined)
|
||||
}
|
||||
})
|
||||
ctx.tasks.onTaskDone((snapshot) => {
|
||||
if (snapshot.id === FOLLOW_UP_TASK_ID) followUpSettled.resolve(undefined)
|
||||
})
|
||||
ctx.on('agent/step', async (agent, turn, step) => {
|
||||
if (agent.session.header.parentSession === undefined && turn === 1 && step === 4) {
|
||||
await followUpSettled.promise
|
||||
}
|
||||
// Both authored follow-ups reach the child inbox before the unknown-id lookup
|
||||
// runs, so the queued FIFO order is what the transcript records.
|
||||
let accepted = 0
|
||||
ctx.on('agent/inbox/enqueue', (agent) => {
|
||||
if (agent.session.header.parentSession === undefined) return
|
||||
accepted += 1
|
||||
if (accepted >= 3) followupsAccepted.resolve(undefined)
|
||||
})
|
||||
|
||||
const flushedTurnEnds = new WeakSet<object>()
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
},
|
||||
{
|
||||
"op": "prompt",
|
||||
"text": "Follow these steps exactly, then stop. 1. Call the subagent tool once with run_in_background set to true, description 'Reply with CHILD_OK', and prompt 'Reply with exactly the word CHILD_OK and nothing else.'. 2. Collect its result with task_output using the task id from the acknowledgement and wait: true. 3. Call send_message with subagent_id exactly '22222222-2222-4222-8222-222222222222' (a subagent that does not exist) and message 'Please continue.'. 4. Collect the task it started with task_output and wait: true, and observe that it failed. 5. Reply with the single word DONE. Do not use the bash tool."
|
||||
"text": "Follow these steps exactly, then stop. 1. Call the subagent tool once with run_in_background set to true, description 'Reply with CHILD_OK', and prompt 'Reply with exactly the word CHILD_OK and nothing else.'. 2. Call send_message twice in a row, both with the subagent id from step 1: first with message 'Now reply with exactly SECOND_OK.', then with message 'Now reply with exactly THIRD_OK.'. 3. Call send_message with subagent_id exactly '22222222-2222-4222-8222-222222222222' (a subagent that does not exist) and message 'Please continue.', and observe that it fails. 4. Reply with the single word DONE. Do not use the bash tool."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user