Merge remote-tracking branch 'origin/master' into worktree/pr743-merge-20260727

# Conflicts:
#	examples/acp-agent/tests/snapshots/escalation-approved/session.jsonl
#	examples/acp-agent/tests/snapshots/escalation-rejected/session.jsonl
#	examples/acp-agent/tests/snapshots/fs-escalation-approved/session.jsonl
#	examples/acp-agent/tests/snapshots/hook-cc-pretool-ask/session.jsonl
#	examples/acp-agent/tests/snapshots/session-query-spill/session.jsonl
#	examples/cordis-agent/tests/cordis-tools.e2e.ts
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.1.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.2.jsonl
#	examples/headless-agent/tests/snapshots/advanced-toolchain/session.jsonl
This commit is contained in:
Tianyi Cui
2026-07-27 23:59:33 +08:00
528 changed files with 10636 additions and 10785 deletions

View File

@@ -1,12 +1,18 @@
# Replay counterpart to goal.cordis.yml; only the live model is replaced.
# Replay counterpart to goal.cordis.yml. It includes cordis.yml directly because
# a config patch cannot target an entry behind a nested include, then restates
# the goal overlay while replacing the live model with keyless replay.
- id: base
name: '@cordisjs/plugin-include'
config:
path: ./goal.cordis.yml
path: ./cordis.yml
patches:
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
disabled: true
- insert:
- id: goal
name: '@deepseek-ai/dsh-goal'
- id: tool-goal
name: '@deepseek-ai/dsh-tool-goal'
- id: llm-replay
name: '@deepseek-ai/dsh-llm-replay'

View File

@@ -0,0 +1,12 @@
# Keyless provider-retry composition for the headless stream-json snapshot.
- id: base
name: '@cordisjs/plugin-include'
config:
path: ./cordis.yml
patches:
- id: llm-deepseek
name: '@deepseek-ai/dsh-llm-deepseek'
disabled: true
- insert:
- id: retry-snapshot-backend
name: './tests/fixtures/retry-snapshot-backend.mjs'

View File

@@ -314,12 +314,12 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('Code Mode: real model writes a p
ctx = await codeModeHarness(workdir)
const agent = ctx.agentLoop.create(SessionId('e2e-code-mode'), { provider: 'deepseek', model: 'deepseek-v4-flash' })
agent.followup([{
agent.followup({ content: [{
type: 'text',
text: 'Using one run_code program: run `echo alpha-7` with the bash tool, run `echo beta-9` with the bash tool, '
+ 'then write both outputs joined by a plus sign into combined.txt (bash heredoc or redirect), '
+ 'and return only the joined string.',
}])
}], source: { kind: 'user' } })
await waitForIdle(ctx, agent)
const events: SessionEvent[] = [...agent.session.events]
@@ -366,21 +366,17 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('Code Mode: real model writes a p
agentOptions: { provider: 'deepseek', model: 'deepseek-v4-flash' },
})
handle.agent.followup([{
handle.agent.followup({ content: [{
type: 'text',
text: 'Use one run_code program to call tools.read on pkg/deep/task.txt. After it finishes, answer: Code Mode workspace handshake?',
}])
}], source: { kind: 'user' } })
await waitForIdle(ctx, handle.agent)
const events: SessionEvent[] = [...handle.agent.session.events]
const dispatch = events.find(event => event.type === 'tool/code-dispatch' && event.data.name === 'read')
const outerResult = events.find(event => event.type === 'tool/result')
const workspaceContext = events.find(event => event.type === 'user/message'
&& event.data.source.kind === 'plugin'
&& typeof event.data.meta === 'object'
&& event.data.meta !== null
&& !Array.isArray(event.data.meta)
&& event.data.meta.kind === 'workspace-instructions')
&& event.data.source.kind === 'workspace-instructions')
expect(dispatch).toBeDefined()
expect(outerResult).toBeDefined()
expect(workspaceContext).toBeDefined()

View File

