fix(subagent): harden continuable persistence
This commit is contained in:
@@ -41,6 +41,9 @@ const DEPTH_TWO_CONFIG = fileURLToPath(new URL('../depth-two.cordis.yml', import
|
||||
const SESSION_SANDBOX_ROOT_CONFIG = fileURLToPath(new URL('../session-sandbox-root.cordis.yml', import.meta.url))
|
||||
const RETRY_CONFIG = fileURLToPath(new URL('../retry.cordis.yml', import.meta.url))
|
||||
const SESSION_TITLE_CONFIG = fileURLToPath(new URL('../session-title.cordis.yml', import.meta.url))
|
||||
const SUBAGENT_DURABILITY_FAILURE_CONFIG = fileURLToPath(
|
||||
new URL('../subagent-durability-failure.cordis.yml', import.meta.url),
|
||||
)
|
||||
const LSP_CONFIG = fileURLToPath(new URL('./lsp.cordis.yml', import.meta.url))
|
||||
const WEB_CONFIG = fileURLToPath(new URL('../web.cordis.yml', import.meta.url))
|
||||
const FS_SEARCH_CONFIG = fileURLToPath(new URL('./fs-search.cordis.yml', import.meta.url))
|
||||
@@ -214,10 +217,15 @@ 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, task_output collects the
|
||||
// child result after settlement, and send_message to an unknown subagent id
|
||||
// starts a follow-up task that settles failed with the id unavailable.
|
||||
{ name: 'subagent-continuable', hasModelTurn: true, recorded: false },
|
||||
// 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.
|
||||
{
|
||||
name: 'subagent-continuable',
|
||||
hasModelTurn: true,
|
||||
recorded: false,
|
||||
configPath: SUBAGENT_DURABILITY_FAILURE_CONFIG,
|
||||
},
|
||||
{
|
||||
name: 'subagent-depth-two-rejection',
|
||||
hasModelTurn: true,
|
||||
|
||||
14
examples/acp-agent/tests/fixtures/subagent-durability-failure.ts
vendored
Normal file
14
examples/acp-agent/tests/fixtures/subagent-durability-failure.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { Context } from 'cordis'
|
||||
|
||||
export const name = 'subagent-durability-failure'
|
||||
|
||||
/** Fail a continuable child's provider-owned final durability confirmation. */
|
||||
export function apply(ctx: Context): void {
|
||||
const flushedTurnEnds = new WeakSet<object>()
|
||||
ctx.on('session/flush', (session) => {
|
||||
if (session.header.parentSession === undefined) return
|
||||
if (session.events.at(-1)?.type !== 'turn/end') return
|
||||
if (flushedTurnEnds.has(session)) throw new Error('snapshot disk full')
|
||||
flushedTurnEnds.add(session)
|
||||
})
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
{"type":"assistant/chunk","seq":21,"time":1785517567391,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":22,"time":1785517567392,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_collect_1","name":"task_output","arguments":"{\"task_id\": \"subagent-1\", \"wait\": true}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"3fcea712-0e14-4f2d-909c-f7de70018053"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[17,18,19,20,21],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":23,"time":1785517567392,"data":{"turn":1,"step":2,"callId":"call_collect_1","name":"task_output","arguments":"{\"task_id\": \"subagent-1\", \"wait\": true}"}}
|
||||
{"type":"tool/result","seq":24,"time":1785517567419,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"call_collect_1"},"content":[{"type":"tool-result","toolCallId":"call_collect_1","content":[{"type":"text","text":"CHILD_OK\n[status: completed]"}],"isError":false}],"role":"user","id":"ae79571a-fa78-4de0-9614-a10b5223230c"}},"sourceEventSeqs":[23],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":24,"time":1785517567419,"data":{"turn":1,"step":2,"message":{"source":{"kind":"tool","callId":"call_collect_1"},"content":[{"type":"tool-result","toolCallId":"call_collect_1","content":[{"type":"text","text":"(no new output)\n[status: failed, subagent \"33333333-3333-4333-8333-333333333333\" durability checkpoint failed; the latest child state was not confirmed persisted and may be unavailable or stale on resume: snapshot disk full]"}],"isError":false}],"role":"user","id":"ae79571a-fa78-4de0-9614-a10b5223230c"}},"sourceEventSeqs":[23],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":25,"time":1785517567419,"data":{"turn":1,"step":2}}
|
||||
{"type":"step/start","seq":26,"time":1785517567425,"data":{"turn":1,"step":3}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1789000000026,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
|
||||
Reference in New Issue
Block a user