mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
fix(test): make session replay portable in CI
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write .agents/notes/implemented/testing/2026-08-24-session-log-snapshot-corpus.md
|
||||
2026-08-24-session-log-snapshot-corpus.md: 30dad40098e5d1da08052384374425cad595a1ba
|
||||
2026-08-24-session-log-snapshot-corpus.zh.md: 49edffc2a63486030ace8e76455e9bae209ee902
|
||||
2026-08-24-session-log-snapshot-corpus.md: eb77f74e85ad97f74df48ecbf831579b6a643cbf
|
||||
2026-08-24-session-log-snapshot-corpus.zh.md: 5aff7fe8d04d8bf04f5cdfd443a7ed121dcbad25
|
||||
|
||||
@@ -40,6 +40,7 @@ Workspace inputs remain scenario-local. A mutating scenario compares a complete
|
||||
- Committed session fixtures are redaction fixed points, contain no system-prompt or tool-schema bulk, and retain exactly one pin per header class.
|
||||
- Mutating scenarios verify their final workspace externally.
|
||||
- Owner-local process expectations use `*.expected.e2e.ts` and a separate built-output gate.
|
||||
- Source and built adapters install replay-only packages in isolated profile fallbacks; distinct prompt-section orders keep their request headers byte-identical.
|
||||
- Source and built launch modes, browser replay, SDK projections, packaged Python runtime cases, documentation gates, and repository hygiene pass.
|
||||
|
||||
## Consequences
|
||||
|
||||
@@ -40,6 +40,7 @@ Workspace 输入继续归各场景本地所有。变更文件的场景比较完
|
||||
- 提交的会话 fixture 是脱敏固定点,不含 system prompt 或工具 schema 正文,并为每个 header 类保留且仅保留一个 pin。
|
||||
- 变更内容的场景从外部验证最终 workspace。
|
||||
- 所属位置的进程预期使用 `*.expected.e2e.ts`,并由单独的构建产物门禁运行。
|
||||
- 源码与构建适配器在隔离的 profile fallback 中安装仅回放包;不同的提示词 section 顺序值使两种模式的请求 header 保持字节一致。
|
||||
- 源码和构建启动模式、浏览器回放、SDK 投影、打包 Python 运行时场景、文档门禁和仓库卫生检查通过。
|
||||
|
||||
## Consequences
|
||||
|
||||
@@ -127,8 +127,8 @@ describe('web e2e: approval takeover keeps its actions reachable', () => {
|
||||
await assertFinalWorkspaceSnapshot(SNAPSHOT_DIR, join(scaffold.workspaceCwd, 'workspace'))
|
||||
return
|
||||
}
|
||||
// The denied attempt contains platform-specific OS text, so direct state
|
||||
// and DOM assertions cover the answered outcome.
|
||||
// Direct state and DOM assertions cover the answered outcome beyond the
|
||||
// pending panel's expected output.
|
||||
expect(JSON.stringify(sessionEvents.filter(e => e.type === 'approval/decided').at(-1)))
|
||||
.toContain('allowed-once')
|
||||
const written = await readFile(join(scaffold.workspaceCwd, 'workspace', 'notes.txt'), 'utf8')
|
||||
|
||||
@@ -26,10 +26,10 @@ const MID_EXPECTED = join(SNAPSHOT_DIR, 'mid-steer.expected.md')
|
||||
const SETTLED_EXPECTED = join(SNAPSHOT_DIR, 'settled.expected.md')
|
||||
const MODE = webSnapshotMode()
|
||||
// The question composer replaces the textarea, so fill → Queue row → Steer
|
||||
// must finish inside the first replay chunk window. At 15 ms that window is
|
||||
// shorter than Playwright's round trips; 50 ms supplies test-only headroom,
|
||||
// while larger values lengthen all three replay scenarios linearly.
|
||||
const REPLAY_PACE_MS = 50
|
||||
// starts only after request/context and must finish before the first replay
|
||||
// chunk. The compact canonical call plus 500 ms pacing gives loaded CI enough
|
||||
// time without stretching a long provider-authored chunk sequence.
|
||||
const REPLAY_PACE_MS = 500
|
||||
|
||||
const PROMPT = 'Use the ask_user_question tool to ask me exactly one question with id "checkpoint", question "Ready to continue?", header "Checkpoint", and options labeled "Yes" and "No". After I answer, reply with one short sentence acknowledging my answer and stop.'
|
||||
const STEER = 'Interjection: include the word BANANA in your final reply.'
|
||||
@@ -101,6 +101,10 @@ describe('web e2e: mid-turn steering lands durably and visibly', () => {
|
||||
const settled = scaffold.whenTurnSettled(MODE === 'record' ? 180_000 : 30_000)
|
||||
await input.fill(PROMPT)
|
||||
await input.press('Enter')
|
||||
await expect.poll(
|
||||
() => sessionEvents.some(event => event.type === 'request/context'),
|
||||
{ timeout: 10_000 },
|
||||
).toBe(true)
|
||||
|
||||
// Enter remains the Queue gesture. The row action then atomically moves
|
||||
// this exact occurrence into the current turn's steering outbox.
|
||||
|
||||
@@ -33,7 +33,7 @@ function requireAgent(exec: ToolExecution): Agent {
|
||||
|
||||
/** Register the Cordis tools and explicit `@pluginId` context injection. */
|
||||
export function apply(ctx: Context): void {
|
||||
ctx.systemPrompt.section({ name: 'tool:cordis', order: 115, text: CORDIS_SYSTEM_PROMPT })
|
||||
ctx.systemPrompt.section({ name: 'tool:cordis', order: 115.5, text: CORDIS_SYSTEM_PROMPT })
|
||||
for (const provider of hostInspectProviders(ctx)) {
|
||||
ctx.effect(() => ctx.cordisInspect.register(provider), `tool-cordis: inspect ${provider.manifest.id}`)
|
||||
}
|
||||
|
||||
@@ -449,6 +449,7 @@ function inferStartedSubagents(
|
||||
for (const leaf of leaves) {
|
||||
for (const match of leaf.matchAll(/started subagent ([^\s"'<>]+)/g)) {
|
||||
const id = match[1]
|
||||
/* v8 ignore next -- the fixed regular expression always has capture group 1. */
|
||||
if (id === undefined || liveSessionIds.includes(id)) continue
|
||||
const index = liveSessionIds.findIndex((value, candidate) => candidate > 0 && value === undefined)
|
||||
if (index < 0) return
|
||||
|
||||
@@ -1246,7 +1246,7 @@ describe('installLlmReplay (per-session keying)', () => {
|
||||
const options: GenerateOptions = {
|
||||
...live('live-parent'),
|
||||
messages: [createUserMessage({
|
||||
content: [{ type: 'text', text: 'started subagent live-child-before-call' }],
|
||||
content: [{ type: 'text', text: 'started subagent live-child-before-call started subagent live-child-before-call' }],
|
||||
source: { kind: 'user' },
|
||||
})],
|
||||
}
|
||||
@@ -1257,6 +1257,21 @@ describe('installLlmReplay (per-session keying)', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('ignores started-subagent text after every recorded session has bound', async () => {
|
||||
const parentFile = writeSession('session.jsonl', { id: '{{session:1}}', createdAt: 1 }, [TEXT_CHUNKS])
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(LlmRuntime)
|
||||
installLlmReplay(ctx, { file: parentFile })
|
||||
|
||||
expect(await drain(ctx.llm.stream({
|
||||
...live('live-parent'),
|
||||
messages: [createUserMessage({
|
||||
content: [{ type: 'text', text: 'started subagent unrecorded-child' }],
|
||||
source: { kind: 'user' },
|
||||
})],
|
||||
}))).toEqual(TEXT_CHUNKS)
|
||||
})
|
||||
|
||||
it('treats a call with no sessionId as the single anonymous (primary) session', async () => {
|
||||
const parentFile = writeSession('session.jsonl', { id: 'p', createdAt: 1 }, [TEXT_CHUNKS])
|
||||
const ctx = new Context()
|
||||
|
||||
@@ -397,7 +397,7 @@ function linkProfilePackage(source: string, cwd: string, packageName: string): v
|
||||
mkdirSync(dirname(link), { recursive: true })
|
||||
if (existsSync(link)) {
|
||||
if (realpathSync(link) !== packageDir) {
|
||||
throw new Error(`ACP profile package ${packageName} resolves to two directories`)
|
||||
throw new Error(`snapshot profile package ${packageName} resolves to two directories`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -348,6 +348,7 @@ export function parseSnapshotManifest(source: string, path = 'snapshot.yml'): Sn
|
||||
...(session === undefined ? {} : { session }),
|
||||
}
|
||||
} catch (error) {
|
||||
/* v8 ignore next -- every parser and validator above throws Error instances. */
|
||||
throw new Error(`session-snapshot: ${path}: ${error instanceof Error ? error.message : String(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1338,12 +1338,10 @@ export function defineAcpSnapshotSuite(options: SnapshotSuiteOptions): void {
|
||||
expect(result.sessionLogs.length, 'this scenario must persist one log per session fixture').toBe(fixtureFiles.length)
|
||||
const harvested = result.sessionLogs.map(log => log.content)
|
||||
const fixtures = await Promise.all(fixtureFiles.map(file => readFile(join(dir, file), 'utf8')))
|
||||
const fixtureHeaders = fixtures.map(fixture => JSON.parse(
|
||||
fixture.split('\n').find(line => line.trim() !== '') ?? '{}',
|
||||
) as { id?: unknown; cwd?: unknown })
|
||||
const fixtureContexts = fixtures.map(fixtureContext)
|
||||
const fixtureCtx: NormalizeContext = {
|
||||
sessionIds: fixtureHeaders.flatMap(header => typeof header.id === 'string' ? [header.id] : []),
|
||||
cwd: typeof fixtureHeaders[0]?.cwd === 'string' ? fixtureHeaders[0].cwd : '\0no-cwd\0',
|
||||
sessionIds: fixtureContexts.flatMap(context => context.sessionIds),
|
||||
cwd: (fixtureContexts[0] as NormalizeContext).cwd,
|
||||
}
|
||||
const actualSnapshots = normalizeSessionSnapshots(harvested, ctx)
|
||||
const expectedSnapshots = normalizeSessionSnapshots(fixtures, fixtureCtx)
|
||||
|
||||
@@ -78,7 +78,7 @@ export async function captureWorkspaceSnapshot(
|
||||
const visit = async (directory: string, segments: readonly string[]): Promise<WorkspaceSnapshotEntry[]> => {
|
||||
const entries = (await readdir(directory, { withFileTypes: true }))
|
||||
.filter(entry => segments.length > 0 || !ignoredRootEntries.has(entry.name))
|
||||
.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)
|
||||
.sort((left, right) => Buffer.compare(Buffer.from(left.name), Buffer.from(right.name)))
|
||||
const captured: WorkspaceSnapshotEntry[] = []
|
||||
for (const entry of entries) {
|
||||
const childSegments = [...segments, entry.name]
|
||||
@@ -93,11 +93,8 @@ export async function captureWorkspaceSnapshot(
|
||||
captured.push(content === undefined
|
||||
? { path, kind: 'binary', base64: bytes.toString('base64') }
|
||||
: { path, kind: 'text', content })
|
||||
} else if (entry.isSymbolicLink()) {
|
||||
captured.push({ path, kind: 'symlink', target: await readlink(absolute) })
|
||||
} else {
|
||||
/* v8 ignore next -- ordinary Git workspaces contain only files, directories, and links. */
|
||||
throw new Error(`session-snapshot: unsupported workspace entry ${JSON.stringify(path)}`)
|
||||
captured.push({ path, kind: 'symlink', target: await readlink(absolute) })
|
||||
}
|
||||
}
|
||||
return captured
|
||||
|
||||
+2
@@ -1,2 +1,4 @@
|
||||
version: 1
|
||||
profile: acp
|
||||
workspace:
|
||||
final: true
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
prepared at runtime
|
||||
@@ -242,7 +242,7 @@ describe('runScenario', () => {
|
||||
agent: { ...profileAgent, configPath: conflictPatch },
|
||||
cwd: dir,
|
||||
env: { DSH_SNAPSHOT: 'record', DSH_SNAPSHOT_FILE: fixtureFile },
|
||||
})).toThrow('ACP profile package conflict-package resolves to two directories')
|
||||
})).toThrow('snapshot profile package conflict-package resolves to two directories')
|
||||
|
||||
const invalidPatch = join(dir, 'invalid.cordis.yml')
|
||||
await writeFile(invalidPatch, 'not: a-list\n')
|
||||
|
||||
@@ -53,4 +53,32 @@ describe('session snapshot identity redaction', () => {
|
||||
expect(redacted[0]).toContain('session {{session:2}}')
|
||||
expect(redactSessionSnapshotIds(redacted)).toEqual(redacted)
|
||||
})
|
||||
|
||||
it('classifies semantic text plus command, RPC, and retry identity fields', () => {
|
||||
const semanticMessage = '88888888-8888-4888-8888-888888888888'
|
||||
const anonymousUser = '99999999-9999-4999-8999-999999999999'
|
||||
const retryId = 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'
|
||||
const source = [
|
||||
JSON.stringify({ type: 'not-a-session', data: { value: 'plain' } }),
|
||||
JSON.stringify({
|
||||
type: 'example',
|
||||
data: {
|
||||
commandId: 'command-7',
|
||||
rpcId: 'rpc-9',
|
||||
retryId,
|
||||
requestId: 'stable-readable-id',
|
||||
text: `Retain this as message ${semanticMessage}. Anonymous user: ${anonymousUser}`,
|
||||
},
|
||||
}),
|
||||
].join('\n')
|
||||
|
||||
const [redacted] = redactSessionSnapshotIds([source])
|
||||
expect(redacted).toContain('"commandId":"{{command:1}}"')
|
||||
expect(redacted).toContain('"rpcId":"{{rpc:1}}"')
|
||||
expect(redacted).toContain('"retryId":"{{retry:1}}"')
|
||||
expect(redacted).toContain('as message {{message:1}}')
|
||||
expect(redacted).toContain('Anonymous user: {{id:1}}')
|
||||
expect(redacted).toContain('"requestId":"stable-readable-id"')
|
||||
expect(redacted?.endsWith('\n')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -26,6 +26,7 @@ describe('snapshot manifest', () => {
|
||||
it('parses composition, recording, header, and exceptional replay metadata', () => {
|
||||
expect(parseSnapshotManifest([
|
||||
'version: 1',
|
||||
'scenario: sdk-case',
|
||||
'profile: sdk',
|
||||
'composition: continuable-subagent',
|
||||
'recording: authored',
|
||||
@@ -56,6 +57,7 @@ describe('snapshot manifest', () => {
|
||||
'',
|
||||
].join('\n'))).toEqual({
|
||||
version: 1,
|
||||
scenario: 'sdk-case',
|
||||
profile: 'sdk',
|
||||
composition: 'continuable-subagent',
|
||||
recording: 'authored',
|
||||
@@ -80,6 +82,38 @@ describe('snapshot manifest', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('parses independently optional header and input fields', () => {
|
||||
expect(parseSnapshotManifest([
|
||||
'version: 1',
|
||||
'profile: headless',
|
||||
'header:',
|
||||
' class: default',
|
||||
'input:',
|
||||
' task: Run once.',
|
||||
'',
|
||||
].join('\n'))).toEqual({
|
||||
version: 1,
|
||||
profile: 'headless',
|
||||
header: { class: 'default' },
|
||||
input: { task: 'Run once.' },
|
||||
})
|
||||
|
||||
expect(parseSnapshotManifest([
|
||||
'version: 1',
|
||||
'profile: sdk',
|
||||
'input:',
|
||||
' attachments:',
|
||||
' - id: sha256:one',
|
||||
' mediaType: image/png',
|
||||
' data: AQ==',
|
||||
'',
|
||||
].join('\n'))).toEqual({
|
||||
version: 1,
|
||||
profile: 'sdk',
|
||||
input: { attachments: [{ id: 'sha256:one', mediaType: 'image/png', data: 'AQ==' }] },
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
['', 'manifest must be a mapping'],
|
||||
['version: 2\nprofile: acp\n', 'manifest.version must equal 1'],
|
||||
@@ -91,6 +125,7 @@ describe('snapshot manifest', () => {
|
||||
['version: 1\nprofile: acp\nheader:\n class: base\n pin: false\n', 'manifest.header.pin must equal true when present'],
|
||||
['version: 1\nprofile: acp\nheader:\n class: base\n childToolSchemas: [1, 1]\n', 'manifest.header.childToolSchemas must be an array of unique positive integers'],
|
||||
['version: 1\nprofile: acp\nheader:\n class: base\n changes: -1\n', 'manifest.header.changes must be a non-negative integer'],
|
||||
['version: 1\nprofile: acp\nheader:\n class: base\n systemPromptSource: ../bad\n', 'manifest.header.systemPromptSource must be a lower-kebab-case name or corpus-relative path'],
|
||||
['version: 1\nprofile: acp\nreplay:\n override: false\n', 'manifest.replay.override must equal true'],
|
||||
['version: 1\nprofile: acp\nplatform: windows\n', 'manifest.platform must be posix or pwsh'],
|
||||
['version: 1\nprofile: acp\npermission: root\n', 'manifest.permission must be read-only, workspace-write, or danger-full-access'],
|
||||
@@ -101,6 +136,10 @@ describe('snapshot manifest', () => {
|
||||
['version: 1\nprofile: acp\ninput:\n task: ""\n', 'manifest.input.task must be a non-empty string when present'],
|
||||
['version: 1\nprofile: acp\ninput: {}\n', 'manifest.input must declare task or attachments'],
|
||||
['version: 1\nprofile: acp\ninput:\n attachments: []\n', 'manifest.input.attachments must be a non-empty array'],
|
||||
['version: 1\nprofile: acp\ninput:\n attachments:\n - id: raw\n mediaType: image/png\n data: AQ==\n', 'manifest.input.attachments[0].id must start with sha256:'],
|
||||
['version: 1\nprofile: acp\ninput:\n attachments:\n - id: sha256:one\n mediaType: image\n data: AQ==\n', 'manifest.input.attachments[0].mediaType must be a MIME type'],
|
||||
['version: 1\nprofile: acp\ninput:\n attachments:\n - id: sha256:one\n mediaType: image/png\n data: ""\n', 'manifest.input.attachments[0].data must be non-empty base64'],
|
||||
['version: 1\nprofile: acp\ninput:\n attachments:\n - id: sha256:one\n mediaType: image/png\n data: AQ==\n - id: sha256:one\n mediaType: image/png\n data: Ag==\n', 'manifest.input.attachments must have unique ids'],
|
||||
['version: 1\nprofile: acp\nsession: {}\n', 'manifest.session.source must be a non-empty string'],
|
||||
['version: 1\nprofile: acp\nsession:\n source: /tmp/session.jsonl\n', 'manifest.session.source must be a relative POSIX path'],
|
||||
['version: 1\nprofile: acp\nsession:\n source: ..\\session.jsonl\n', 'manifest.session.source must be a relative POSIX path'],
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"kind": "chunks",
|
||||
"chunks": [
|
||||
{ "type": "block-start", "index": 0, "blockType": "tool-call" },
|
||||
{ "type": "tool-call-delta", "index": 0, "id": "call_wait", "name": "bash", "argumentsDelta": "{\"command\":\"node -e \\\"require('node:fs').writeFileSync('started.txt', 'started'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}" },
|
||||
{ "type": "block-end", "index": 0, "block": { "type": "tool-call", "id": "call_wait", "name": "bash", "arguments": "{\"command\":\"node -e \\\"require('node:fs').writeFileSync('started.txt', 'started'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}" } },
|
||||
{ "type": "tool-call-delta", "index": 0, "id": "call_wait", "name": "bash", "argumentsDelta": "{\"command\":\"node -e \\\"const fs=require('node:fs'); fs.writeFileSync('started.tmp', 'started'); fs.renameSync('started.tmp', 'started.txt'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}" },
|
||||
{ "type": "block-end", "index": 0, "block": { "type": "tool-call", "id": "call_wait", "name": "bash", "arguments": "{\"command\":\"node -e \\\"const fs=require('node:fs'); fs.writeFileSync('started.tmp', 'started'); fs.renameSync('started.tmp', 'started.txt'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}" } },
|
||||
{ "type": "block-start", "index": 1, "blockType": "tool-call" },
|
||||
{ "type": "tool-call-delta", "index": 1, "id": "call_skipped", "name": "bash", "argumentsDelta": "{\"command\":\"printf skipped > skipped.txt\",\"description\":\"Write skipped marker\"}" },
|
||||
{ "type": "block-end", "index": 1, "block": { "type": "tool-call", "id": "call_skipped", "name": "bash", "arguments": "{\"command\":\"printf skipped > skipped.txt\",\"description\":\"Write skipped marker\"}" } },
|
||||
|
||||
@@ -12,15 +12,15 @@
|
||||
{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash"}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"call_wait","name":"bash","argumentsDelta":"{\"command\":\"node -e \\\"require('node:fs').writeFileSync('started.txt', 'started'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}"}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_wait","name":"bash","arguments":"{\"command\":\"node -e \\\"require('node:fs').writeFileSync('started.txt', 'started'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}"}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"call_wait","name":"bash","argumentsDelta":"{\"command\":\"node -e \\\"const fs=require('node:fs'); fs.writeFileSync('started.tmp', 'started'); fs.renameSync('started.tmp', 'started.txt'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}"}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"call_wait","name":"bash","arguments":"{\"command\":\"node -e \\\"const fs=require('node:fs'); fs.writeFileSync('started.tmp', 'started'); fs.renameSync('started.tmp', 'started.txt'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}"}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_skipped","name":"bash","argumentsDelta":"{\"command\":\"printf skipped > skipped.txt\",\"description\":\"Write skipped marker\"}"}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_skipped","name":"bash","arguments":"{\"command\":\"printf skipped > skipped.txt\",\"description\":\"Write skipped marker\"}"}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":10}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_wait","name":"bash","arguments":"{\"command\":\"node -e \\\"require('node:fs').writeFileSync('started.txt', 'started'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}"},{"type":"tool-call","id":"call_skipped","name":"bash","arguments":"{\"command\":\"printf skipped > skipped.txt\",\"description\":\"Write skipped marker\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:3}}"},"usage":{"inputTokens":10,"outputTokens":10}},"sourceEventSeqs":[12,13,14,15,16,17,18,19],"surfaceOp":"append"}
|
||||
{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_wait","name":"bash","arguments":"{\"command\":\"node -e \\\"require('node:fs').writeFileSync('started.txt', 'started'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}"}}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"call_wait","name":"bash","arguments":"{\"command\":\"node -e \\\"const fs=require('node:fs'); fs.writeFileSync('started.tmp', 'started'); fs.renameSync('started.tmp', 'started.txt'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}"},{"type":"tool-call","id":"call_skipped","name":"bash","arguments":"{\"command\":\"printf skipped > skipped.txt\",\"description\":\"Write skipped marker\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:3}}"},"usage":{"inputTokens":10,"outputTokens":10}},"sourceEventSeqs":[12,13,14,15,16,17,18,19],"surfaceOp":"append"}
|
||||
{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_wait","name":"bash","arguments":"{\"command\":\"node -e \\\"const fs=require('node:fs'); fs.writeFileSync('started.tmp', 'started'); fs.renameSync('started.tmp', 'started.txt'); setInterval(() => {}, 1000)\\\"\",\"description\":\"Wait until cancellation\"}"}}
|
||||
{"type":"tool/result","data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_wait"},"content":[{"type":"tool-result","toolCallId":"call_wait","content":[{"type":"text","text":"Error: tool call aborted"}],"isError":true}],"role":"user","id":"{{message:4}}"},"error":{"name":"AbortError","code":"ABORTED"}},"sourceEventSeqs":[21],"surfaceOp":"append"}
|
||||
{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_skipped","name":"bash","arguments":"{\"command\":\"printf skipped > skipped.txt\",\"description\":\"Write skipped marker\"}"}}
|
||||
{"type":"tool/result","data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_skipped"},"content":[{"type":"tool-result","toolCallId":"call_skipped","content":[{"type":"text","text":"Error: tool call aborted before dispatch"}],"isError":true}],"role":"user","id":"{{message:5}}"},"error":{"name":"AbortError","code":"ABORTED_BEFORE_DISPATCH"}},"sourceEventSeqs":[23],"surfaceOp":"append"}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"mcpCapabilities":{"http":true},"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false},"sessionCapabilities":{"close":{},"list":{},"resume":{}}},"authMethods":[]}}
|
||||
{"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","configOptions":[{"id":"model","name":"Model","category":"model","type":"select","currentValue":"[\"deepseek-official\",\"deepseek-v4-flash\"]","options":[{"group":"deepseek-official","name":"DeepSeek","options":[{"value":"[\"deepseek-official\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek-official\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]}]}]}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_wait","title":"bash","kind":"other","status":"in_progress","rawInput":{"command":"node -e \"require('node:fs').writeFileSync('started.txt', 'started'); setInterval(() => {}, 1000)\"","description":"Wait until cancellation"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_wait","title":"bash","kind":"other","status":"in_progress","rawInput":{"command":"node -e \"const fs=require('node:fs'); fs.writeFileSync('started.tmp', 'started'); fs.renameSync('started.tmp', 'started.txt'); setInterval(() => {}, 1000)\"","description":"Wait until cancellation"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_wait","status":"failed","content":[{"type":"content","content":{"type":"text","text":"Error: tool call aborted"}}]}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_skipped","title":"bash","kind":"other","status":"in_progress","rawInput":{"command":"printf skipped > skipped.txt","description":"Write skipped marker"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_skipped","status":"failed","content":[{"type":"content","content":{"type":"text","text":"Error: tool call aborted before dispatch"}}]}}}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { cp, copyFile, mkdir, readFile, readdir, rm, utimes, writeFile } from 'n
|
||||
import { existsSync } from 'node:fs'
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import { homedir } from 'node:os'
|
||||
import { delimiter, dirname, join } from 'node:path'
|
||||
import { basename, delimiter, dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
fixtureContext,
|
||||
formatSystemPromptSnapshot,
|
||||
formatToolSchemasSnapshot,
|
||||
materializeProfilePatch,
|
||||
normalizeSessionSnapshots,
|
||||
normalizedHeaders,
|
||||
normalizedSystemPrompts,
|
||||
@@ -62,7 +63,7 @@ function snapshotMode(value: string | undefined): SnapshotMode {
|
||||
}
|
||||
|
||||
const mode = snapshotMode(process.env.DSH_SNAPSHOT)
|
||||
const RUNTIME_WORKSPACE_ENTRIES = ['.agents', '.dsh'] as const
|
||||
const RUNTIME_WORKSPACE_ENTRIES = ['.agents', '.dsh', '.snapshot-patches'] as const
|
||||
|
||||
interface JsonObject {
|
||||
[key: string]: unknown
|
||||
@@ -507,11 +508,15 @@ describe('headless recorded-session snapshots', () => {
|
||||
const fixtureFiles = sessionFixtureNames(await readdir(scenario.dir))
|
||||
const replaying = mode !== 'record'
|
||||
const compositionPatch = join(composition.dir, replaying ? 'cordis.snapshot.yml' : 'cordis.yml')
|
||||
const patches = [
|
||||
const patchSources = [
|
||||
join(baseComposition.dir, 'cordis.yml'),
|
||||
...composition === baseComposition && !replaying ? [] : [compositionPatch],
|
||||
join(baseComposition.dir, 'model.cordis.yml'),
|
||||
]
|
||||
const patchRoot = '.snapshot-patches'
|
||||
const patches = patchSources.map((source, index) => source.endsWith('.snapshot.yml')
|
||||
? join(patchRoot, `${String(index)}-${basename(source)}`)
|
||||
: source)
|
||||
|
||||
let actualLogs: SessionLog[] = []
|
||||
let initialWorkspace: WorkspaceSnapshotEntry[] | undefined
|
||||
@@ -556,6 +561,12 @@ describe('headless recorded-session snapshots', () => {
|
||||
DSH_TELEMETRY_DISABLED: '1',
|
||||
},
|
||||
prepare: async (cwd) => {
|
||||
await mkdir(join(cwd, patchRoot), { recursive: true })
|
||||
patchSources.forEach((source, index) => {
|
||||
if (source.endsWith('.snapshot.yml')) {
|
||||
materializeProfilePatch(source, cwd, join(cwd, patchRoot), index)
|
||||
}
|
||||
})
|
||||
await seedWorkspace(scenario, cwd)
|
||||
initialWorkspace = await captureWorkspaceSnapshot(cwd, {
|
||||
ignoredRootEntries: RUNTIME_WORKSPACE_ENTRIES,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,10 +16,6 @@
|
||||
- img
|
||||
- img
|
||||
- text: Context injection @deepseek-ai/dsh-system-prompt
|
||||
- button "Think The user wants me to use the ask_user_question tool to ask them a specific question with the given parameters. Let me do exactly that.":
|
||||
- img
|
||||
- img
|
||||
- text: Think The user wants me to use the ask_user_question tool to ask them a specific question with the given parameters. Let me do exactly that.
|
||||
- text: Running
|
||||
- button "Ask question waiting":
|
||||
- img
|
||||
|
||||
@@ -1,40 +1,36 @@
|
||||
{"type":"session","version":0,"id":"{{session:1}}","createdAt":1787520632977,"cwd":"{{cwd}}","agentPreset":"standard"}
|
||||
{"type":"session","version":0,"id":"{{session:1}}","createdAt":1787528667010,"cwd":"{{cwd}}","agentPreset":"standard"}
|
||||
{"type":"permission/preset","data":{"preset":"workspace-write"}}
|
||||
{"type":"sandbox/mode","data":{"mode":"workspace-write"}}
|
||||
{"type":"approval/policy","data":{"policy":"ask"}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Use the ask_user_question tool to ask me exactly one question with id \"checkpoint\", question \"Ready to continue?\", header \"Checkpoint\", and options labeled \"Yes\" and \"No\". After I answer, reply with one short sentence acknowledging my answer and stop."}],"source":{"kind":"user","rpcId":"{{rpc:1}}","clientTimeZone":"Asia/Shanghai"},"role":"user","id":"{{message:1}}"}]}}
|
||||
{"type":"turn/start","data":{"turn":1}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"Interjection: include the word BANANA in your final reply."}],"source":{"kind":"user","rpcId":"{{rpc:2}}","clientTimeZone":"Asia/Shanghai"},"role":"user","id":"{{message:2}}"}]}}
|
||||
{"type":"step/start","data":{"turn":1,"step":1}}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Use the ask_user_question tool to ask me exactly one question with id \"checkpoint\", question \"Ready to continue?\", header \"Checkpoint\", and options labeled \"Yes\" and \"No\". After I answer, reply with one short sentence acknowledging my answer and stop."}],"source":{"kind":"user","rpcId":"{{rpc:1}}","clientTimeZone":"Asia/Shanghai"},"role":"user","id":"{{message:1}}"},"surfaceOp":"append"}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: workspace-write. Any available operation enforced by the DSH file sandbox may modify files under the session workspace: \"{{cwd}}\". Some platform temporary areas may also be writable.\n\nApproval policy: ask. Operations that require approval may ask through the configured answerers; without an available answerer, the request fails closed."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: workspace-write. Any available operation enforced by the DSH file sandbox may modify files under the session workspace: \"{{cwd}}\". Some platform temporary areas may also be writable."},{"name":"approval:policy","text":"Approval policy: ask. Operations that require approval may ask through the configured answerers; without an available answerer, the request fails closed."}]},"role":"user","id":"{{message:3}}"},"surfaceOp":"append"}
|
||||
{"type":"session/title","data":{"title":"Use the ask_user_question tool to","messageSeqs":[8],"source":{"kind":"fallback"}}}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Current runtime context. This snapshot supersedes earlier runtime-context snapshots.\n\nCurrent DSH file policy: workspace-write. Any available operation enforced by the DSH file sandbox may modify files under the session workspace: \"{{cwd}}\". Some platform temporary areas may also be writable.\n\nApproval policy: ask. Operations that require approval may ask through the configured answerers; without an available answerer, the request fails closed."}],"source":{"kind":"plugin","plugin":"@deepseek-ai/dsh-system-prompt","form":"snapshot","sections":[{"name":"sandbox:policy","text":"Current DSH file policy: workspace-write. Any available operation enforced by the DSH file sandbox may modify files under the session workspace: \"{{cwd}}\". Some platform temporary areas may also be writable."},{"name":"approval:policy","text":"Approval policy: ask. Operations that require approval may ask through the configured answerers; without an available answerer, the request fails closed."}]},"role":"user","id":"{{message:2}}"},"surfaceOp":"append"}
|
||||
{"type":"session/title","data":{"title":"Use the ask_user_question tool to","messageSeqs":[7],"source":{"kind":"fallback"}}}
|
||||
{"type":"request/header","data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"request/context","data":{"provider":"deepseek-official","model":"deepseek-v4-flash","contextWindow":128000}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"reasoning-chunks","data":{"turn":1,"step":1,"index":0,"dt":[52,52,52,52,52,50,50,52,52,51,51,50,52,53,50,52,52,52,52,51,52,51,50,51,52,52,51,51],"texts":["The"," user"," wants"," me"," to"," use"," the"," ask","_user","_","question"," tool"," to"," ask"," them"," a"," specific"," question"," with"," the"," given"," parameters","."," Let"," me"," do"," exactly"," that","."]}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"tool-call-chunks","data":{"turn":1,"step":1,"index":1,"dt":[51,52,51,51,51,52,52,52,50,52,52,50,52,50,52,50,50,51,51,51,52,50,53,50,51,50,53,51,52,51,52,51,49,51,53,52,51,51,52,52,50,52,53,53,51,52,50],"id":"call_00_sAvjivLShvnWVk0sPQPV7661","name":"ask_user_question","args":["","{","\"","questions","\"",": ","[","{\"","id","\":"," \"","check","point","\","," \"","question","\":"," \"","Ready"," to"," continue","?\","," \"","header","\":"," \"","Check","point","\","," \"","options","\":"," [","{\"","label","\":"," \"","Yes","\"},"," {\"","label","\":"," \"","No","\"","}]","}]","}"]}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to use the ask_user_question tool to ask them a specific question with the given parameters. Let me do exactly that."}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_sAvjivLShvnWVk0sPQPV7661","name":"ask_user_question","arguments":"{\"questions\": [{\"id\": \"checkpoint\", \"question\": \"Ready to continue?\", \"header\": \"Checkpoint\", \"options\": [{\"label\": \"Yes\"}, {\"label\": \"No\"}]}]}"}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":151,"outputTokens":115,"cacheReadTokens":7680,"reasoningTokens":29}}}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Interjection: include the word BANANA in your final reply."}],"source":{"kind":"user","rpcId":"{{rpc:2}}","clientTimeZone":"Asia/Shanghai"},"role":"user","id":"{{message:3}}"}]}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[],"outcome":"canceled"}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"inserted":[{"content":[{"type":"text","text":"Interjection: include the word BANANA in your final reply."}],"source":{"kind":"user","rpcId":"{{rpc:2}}","clientTimeZone":"Asia/Shanghai"},"role":"user","id":"{{message:3}}"}]}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":0,"id":"steering-question","name":"ask_user_question","argumentsDelta":"{\"questions\":[{\"id\":\"checkpoint\",\"question\":\"Ready to continue?\",\"header\":\"Checkpoint\",\"options\":[{\"label\":\"Yes\"},{\"label\":\"No\"}]}]}"}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"tool-call","id":"steering-question","name":"ask_user_question","arguments":"{\"questions\":[{\"id\":\"checkpoint\",\"question\":\"Ready to continue?\",\"header\":\"Checkpoint\",\"options\":[{\"label\":\"Yes\"},{\"label\":\"No\"}]}]}"}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to use the ask_user_question tool to ask them a specific question with the given parameters. Let me do exactly that."},{"type":"tool-call","id":"call_00_sAvjivLShvnWVk0sPQPV7661","name":"ask_user_question","arguments":"{\"questions\": [{\"id\": \"checkpoint\", \"question\": \"Ready to continue?\", \"header\": \"Checkpoint\", \"options\": [{\"label\": \"Yes\"}, {\"label\": \"No\"}]}]}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":151,"outputTokens":115,"cacheReadTokens":7680,"reasoningTokens":29}},"sourceEventSeqs":[13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95],"surfaceOp":"append"}
|
||||
{"type":"tool/call","data":{"turn":1,"step":1,"callId":"call_00_sAvjivLShvnWVk0sPQPV7661","name":"ask_user_question","arguments":"{\"questions\": [{\"id\": \"checkpoint\", \"question\": \"Ready to continue?\", \"header\": \"Checkpoint\", \"options\": [{\"label\": \"Yes\"}, {\"label\": \"No\"}]}]}"}}
|
||||
{"type":"tool/result","data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_00_sAvjivLShvnWVk0sPQPV7661"},"content":[{"type":"tool-result","toolCallId":"call_00_sAvjivLShvnWVk0sPQPV7661","content":[{"type":"text","text":"{\"answers\":[{\"id\":\"checkpoint\",\"selected\":[\"Yes\"]}]}"}],"isError":false}],"role":"user","id":"{{message:5}}"}},"sourceEventSeqs":[97],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"tool-call","id":"steering-question","name":"ask_user_question","arguments":"{\"questions\":[{\"id\":\"checkpoint\",\"question\":\"Ready to continue?\",\"header\":\"Checkpoint\",\"options\":[{\"label\":\"Yes\"},{\"label\":\"No\"}]}]}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:4}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[15,16,17,18,19],"surfaceOp":"append"}
|
||||
{"type":"tool/call","data":{"turn":1,"step":1,"callId":"steering-question","name":"ask_user_question","arguments":"{\"questions\":[{\"id\":\"checkpoint\",\"question\":\"Ready to continue?\",\"header\":\"Checkpoint\",\"options\":[{\"label\":\"Yes\"},{\"label\":\"No\"}]}]}"}}
|
||||
{"type":"tool/result","data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"steering-question"},"content":[{"type":"tool-result","toolCallId":"steering-question","content":[{"type":"text","text":"{\"answers\":[{\"id\":\"checkpoint\",\"selected\":[\"Yes\"]}]}"}],"isError":false}],"role":"user","id":"{{message:5}}"}},"sourceEventSeqs":[21],"surfaceOp":"append"}
|
||||
{"type":"step/end","data":{"turn":1,"step":1}}
|
||||
{"type":"agent/inbox/spliced","data":{"target":"next-step","start":0,"removedCount":1,"inserted":[]}}
|
||||
{"type":"step/start","data":{"turn":1,"step":2}}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Interjection: include the word BANANA in your final reply."}],"source":{"kind":"user","rpcId":"{{rpc:2}}","clientTimeZone":"Asia/Shanghai"},"role":"user","id":"{{message:2}}"},"surfaceOp":"append"}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"reasoning-chunks","data":{"turn":1,"step":2,"index":0,"dt":[53,53,51,51,53,51,52,52,51,52,50,53,52,51,49,51,52,52,50,52,52,52,51,53,51,51,50,52],"texts":["The"," user"," selected"," \"","Yes","\""," and"," wants"," me"," to"," include"," the"," word"," \"","B","AN","ANA","\""," in"," my"," final"," reply","."," Let"," me"," acknowledge"," their"," answer","."]}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"text-chunks","data":{"turn":1,"step":2,"index":1,"dt":[51,53,53,52,51,52,53,51,51,51],"texts":["Great",","," let","'s"," move"," forward","."," B","AN","ANA","!"]}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user selected \"Yes\" and wants me to include the word \"BANANA\" in my final reply. Let me acknowledge their answer."}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"Great, let's move forward. BANANA!"}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":172,"outputTokens":41,"cacheReadTokens":7808,"reasoningTokens":29}}}}
|
||||
{"type":"user/message","data":{"content":[{"type":"text","text":"Interjection: include the word BANANA in your final reply."}],"source":{"kind":"user","rpcId":"{{rpc:2}}","clientTimeZone":"Asia/Shanghai"},"role":"user","id":"{{message:3}}"},"surfaceOp":"append"}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":0,"text":"Great, let's move forward. BANANA!"}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"text","text":"Great, let's move forward. BANANA!"}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":10,"outputTokens":5}}}}
|
||||
{"type":"assistant/chunk","data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user selected \"Yes\" and wants me to include the word \"BANANA\" in my final reply. Let me acknowledge their answer."},{"type":"text","text":"Great, let's move forward. BANANA!"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":172,"outputTokens":41,"cacheReadTokens":7808,"reasoningTokens":29}},"sourceEventSeqs":[103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148],"surfaceOp":"append"}
|
||||
{"type":"assistant/message","data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"text","text":"Great, let's move forward. BANANA!"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-flash"},"id":"{{message:6}}"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[27,28,29,30,31],"surfaceOp":"append"}
|
||||
{"type":"step/end","data":{"turn":1,"step":2}}
|
||||
{"type":"turn/end","data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
- img
|
||||
- img
|
||||
- text: Context injection @deepseek-ai/dsh-system-prompt
|
||||
- button "Think The user wants me to use the ask_user_question tool to ask them a specific question with the given parameters. Let me do exactly that.":
|
||||
- img
|
||||
- img
|
||||
- text: Think The user wants me to use the ask_user_question tool to ask them a specific question with the given parameters. Let me do exactly that.
|
||||
- button "Ask question 1/1 answered":
|
||||
- img
|
||||
- img
|
||||
@@ -27,10 +23,6 @@
|
||||
- text: "Interjection: include the word BANANA in your final reply. {{clock}}"
|
||||
- button "Copy":
|
||||
- img
|
||||
- button "Think The user selected \"Yes\" and wants me to include the word \"BANANA\" in my final reply. Let me acknowledge their answer.":
|
||||
- img
|
||||
- img
|
||||
- text: Think The user selected "Yes" and wants me to include the word "BANANA" in my final reply. Let me acknowledge their answer.
|
||||
- paragraph: Great, let's move forward. BANANA!
|
||||
- button "Copy":
|
||||
- img
|
||||
@@ -48,6 +40,6 @@
|
||||
- button "Select model, current DeepSeek-V4-Flash":
|
||||
- text: DeepSeek-V4-Flash
|
||||
- img
|
||||
- button "6% of context used"
|
||||
- button "0% of context used"
|
||||
- button "Send message" [disabled]
|
||||
- text: 1 turns · 2 steps LLM {{duration}} · Tool call {{duration}} TTFT avg {{duration}} · {{throughput}} tok/s Cache hit 98% Input 15.8K tok · Output 156 tok
|
||||
- text: 1 turns · 2 steps LLM {{duration}} · Tool call {{duration}} TTFT avg {{duration}} · {{throughput}} tok/s Cache hit 0% Input 20 tok · Output 10 tok
|
||||
|
||||
Reference in New Issue
Block a user