@@ -56,12 +56,12 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('coding task: fix a failing test
ctx = await codingHarness(workdir, { persona: SYSTEM_PROMPT })
const agent = ctx.agentLoop.create(SessionId('e2e-task'), { provider: 'deepseek', model: 'deepseek-v4-flash' })
agent.followup([{
agent.followup({ content: [{
type: 'text',
text: 'In the current directory, `node add.test.js` fails because add.js has a bug. '
+ 'Fix add.js so the test passes, run `node add.test.js` to verify, and report the result. '
+ 'Do not modify add.test.js.',
}])
}], source: { kind: 'user' } })
await waitForIdle(ctx, agent)
// The agent claims success…

View File

@@ -46,12 +46,12 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('compaction: a long session compa
})
const agent = ctx.agentLoop.create(SessionId('e2e-compaction'), { provider: 'deepseek', model: 'deepseek-v4-flash' })
agent.followup([{
agent.followup({ content: [{
type: 'text',
text: 'Read file1.txt, file2.txt, file3.txt, and file4.txt one at a '
+ 'time using cat (a separate bash command for each). After reading all four, tell me how '
+ 'many files you read and the number mentioned in file1.txt.',
}])
}], source: { kind: 'user' } })
await waitForIdle(ctx, agent)
const events = [...agent.session.events]

View File

