From cef99b17d47fdda90921da698cdc8d35e497e2b3 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Tue, 18 Aug 2026 14:31:22 +0800 Subject: [PATCH] test(acp): snapshot persistent PowerShell tool --- examples/acp-agent/tests/acp.snapshot.ts | 10 +++++ .../tests/persistent-pwsh.cordis.snapshot.yml | 45 +++++++++++++++++++ .../tests/persistent-pwsh.cordis.yml | 42 +++++++++++++++++ .../persistent-pwsh-tool-turn/input.json | 7 +++ .../persistent-pwsh-tool-turn/session.jsonl | 34 ++++++++++++++ .../stdout.expected.jsonl | 4 ++ .../system-prompt.expected.md | 3 ++ .../tool-schemas.expected.json | 21 +++++++++ examples/package.json | 1 + pnpm-lock.yaml | 3 ++ 10 files changed, 170 insertions(+) create mode 100644 examples/acp-agent/tests/persistent-pwsh.cordis.snapshot.yml create mode 100644 examples/acp-agent/tests/persistent-pwsh.cordis.yml create mode 100644 examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/input.json create mode 100644 examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/session.jsonl create mode 100644 examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/stdout.expected.jsonl create mode 100644 examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/system-prompt.expected.md create mode 100644 examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/tool-schemas.expected.json diff --git a/examples/acp-agent/tests/acp.snapshot.ts b/examples/acp-agent/tests/acp.snapshot.ts index c5bf81fc1f..b784529d4e 100644 --- a/examples/acp-agent/tests/acp.snapshot.ts +++ b/examples/acp-agent/tests/acp.snapshot.ts @@ -61,6 +61,7 @@ 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)) const PARTIAL_LANDLOCK_CONFIG = fileURLToPath(new URL('../partial-landlock.cordis.yml', import.meta.url)) const PWSH_CONFIG = fileURLToPath(new URL('./pwsh.cordis.yml', import.meta.url)) +const PERSISTENT_PWSH_CONFIG = fileURLToPath(new URL('./persistent-pwsh.cordis.yml', import.meta.url)) const BACKGROUND_TASK_ADMISSION_CONFIG = fileURLToPath( new URL('../background-job-admission.cordis.yml', import.meta.url), ) @@ -264,6 +265,15 @@ const SCENARIOS: Scenario[] = [ // newline and one recording replays on every host. pwshOnly: true, }, + { + name: 'persistent-pwsh-tool-turn', + hasModelTurn: true, + recorded: true, + pinsHeader: true, + headerClass: 'persistent-pwsh', + configPath: PERSISTENT_PWSH_CONFIG, + pwshOnly: true, + }, // Authored keyless replay through a test-only partial-Landlock provider: // the exact compatibility notice must stay ordinary stderr when the wrapped // `false` command exits 1, rather than becoming SANDBOX_UNAVAILABLE. diff --git a/examples/acp-agent/tests/persistent-pwsh.cordis.snapshot.yml b/examples/acp-agent/tests/persistent-pwsh.cordis.snapshot.yml new file mode 100644 index 0000000000..7b90b2298b --- /dev/null +++ b/examples/acp-agent/tests/persistent-pwsh.cordis.snapshot.yml @@ -0,0 +1,45 @@ +# Keyless replay counterpart to persistent-pwsh.cordis.yml. +- id: llm-replay + name: '@deepseek-ai/dsh-llm-replay' + config: + providers: + - id: deepseek-official + name: DeepSeek + models: + - id: deepseek-v4-pro + +- id: terminal + name: '@deepseek-ai/dsh-terminal' + +- id: sandbox-policy + name: '@deepseek-ai/dsh-sandbox-policy' + config: + mode: danger-full-access + workspaceRoot: !!js process.cwd() + +- id: subprocess + name: '@deepseek-ai/dsh-subprocess-local' + +- id: terminal-pwsh + name: '@deepseek-ai/dsh-terminal-bash' + config: + shellDialect: pwsh + timeoutMs: 30000 + +- id: acp-agent + name: '@deepseek-ai/dsh-acp-demo' + config: + provider: deepseek-official + model: deepseek-v4-pro + persistenceRoot: !!js process.env.DSH_SNAPSHOT_SESSIONS_ROOT ?? './.sessions' + persistenceCompression: none + workspaceContext: false + skills: + enabled: false + toolBash: false + toolJobs: false + goals: false + persona: You are a concise snapshot agent working in {{cwd}}. + +- id: tool-pwsh-persistent + name: '@deepseek-ai/dsh-tool-pwsh-persistent' diff --git a/examples/acp-agent/tests/persistent-pwsh.cordis.yml b/examples/acp-agent/tests/persistent-pwsh.cordis.yml new file mode 100644 index 0000000000..0b3cd18c70 --- /dev/null +++ b/examples/acp-agent/tests/persistent-pwsh.cordis.yml @@ -0,0 +1,42 @@ +# Minimal live counterpart for the persistent-pwsh-tool-turn snapshot composition. +- id: llm-deepseek + name: '@deepseek-ai/dsh-llm-deepseek' + config: + models: + - id: deepseek-v4-pro + +- id: sandbox-policy + name: '@deepseek-ai/dsh-sandbox-policy' + config: + mode: danger-full-access + workspaceRoot: !!js process.cwd() + +- id: subprocess + name: '@deepseek-ai/dsh-subprocess-local' + +- id: terminal + name: '@deepseek-ai/dsh-terminal' + +- id: terminal-pwsh + name: '@deepseek-ai/dsh-terminal-bash' + config: + shellDialect: pwsh + timeoutMs: 30000 + +- id: acp-agent + name: '@deepseek-ai/dsh-acp-demo' + config: + provider: deepseek-official + model: deepseek-v4-pro + persistenceRoot: !!js process.env.DSH_SNAPSHOT_SESSIONS_ROOT ?? './.sessions' + persistenceCompression: !!js "process.env.DSH_SNAPSHOT === undefined ? 'zstd' : 'none'" + workspaceContext: false + skills: + enabled: false + toolBash: false + toolJobs: false + goals: false + persona: You are a concise snapshot agent working in {{cwd}}. + +- id: tool-pwsh-persistent + name: '@deepseek-ai/dsh-tool-pwsh-persistent' diff --git a/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/input.json b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/input.json new file mode 100644 index 0000000000..653e9a346c --- /dev/null +++ b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/input.json @@ -0,0 +1,7 @@ +{ + "steps": [ + { "op": "initialize" }, + { "op": "newSession" }, + { "op": "prompt", "text": "Use the pwsh tool to run exactly: [Console]::Out.Write('PWSH_OK'). Then reply with the single word DONE and stop." } + ] +} diff --git a/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/session.jsonl b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/session.jsonl new file mode 100644 index 0000000000..220bdd516e --- /dev/null +++ b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/session.jsonl @@ -0,0 +1,34 @@ +{"type":"session","version":0,"id":"0b7ff6ab-2486-4b2f-a43e-0fa29a1a46ed","createdAt":1785678162241,"cwd":"{{cwd}}","delegationDepth":0} +{"type":"agent/inbox/spliced","seq":0,"time":1785898456879,"data":{"target":"next-turn","start":0,"inserted":[{"content":[{"type":"text","text":"Use the pwsh tool to run exactly: [Console]::Out.Write('PWSH_OK'). Then reply with the single word DONE and stop."}],"source":{"kind":"user"},"role":"user","id":"b4ce4fdc-a87a-41d0-b418-80a0fb235abb"}]}} +{"type":"turn/start","seq":1,"time":1785898456880,"data":{"turn":1}} +{"type":"agent/inbox/spliced","seq":2,"time":1785898456880,"data":{"target":"next-turn","start":0,"removedCount":1,"inserted":[]}} +{"type":"step/start","seq":3,"time":1785678162261,"data":{"turn":1,"step":1}} +{"type":"user/message","seq":4,"time":1785898456903,"data":{"content":[{"type":"text","text":"Use the pwsh tool to run exactly: [Console]::Out.Write('PWSH_OK'). Then reply with the single word DONE and stop."}],"source":{"kind":"user"},"role":"user","id":"b4ce4fdc-a87a-41d0-b418-80a0fb235abb"},"surfaceOp":"append"} +{"type":"session/title","seq":5,"time":1785898456903,"data":{"title":"Use the pwsh tool to","messageSeqs":[4],"source":{"kind":"fallback"}}} +{"type":"request/header","seq":6,"time":1785898456904,"data":{"header":{"config":{"provider":"deepseek-official","model":"deepseek-v4-pro"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}} +{"type":"request/context","seq":7,"time":1785898456904,"data":{"provider":"deepseek-official","model":"deepseek-v4-pro"}} +{"type":"assistant/chunk","seq":8,"time":1785678162968,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}} +{"type":"reasoning-chunks","seq0":9,"time0":1785678163361,"data":{"turn":1,"step":1,"index":0,"dt":[0,0,0,1,0,0,0,0,0,17,0,0,0,0,0,0,0,1,290,0,1],"texts":["The"," user"," wants"," me"," to"," run"," a"," PowerShell"," command"," and"," then"," reply"," with"," \"","D","ONE","\"."," Let"," me"," execute"," it","."]}} +{"type":"assistant/chunk","seq":31,"time":1785678163671,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}} +{"type":"tool-call-chunks","seq0":32,"time0":1785678163671,"data":{"turn":1,"step":1,"index":1,"dt":[0,0,0,0,0,17,0,0,0,0,0,109,0,0,0,0,0,0,22,305],"id":"call_00_e0MSVSocL0o4UWjOdG4c2072","name":"pwsh","args":["","{","\"","command","\"",": ","\"","[","Console","]","::","Out",".Write","('","P","WS","H","_OK","')","\"","}"]}} +{"type":"assistant/chunk","seq":53,"time":1785678164124,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to run a PowerShell command and then reply with \"DONE\". Let me execute it."}}}} +{"type":"assistant/chunk","seq":54,"time":1785678164124,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_e0MSVSocL0o4UWjOdG4c2072","name":"pwsh","arguments":"{\"command\":\"[Console]::Out.Write('PWSH_OK')\"}"}}}} +{"type":"assistant/chunk","seq":55,"time":1785678164124,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":1255,"outputTokens":99,"cacheReadTokens":0,"reasoningTokens":22}}}} +{"type":"assistant/chunk","seq":56,"time":1785898456913,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}} +{"type":"assistant/message","seq":57,"time":1785898456913,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to run a PowerShell command and then reply with \"DONE\". Let me execute it."},{"type":"tool-call","id":"call_00_e0MSVSocL0o4UWjOdG4c2072","name":"pwsh","arguments":"{\"command\":\"[Console]::Out.Write('PWSH_OK')\"}"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-pro"},"id":"82945de6-83e2-4b93-b6d2-89d58921eacf"},"usage":{"inputTokens":1255,"outputTokens":99,"cacheReadTokens":0,"reasoningTokens":22}},"sourceEventSeqs":[8,9,10,11,12,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],"surfaceOp":"append"} +{"type":"tool/call","seq":58,"time":1785898456913,"data":{"turn":1,"step":1,"callId":"call_00_e0MSVSocL0o4UWjOdG4c2072","name":"pwsh","arguments":"{\"command\":\"[Console]::Out.Write('PWSH_OK')\"}"}} +{"type":"tool/result","seq":59,"time":1785898456933,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_00_e0MSVSocL0o4UWjOdG4c2072"},"content":[{"type":"tool-result","toolCallId":"call_00_e0MSVSocL0o4UWjOdG4c2072","content":[{"type":"text","text":"PWSH_OK"}],"isError":false}],"role":"user","id":"874a846b-54b7-45cc-b3cb-edb8f868e1c5"}},"sourceEventSeqs":[58],"surfaceOp":"append"} +{"type":"step/end","seq":60,"time":1785898456933,"data":{"turn":1,"step":1}} +{"type":"step/start","seq":61,"time":1785898456939,"data":{"turn":1,"step":2}} +{"type":"assistant/chunk","seq":62,"time":1785678165136,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}} +{"type":"reasoning-chunks","seq0":63,"time0":1785678165312,"data":{"turn":1,"step":2,"index":0,"dt":[44,56,0,0,42,0,0,0,48,0,0,0,48,0,0,60,0,0,0,0,39,0,0,0],"texts":["The"," command"," executed"," successfully"," and"," printed"," \"","P","WS","H","_OK","\"."," Now"," I"," need"," to"," reply"," with"," \"","D","ONE","\""," and"," stop","."]}} +{"type":"assistant/chunk","seq":88,"time":1785678165649,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}} +{"type":"assistant/chunk","seq":89,"time":1785678165649,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"D"}}} +{"type":"assistant/chunk","seq":90,"time":1785678165693,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}} +{"type":"assistant/chunk","seq":91,"time":1785678165693,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The command executed successfully and printed \"PWSH_OK\". Now I need to reply with \"DONE\" and stop."}}}} +{"type":"assistant/chunk","seq":92,"time":1785678165693,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}}} +{"type":"assistant/chunk","seq":93,"time":1785678165693,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":90,"outputTokens":28,"cacheReadTokens":1280,"reasoningTokens":25}}}} +{"type":"assistant/chunk","seq":94,"time":1785898456944,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}} +{"type":"assistant/message","seq":95,"time":1785898456944,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The command executed successfully and printed \"PWSH_OK\". Now I need to reply with \"DONE\" and stop."},{"type":"text","text":"DONE"}],"source":{"kind":"model","provider":"deepseek-official","model":"deepseek-v4-pro"},"id":"36aaf6a0-1556-42e4-aed3-626caa8f7aaf"},"usage":{"inputTokens":90,"outputTokens":28,"cacheReadTokens":1280,"reasoningTokens":25}},"sourceEventSeqs":[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],"surfaceOp":"append"} +{"type":"step/end","seq":96,"time":1785898456944,"data":{"turn":1,"step":2}} +{"type":"turn/end","seq":97,"time":1785898456944,"data":{"turn":1,"reason":{"kind":"completed"}}} diff --git a/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/stdout.expected.jsonl b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/stdout.expected.jsonl new file mode 100644 index 0000000000..82ae8907ca --- /dev/null +++ b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/stdout.expected.jsonl @@ -0,0 +1,4 @@ +{"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":"DONE"}}}} +{"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}} diff --git a/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/system-prompt.expected.md b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/system-prompt.expected.md new file mode 100644 index 0000000000..229b3a6f6c --- /dev/null +++ b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/system-prompt.expected.md @@ -0,0 +1,3 @@ +You are an AI agent powered by DeepSeek Harness. + +You are a concise snapshot agent working in {{cwd}}. diff --git a/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/tool-schemas.expected.json b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/tool-schemas.expected.json new file mode 100644 index 0000000000..20f5a3e55c --- /dev/null +++ b/examples/acp-agent/tests/snapshots/persistent-pwsh-tool-turn/tool-schemas.expected.json @@ -0,0 +1,21 @@ +{ + "initial": [ + { + "name": "pwsh", + "description": "Run commands in a persistent PowerShell shell. State, including the current directory and exported environment variables, persists across calls for this agent.", + "parameters": { + "type": "object", + "properties": { + "command": { + "type": "string", + "description": "The PowerShell command to run. Relative path is preferred in the command." + } + }, + "required": [ + "command" + ] + } + } + ], + "changes": [] +} diff --git a/examples/package.json b/examples/package.json index cd348d5fe2..8b1d8875ba 100644 --- a/examples/package.json +++ b/examples/package.json @@ -95,6 +95,7 @@ "@deepseek-ai/dsh-tool-lsp": "workspace:*", "@deepseek-ai/dsh-tool-terminal": "workspace:*", "@deepseek-ai/dsh-tool-pwsh": "workspace:*", + "@deepseek-ai/dsh-tool-pwsh-persistent": "workspace:*", "@deepseek-ai/dsh-tool-ralph": "workspace:*", "@deepseek-ai/dsh-tool-session-query": "workspace:*", "@deepseek-ai/dsh-tool-skill": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d1d23dd996..465df6b10a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -688,6 +688,9 @@ importers: '@deepseek-ai/dsh-tool-pwsh': specifier: workspace:* version: link:../packages/shell/tool-pwsh + '@deepseek-ai/dsh-tool-pwsh-persistent': + specifier: workspace:* + version: link:../packages/shell/tool-pwsh-persistent '@deepseek-ai/dsh-tool-ralph': specifier: workspace:* version: link:../packages/workflow/tool-ralph