From e37985f5d51abb77b6b94f44425ef85aa2a4cc82 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Sat, 22 Aug 2026 19:32:27 +0800 Subject: [PATCH] test(acp): align assembled automation coverage --- examples/acp-agent/tests/escalation.e2e.ts | 20 ++++++++++++++++-- .../goal-round-driver/stdout.expected.jsonl | 14 ++++++++----- .../goal-wrapup/stdout.expected.jsonl | 12 +++++++---- examples/acp-agent/tests/hooks.e2e.ts | 14 ++++++++++--- .../stdout.expected.jsonl | 6 +++--- packages/examples/acp-demo/package.json | 2 +- .../acp-demo/tests/control-surface.cordis.yml | 2 +- .../acp-snapshot/src/normalize.ts | 5 +++++ .../acp-snapshot/tests/normalize.spec.ts | 21 +++++++++++++++++++ pnpm-lock.yaml | 6 +++--- 10 files changed, 80 insertions(+), 22 deletions(-) diff --git a/examples/acp-agent/tests/escalation.e2e.ts b/examples/acp-agent/tests/escalation.e2e.ts index e754dc8799..831e7631b2 100644 --- a/examples/acp-agent/tests/escalation.e2e.ts +++ b/examples/acp-agent/tests/escalation.e2e.ts @@ -53,10 +53,24 @@ const hasSeatbelt = process.platform === 'darwin' && spawnSync('sandbox-exec', [ }).status === 0 const hasRunner = hasBwrap || hasSeatbelt +const STANDARD_EXECUTION_UPDATES = new Set([ + 'agent_message_chunk', + 'agent_thought_chunk', + 'tool_call', + 'tool_call_update', + 'usage_update', +]) + interface Spawned extends LaunchedAcpTestAgent { permissionRequests: RequestPermissionRequest[] } +/** Require a model answer while allowing every standard semantic execution update. */ +function expectStandardExecutionUpdates(updates: LaunchedAcpTestAgent['updates']): void { + expect(updates.some(update => update.sessionUpdate === 'agent_message_chunk')).toBe(true) + expect(updates.every(update => STANDARD_EXECUTION_UPDATES.has(update.sessionUpdate))).toBe(true) +} + /** Boot the example with an optional sandbox override; the scripted client answers every permission prompt with `answer`. */ function launchExampleAcpAgent( cwd: string, @@ -112,7 +126,9 @@ describe('default sandbox composition keyless smoke (real cordis.yml via the Loa const init = await client.initialize({ protocolVersion: PROTOCOL_VERSION, clientCapabilities: {} }) expect(init.protocolVersion).toBe(PROTOCOL_VERSION) expect(init.agentCapabilities).toEqual({ + mcpCapabilities: { http: true }, promptCapabilities: { image: false, audio: false, embeddedContext: false }, + sessionCapabilities: { close: {}, list: {}, resume: {} }, }) const { sessionId } = await client.newSession({ cwd: workdir, mcpServers: [] }) expect(sessionId.length).toBeGreaterThan(0) @@ -136,7 +152,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY || !hasRunner)('default sandbox co }], }) expect(['end_turn', 'max_tokens']).toContain(res.stopReason) - expect(updates.every(update => update.sessionUpdate === 'agent_message_chunk')).toBe(true) + expectStandardExecutionUpdates(updates) // The WORLD: the approved escalated retry landed the write. const proof = await readFile(join(workdir, 'escalated.txt'), 'utf8') @@ -168,7 +184,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY || !hasRunner)('default sandbox co }], }) expect(['end_turn', 'max_tokens']).toContain(res.stopReason) - expect(updates.every(update => update.sessionUpdate === 'agent_message_chunk')).toBe(true) + expectStandardExecutionUpdates(updates) // The WORLD: rejected means the file never appeared. await expect(readFile(join(workdir, 'refused.txt'), 'utf8')).rejects.toThrow() diff --git a/examples/acp-agent/tests/goal-snapshots/goal-round-driver/stdout.expected.jsonl b/examples/acp-agent/tests/goal-snapshots/goal-round-driver/stdout.expected.jsonl index c0a4330ea9..ac407eb9c9 100644 --- a/examples/acp-agent/tests/goal-snapshots/goal-round-driver/stdout.expected.jsonl +++ b/examples/acp-agent/tests/goal-snapshots/goal-round-driver/stdout.expected.jsonl @@ -1,6 +1,10 @@ -{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}} -{"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}"}} -{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"GOAL READY"}}}} +{"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_goal_create","title":"create_goal","kind":"other","status":"in_progress","rawInput":{"objective":"Finish the ACP goal-round-driver snapshot proof","max_goal_rounds":2}}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_goal_create","status":"completed","content":[{"type":"content","content":{"type":"text","text":"{\"goal\":{\"id\":\"goal-{{sessionId}}\",\"revision\":1,\"objective\":\"Finish the ACP goal-round-driver snapshot proof\",\"phase\":\"active\",\"roundsStarted\":0,\"maxGoalRounds\":2},\"activation\":\"armed\"}"}}]}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_goal_get","title":"get_goal","kind":"other","status":"in_progress","rawInput":{}}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_goal_get","status":"completed","content":[{"type":"content","content":{"type":"text","text":"{\"goal\":{\"id\":\"goal-{{sessionId}}\",\"revision\":1,\"objective\":\"Finish the ACP goal-round-driver snapshot proof\",\"phase\":\"active\",\"roundsStarted\":0,\"maxGoalRounds\":2},\"activation\":\"armed\"}"}}]}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","messageId":"{{messageId}}","content":{"type":"text","text":"GOAL READY"}}}} {"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}} -{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"GOAL ROUND ONE"}}}} -{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"partial"}}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","messageId":"{{messageId}}","content":{"type":"text","text":"GOAL ROUND ONE"}}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","messageId":"{{messageId}}","content":{"type":"text","text":"partial"}}}} diff --git a/examples/acp-agent/tests/goal-snapshots/goal-wrapup/stdout.expected.jsonl b/examples/acp-agent/tests/goal-snapshots/goal-wrapup/stdout.expected.jsonl index e5c0dbb921..26ecad6bd3 100644 --- a/examples/acp-agent/tests/goal-snapshots/goal-wrapup/stdout.expected.jsonl +++ b/examples/acp-agent/tests/goal-snapshots/goal-wrapup/stdout.expected.jsonl @@ -1,5 +1,9 @@ -{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}} -{"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}"}} -{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"GOAL READY"}}}} +{"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_goal_create","title":"create_goal","kind":"other","status":"in_progress","rawInput":{"objective":"Finish the ACP goal wrap-up snapshot proof","max_goal_rounds":2}}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_goal_create","status":"completed","content":[{"type":"content","content":{"type":"text","text":"{\"goal\":{\"id\":\"goal-{{sessionId}}\",\"revision\":1,\"objective\":\"Finish the ACP goal wrap-up snapshot proof\",\"phase\":\"active\",\"roundsStarted\":0,\"maxGoalRounds\":2},\"activation\":\"armed\"}"}}]}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","messageId":"{{messageId}}","content":{"type":"text","text":"GOAL READY"}}}} {"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}} -{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"GOAL WRAP-UP: the snapshot objective is achieved and this closing message reaches the user."}}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_goal_complete","title":"update_goal","kind":"other","status":"in_progress","rawInput":{"goal_id":"goal-{{sessionId}}","revision":1,"action":"complete"}}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_goal_complete","status":"completed","content":[{"type":"content","content":{"type":"text","text":"{\"goal\":{\"id\":\"goal-{{sessionId}}\",\"revision\":2,\"objective\":\"Finish the ACP goal wrap-up snapshot proof\",\"phase\":\"complete\",\"roundsStarted\":1,\"maxGoalRounds\":2},\"activation\":\"disarmed\"}"}}]}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","messageId":"{{messageId}}","content":{"type":"text","text":"GOAL WRAP-UP: the snapshot objective is achieved and this closing message reaches the user."}}}} diff --git a/examples/acp-agent/tests/hooks.e2e.ts b/examples/acp-agent/tests/hooks.e2e.ts index b99d022c17..994273a821 100644 --- a/examples/acp-agent/tests/hooks.e2e.ts +++ b/examples/acp-agent/tests/hooks.e2e.ts @@ -24,6 +24,14 @@ const AGENT: AgentUnderTest = { tsconfigPath: fileURLToPath(new URL('../../../tsconfig.json', import.meta.url)), } +const STANDARD_EXECUTION_UPDATES = new Set([ + 'agent_message_chunk', + 'agent_thought_chunk', + 'tool_call', + 'tool_call_update', + 'usage_update', +]) + let spawned: LaunchedAcpTestAgent | undefined let workdir: string | undefined @@ -65,8 +73,8 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('acp-agent e2e: a PreToolUse hook // Assert the denied operation independently of the model response. await expect(access(join(workdir, 'proof.txt'))).rejects.toThrow() - // ACP publishes only the committed answer; hook/tool trace stays in the session log. - expect(updates.length).toBeGreaterThan(0) - expect(updates.every(update => update.sessionUpdate === 'agent_message_chunk')).toBe(true) + // ACP publishes committed semantic execution facts, never hook internals or UI projections. + expect(updates.some(update => update.sessionUpdate === 'agent_message_chunk')).toBe(true) + expect(updates.every(update => STANDARD_EXECUTION_UPDATES.has(update.sessionUpdate))).toBe(true) }, 180_000) }) diff --git a/examples/acp-agent/tests/snapshots/fs-write-overwrite-bounded/stdout.expected.jsonl b/examples/acp-agent/tests/snapshots/fs-write-overwrite-bounded/stdout.expected.jsonl index 287dbecd6d..385903eb32 100644 --- a/examples/acp-agent/tests/snapshots/fs-write-overwrite-bounded/stdout.expected.jsonl +++ b/examples/acp-agent/tests/snapshots/fs-write-overwrite-bounded/stdout.expected.jsonl @@ -1,12 +1,12 @@ {"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"}]}]},{"id":"reasoning_effort","name":"Reasoning effort","category":"thought_level","type":"select","currentValue":"max","options":[{"value":"off","name":"off"},{"value":"low","name":"low"},{"value":"high","name":"high"},{"value":"max","name":"max"}]}]}} {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","messageId":"{{messageId}}","content":{"type":"text","text":"The user wants me to read data.txt first, then write to replace its contents with the exact line, then reply DONE."}}}} -{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"usage_update","used":6311,"size":1000000}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"usage_update","used":"{{usedTokens}}","size":1000000}}} {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_00_Jxz49JNt6i4oaDnzes2I0794","title":"read","kind":"other","status":"in_progress","rawInput":{"file_path":"data.txt"}}}} {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_00_Jxz49JNt6i4oaDnzes2I0794","status":"completed","content":[{"type":"content","content":{"type":"text","text":"{{cwd}}/data.txt\nfile\n\n1: original contents\n\n(End of file - total 1 lines)\n"}}]}}} -{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"usage_update","used":6414,"size":1000000}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"usage_update","used":"{{usedTokens}}","size":1000000}}} {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_00_ET_7mLiYX652hJA9GW6d1bl4653","title":"write","kind":"other","status":"in_progress","rawInput":{"file_path":"data.txt","content":"The replacement line is deliberately longer than the configured sixty-four byte diff-basis bound."}}}} {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_00_ET_7mLiYX652hJA9GW6d1bl4653","status":"completed","content":[{"type":"content","content":{"type":"text","text":"{{cwd}}/data.txt\nfile\n\nUpdated file\n"}}]}}} {"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","messageId":"{{messageId}}","content":{"type":"text","text":"DONE"}}}} -{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"usage_update","used":6473,"size":1000000}}} +{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"usage_update","used":"{{usedTokens}}","size":1000000}}} {"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}} diff --git a/packages/examples/acp-demo/package.json b/packages/examples/acp-demo/package.json index cbfc63e7d9..dc422c008e 100644 --- a/packages/examples/acp-demo/package.json +++ b/packages/examples/acp-demo/package.json @@ -65,12 +65,12 @@ "@deepseek-ai/dsh-agent-spine-demo": "workspace:^", "@deepseek-ai/dsh-app-boot": "workspace:^", "@deepseek-ai/dsh-invariants": "workspace:^", + "@deepseek-ai/dsh-llm": "workspace:^", "@deepseek-ai/dsh-session-checkpoint-policy": "workspace:^", "@deepseek-ai/dsh-session-persistence-jsonl": "workspace:^", "@deepseek-ai/dsh-session-query": "workspace:^", "@deepseek-ai/dsh-session-query-sqlite": "workspace:^", "@deepseek-ai/dsh-system-prompt": "workspace:^", - "@deepseek-ai/dsh-token-meter": "workspace:^", "@deepseek-ai/dsh-tools": "workspace:^", "@deepseek-ai/dsh-agent-instructions": "workspace:^", "@deepseek-ai/cordis": "workspace:^", diff --git a/packages/examples/acp-demo/tests/control-surface.cordis.yml b/packages/examples/acp-demo/tests/control-surface.cordis.yml index a6ccfdd876..e8fd47adc5 100644 --- a/packages/examples/acp-demo/tests/control-surface.cordis.yml +++ b/packages/examples/acp-demo/tests/control-surface.cordis.yml @@ -12,4 +12,4 @@ workspaceContext: false - id: token-meter - name: '@deepseek-ai/dsh-token-meter' + name: '../../../llm/token-meter/src/index.ts' diff --git a/packages/test-support/acp-snapshot/src/normalize.ts b/packages/test-support/acp-snapshot/src/normalize.ts index 8e3919c662..259f3ad8fe 100644 --- a/packages/test-support/acp-snapshot/src/normalize.ts +++ b/packages/test-support/acp-snapshot/src/normalize.ts @@ -8,6 +8,7 @@ const SESSION_ID = '{{sessionId}}' const MESSAGE_ID = '{{messageId}}' +const USED_TOKENS = '{{usedTokens}}' const CWD = '{{cwd}}' const SYSTEM = '{{system}}' const TOOLS = '{{tools}}' @@ -193,6 +194,10 @@ function scrubValue(value: unknown, ctx: NormalizeContext, cwdPathMode: CwdPathM if (value !== null && typeof value === 'object') { const out: Record = {} for (const [k, v] of Object.entries(value)) out[k] = scrubValue(v, ctx, cwdPathMode, k) + if ( + (value as { sessionUpdate?: unknown }).sessionUpdate === 'usage_update' + && typeof (value as { used?: unknown }).used === 'number' + ) out.used = USED_TOKENS return out } return value diff --git a/packages/test-support/acp-snapshot/tests/normalize.spec.ts b/packages/test-support/acp-snapshot/tests/normalize.spec.ts index 465a2d0f4f..86c6059a81 100644 --- a/packages/test-support/acp-snapshot/tests/normalize.spec.ts +++ b/packages/test-support/acp-snapshot/tests/normalize.spec.ts @@ -68,6 +68,27 @@ describe('normalizeStdout', () => { expect(out).toContain('"messageId":"{{messageId}}"') }) + it('stabilizes path-dependent context occupancy without hiding capacity', () => { + const raw = JSON.stringify({ + jsonrpc: '2.0', + method: 'session/update', + params: { + sessionId: ctx.sessionIds[0], + update: { sessionUpdate: 'usage_update', used: 6_438, size: 1_000_000 }, + }, + }) + + const frame = JSON.parse(normalizeStdout(raw, ctx)) as { + params: { update: { used: string; size: number } } + } + + expect(frame.params.update).toEqual({ + sessionUpdate: 'usage_update', + used: '{{usedTokens}}', + size: 1_000_000, + }) + }) + it('scrubs cwd at file URI and chained-punctuation boundaries', () => { const raw = JSON.stringify({ jsonrpc: '2.0', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f8671de95e..92a317e71a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3991,6 +3991,9 @@ importers: '@deepseek-ai/dsh-invariants': specifier: workspace:^ version: link:../../runtime-diagnostics/invariants + '@deepseek-ai/dsh-llm': + specifier: workspace:^ + version: link:../../llm/llm '@deepseek-ai/dsh-session-checkpoint-policy': specifier: workspace:^ version: link:../../session/session-checkpoint-policy @@ -4006,9 +4009,6 @@ importers: '@deepseek-ai/dsh-system-prompt': specifier: workspace:^ version: link:../../core/system-prompt - '@deepseek-ai/dsh-token-meter': - specifier: workspace:^ - version: link:../../llm/token-meter '@deepseek-ai/dsh-tools': specifier: workspace:^ version: link:../../core/tools