@@ -59,7 +59,7 @@ export const inject = ['llm']
/** Register the keyless `cli-mock` adapter. */
export function apply(ctx: Context): void {
ctx.llm.registerAdapter(['cli-mock'], new CliMockAdapter())
ctx.on('agent/request', async (_agent, _turn, step, _config, _signal, next) => {
ctx.on('agent/request', async (_agent, _turn, step, _signal, next) => {
const config = await next()
return step === 2 ? { ...config, reasoningEffort: OFF } : config
})

View File

@@ -7,7 +7,7 @@ export const name = 'seed-goal'
export const inject = ['goals']
export function apply(ctx: Context): void {
ctx.on('agent/pre-step', (agent) => {
ctx.on('agent/step', (agent) => {
if (ctx.goals.get(agent) !== undefined) return
ctx.goals.create(agent, {
objective: 'Prove the composed goal survives in the session log',

View File

@@ -0,0 +1,53 @@
/** Deterministic provider adapter for the headless retry-policy snapshot. */
import {
LlmAdapter,
LlmError,
resolveRetryPolicy,
} from '@deepseek-ai/dsh-llm'
class RetrySnapshotAdapter extends LlmAdapter {
requests = 0
firstMessages
policy = resolveRetryPolicy({
mode: 'normal',
maxRetries: 1,
retryableCodes: ['RATE_LIMIT'],
backoff: { initialDelayMs: 1, maxDelayMs: 1, jitterRatio: 0 },
}, 'retry-snapshot-backend.retryPolicy')
providerRetryPolicy() {
return this.policy
}
async * stream(options) {
const messages = JSON.stringify(options.messages)
this.requests++
if (this.requests === 1) {
this.firstMessages = messages
throw new LlmError('snapshot transient failure', 'RATE_LIMIT', { status: 429 })
}
if (this.requests === 2 && messages !== this.firstMessages) {
throw new Error('retry snapshot changed the model-visible messages')
}
const text = 'RETRY_OK'
yield { type: 'block-start', index: 0, blockType: 'text' }
yield { type: 'text-delta', index: 0, text }
yield { type: 'block-end', index: 0, block: { type: 'text', text } }
yield { type: 'usage', usage: { inputTokens: 4, outputTokens: 2 } }
yield { type: 'finish', reason: { kind: 'stop' } }
}
}
/** Cordis plugin name. */
export const name = 'retry-snapshot-backend'
/** Required LLM registry service. */
export const inject = ['llm']
/**
* Register the deterministic provider adapter.
* @param {import('cordis').Context} ctx - plugin context carrying the LLM service.
*/
export function apply(ctx) {
ctx.llm.registerAdapter(['deepseek'], new RetrySnapshotAdapter())
}

View File

@@ -30,7 +30,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('full loop: real model + real bas
ctx = await codingHarness(workdir, { persona: SYSTEM_PROMPT })
const agent = ctx.agentLoop.create(SessionId('e2e-loop'), { provider: 'deepseek', model: 'deepseek-v4-flash' })
agent.followup([{ type: 'text', text: 'Run `echo e2e-ok` with the bash tool and tell me its exact output.' }])
agent.followup({ content: [{ type: 'text', text: 'Run `echo e2e-ok` with the bash tool and tell me its exact output.' }], source: { kind: 'user' } })
await waitForIdle(ctx, agent)
const events = [...agent.session.events]

View File

@@ -24,6 +24,8 @@ const ptyStreamExpected = join(ptyScenarioDir, 'stream-json.expected.jsonl')
const ptyConfigPath = fileURLToPath(new URL('../pty.cordis.snapshot.yml', import.meta.url))
const goalScenarioDir = join(snapshotsDir, 'goal-tools')
const goalConfigPath = fileURLToPath(new URL('../goal.cordis.snapshot.yml', import.meta.url))
const retryScenarioDir = join(snapshotsDir, 'provider-retry')
const retryConfigPath = fileURLToPath(new URL('../retry.cordis.snapshot.yml', import.meta.url))
const ralphScenarioDir = join(snapshotsDir, 'ralph-loop')
const ralphConfigPath = fileURLToPath(new URL('../ralph.cordis.snapshot.yml', import.meta.url))
const binScript = fileURLToPath(new URL('../../../packages/examples/cli-demo/src/bin.ts', import.meta.url))
@@ -125,6 +127,46 @@ async function persistedLogs(cwd: string): Promise<PersistedLog[]> {
}
describe('headless stream-json snapshots', () => {
it('retries a transient provider failure through the one-shot app', async () => {
const prompt = await scenarioPrompt(retryScenarioDir, 'provider-retry')
const streamExpected = join(retryScenarioDir, 'stream-json.expected.jsonl')
let runCwd = ''
const result = await runLoaderSmoke({
label: 'provider retry headless stream-json snapshot',
tempDirPrefix: 'headless-snapshot-provider-retry-',
binScript,
configPath: retryConfigPath,
binArgs: ['--config', retryConfigPath, '--output-format', 'stream-json', prompt],
tsconfigPath,
env: {
DSH_SNAPSHOT: 'replay',
NODE_OPTIONS: [process.env.NODE_OPTIONS, '--disable-warning=ExperimentalWarning'].filter(Boolean).join(' '),
},
prepare: (cwd) => { runCwd = cwd },
inspect: async (cwd) => {
const logs = await persistedLogs(cwd)
expect(logs).toHaveLength(1)
const records = parseJsonl(logs[0]?.content ?? '')
const retries = records.filter(record => record.type === 'llm/retry')
expect(retries).toHaveLength(1)
expect(retries[0]?.data).toMatchObject({
provider: 'deepseek',
mode: 'normal',
policyKey: '["normal",1,["RATE_LIMIT"],1,1,0]',
retry: 1,
maxRetries: 1,
delayMs: 1,
failure: { message: 'snapshot transient failure', code: 'RATE_LIMIT', status: 429 },
})
},
})
expect(result.stderr).toBe('')
const normalized = normalizeHeadlessStream(result.stdout, runCwd)
if (refreshing) await writeFile(streamExpected, normalized)
expect(normalized).toBe(await readFile(streamExpected, 'utf8'))
}, LOADER_SMOKE_TEST_TIMEOUT_MS)
it('logs the model default and a dynamic next-step reasoning effort', async () => {
const result = await runLoaderSmoke({
label: 'reasoning effort headless stream-json snapshot',
@@ -272,14 +314,16 @@ describe('headless stream-json snapshots', () => {
const goalChanges = records.filter((record) => {
if (record.type !== 'user/message') return false
const data = record.data as JsonObject | undefined
const meta = data?.meta as JsonObject | undefined
return meta?.kind === 'goal/change'
const source = data?.source as JsonObject | undefined
const change = source?.change as JsonObject | undefined
return source?.kind === 'goal' && change?.kind === 'goal/change'
})
expect(goalChanges).toHaveLength(1)
const data = goalChanges[0]?.data as JsonObject | undefined
const meta = data?.meta as JsonObject | undefined
const goal = meta?.goal as JsonObject | undefined
expect(meta?.operation).toBe('create')
const source = data?.source as JsonObject | undefined
const change = source?.change as JsonObject | undefined
const goal = change?.goal as JsonObject | undefined
expect(change?.operation).toBe('create')
expect(goal).toMatchObject({
objective: 'Finish the headless goal-tool snapshot proof',
phase: 'active',

View File

@@ -41,7 +41,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('resume: continue a persisted ses
sessionId: SESSION_ID,
agentOptions: { provider: 'deepseek', model: 'deepseek-v4-flash' },
})).agent
first.followup([{ type: 'text', text: `Remember this code for later: ${SECRET}. Just acknowledge it.` }])
first.followup({ content: [{ type: 'text', text: `Remember this code for later: ${SECRET}. Just acknowledge it.` }], source: { kind: 'user' } })
await waitForIdle(ctx, first)
await ctx.fiber.dispose()
ctx = undefined
@@ -58,7 +58,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('resume: continue a persisted ses
// The prior user turn is in the rehydrated log before the model is asked.
expect(JSON.stringify(resumed.session.deriveMessages())).toContain(SECRET)
resumed.followup([{ type: 'text', text: 'What was the code I asked you to remember? Reply with just the code.' }])
resumed.followup({ content: [{ type: 'text', text: 'What was the code I asked you to remember? Reply with just the code.' }], source: { kind: 'user' } })
await waitForIdle(ctx, resumed)
// The model recalls it — only possible from the resumed history.

View File

@@ -21,7 +21,7 @@
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":20,"time":0,"data":{"turn":1,"step":2,"content":[{"type":"tool-call","id":"call_goal_create","name":"create_goal","arguments":"{\"objective\":\"Finish the headless goal-tool snapshot proof\",\"max_goal_rounds\":7}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":20,"outputTokens":8}},"sourceEventSeqs":[15,16,17,18,19],"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"tool/call","seq":21,"time":0,"data":{"turn":1,"step":2,"callId":"call_goal_create","name":"create_goal","arguments":"{\"objective\":\"Finish the headless goal-tool snapshot proof\",\"max_goal_rounds\":7}"}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"tool/result","seq":22,"time":0,"data":{"turn":1,"step":2,"callId":"call_goal_create","content":[{"type":"text","text":"{\"goal\":{\"id\":\"goal-{{sessionId}}\",\"revision\":1,\"objective\":\"Finish the headless goal-tool snapshot proof\",\"phase\":\"active\",\"roundsStarted\":0,\"maxGoalRounds\":7},\"activation\":\"armed\"}"}],"isError":false},"sourceEventSeqs":[21],"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"user/message","seq":23,"time":0,"data":{"content":[{"type":"text","text":"<goal_state>{\"goal\":{\"id\":\"goal-{{sessionId}}\",\"revision\":1,\"objective\":\"Finish the headless goal-tool snapshot proof\",\"phase\":\"active\",\"maxGoalRounds\":7},\"roundsStarted\":0,\"createdAt\":0,\"updatedAt\":0}</goal_state>"}],"source":{"kind":"goal","goalId":"goal-{{sessionId}}","revision":1,"round":0},"meta":{"kind":"goal/change","version":1,"operation":"create","goal":{"id":"goal-{{sessionId}}","revision":1,"objective":"Finish the headless goal-tool snapshot proof","phase":"active","maxGoalRounds":7},"roundsStarted":0,"createdAt":0,"updatedAt":0}},"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"user/message","seq":23,"time":0,"data":{"content":[{"type":"text","text":"<goal_state>{\"goal\":{\"id\":\"goal-{{sessionId}}\",\"revision\":1,\"objective\":\"Finish the headless goal-tool snapshot proof\",\"phase\":\"active\",\"maxGoalRounds\":7},\"roundsStarted\":0,\"createdAt\":0,\"updatedAt\":0}</goal_state>"}],"source":{"kind":"goal","goalId":"goal-{{sessionId}}","revision":1,"round":0,"change":{"kind":"goal/change","version":1,"operation":"create","goal":{"id":"goal-{{sessionId}}","revision":1,"objective":"Finish the headless goal-tool snapshot proof","phase":"active","maxGoalRounds":7},"roundsStarted":0,"createdAt":0,"updatedAt":0}}},"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/end","seq":24,"time":0,"data":{"turn":1,"step":2}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/start","seq":25,"time":0,"data":{"turn":1,"step":3}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":26,"time":0,"data":{"turn":1,"step":3,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}}

View File

@@ -0,0 +1,8 @@
{
"steps": [
{
"op": "prompt",
"text": "retry the transient provider failure"
}
]
}

View File

@@ -0,0 +1,19 @@
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"turn/start","seq":0,"time":0,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"user/message","seq":1,"time":0,"data":{"content":[{"type":"text","text":"retry the transient provider failure"}],"source":{"kind":"user"}},"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"session/title","seq":2,"time":0,"data":{"title":"retry the transient provider failure","messageSeqs":[1],"source":{"kind":"fallback"}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/start","seq":3,"time":0,"data":{"turn":1,"step":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"request/header","seq":4,"time":0,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/end","seq":5,"time":0,"data":{"turn":1,"step":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"llm/retry","seq":6,"time":0,"data":{"turn":1,"step":1,"provider":"deepseek","mode":"normal","policyKey":"[\"normal\",1,[\"RATE_LIMIT\"],1,1,0]","retry":1,"maxRetries":1,"delayMs":1,"failure":{"message":"snapshot transient failure","code":"RATE_LIMIT","status":429}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"turn/end","seq":7,"time":0,"data":{"turn":1,"reason":{"kind":"error","step":1,"failure":{"message":"snapshot transient failure","code":"RATE_LIMIT","status":429}}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"turn/start","seq":8,"time":0,"data":{"turn":2,"trigger":{"kind":"retry"}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/start","seq":9,"time":0,"data":{"turn":2,"step":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":10,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":11,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"text-delta","index":0,"text":"RETRY_OK"}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":12,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"RETRY_OK"}}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":13,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":4,"outputTokens":2}}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":2,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"assistant/message","seq":15,"time":0,"data":{"turn":2,"step":1,"content":[{"type":"text","text":"RETRY_OK"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":4,"outputTokens":2}},"sourceEventSeqs":[10,11,12,13,14],"surfaceOp":"append"}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"step/end","seq":16,"time":0,"data":{"turn":2,"step":1}}}
{"type":"session_event","sessionId":"{{sessionId}}","event":{"type":"turn/end","seq":17,"time":0,"data":{"turn":2,"reason":{"kind":"completed"}}}}
{"type":"result","success":true,"sessionId":"{{sessionId}}","turn":2,"result":"RETRY_OK","reason":{"kind":"completed"},"usage":{"inputTokens":4,"outputTokens":2}}

View File

@@ -28,10 +28,10 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('todo_write: real model records a
ctx = await codingHarness(workdir, { persona: TODO_SYSTEM_PROMPT })
const agent = ctx.agentLoop.create(SessionId('e2e-todo'), { provider: 'deepseek', model: 'deepseek-v4-flash' })
agent.followup([{ type: 'text', text:
agent.followup({ content: [{ type: 'text', text:
'Use the todo_write tool to record a plan of exactly two steps: first '
+ '"inspect the failing test" (in_progress), then "apply the fix" (pending). '
+ 'Send both in one todo_write call, then reply with the single word DONE.' }])
+ 'Send both in one todo_write call, then reply with the single word DONE.' }], source: { kind: 'user' } })
await waitForIdle(ctx, agent)
const events = [...agent.session.events]