fix(subagent): harden continuable persistence
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
# Keyless counterpart to subagent-durability-failure.cordis.yml: replace the
|
||||
# live adapter with replay and fail the provider-owned final child checkpoint.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
- id: llm-deepseek
|
||||
name: '@deepseek-ai/dsh-llm-deepseek'
|
||||
disabled: true
|
||||
- id: acp-agent
|
||||
name: '@deepseek-ai/dsh-acp-demo'
|
||||
config:
|
||||
provider: deepseek-official
|
||||
model: deepseek-v4-flash
|
||||
persistenceRoot: !!js process.env.DSH_SNAPSHOT_SESSIONS_ROOT ?? './.sessions'
|
||||
persistenceCompression: none
|
||||
workspaceContext:
|
||||
maxBytes: 65536
|
||||
persona: |
|
||||
You are a coding assistant powered by the {{model}} model. Your working directory is {{cwd}}. Your bash tool runs under a file sandbox — a `[sandbox: file access denied …]` result is policy, not a command bug.
|
||||
|
||||
Verify your work by running the code or tests. Keep answers brief and factual.
|
||||
- id: sandbox
|
||||
name: '@deepseek-ai/dsh-sandbox-local'
|
||||
config:
|
||||
runnerCommand:
|
||||
- bash
|
||||
- -c
|
||||
- while [ "$1" != "--" ]; do shift; done; shift; exec "$@"
|
||||
- passthrough-runner
|
||||
runnerFailureSignatures:
|
||||
- 'passthrough-runner: profile rejected'
|
||||
- insert:
|
||||
- id: llm-replay
|
||||
name: '@deepseek-ai/dsh-llm-replay'
|
||||
config:
|
||||
providers:
|
||||
- id: deepseek-official
|
||||
name: DeepSeek
|
||||
models:
|
||||
- id: deepseek-v4-flash
|
||||
- id: deepseek-v4-pro
|
||||
- id: subagent-durability-failure
|
||||
name: './tests/fixtures/subagent-durability-failure.ts'
|
||||
10
examples/acp-agent/subagent-durability-failure.cordis.yml
Normal file
10
examples/acp-agent/subagent-durability-failure.cordis.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
# Snapshot-only durability-failure overlay. The child turn's ordinary flush
|
||||
# succeeds; the provider-owned final confirmation fails deterministically.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
- insert:
|
||||
- id: subagent-durability-failure
|
||||
name: './tests/fixtures/subagent-durability-failure.ts'
|
||||
@@ -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