Przeglądaj źródła

test(acp): remove vacuous policy inheritance scenario

The ACP fixture configured read-only as the deployment default for both parent and child. Its delegated write therefore remained denied even with inheritance disabled, so the scenario could not fail on the regression it claimed to protect.

Delete the overlay, scenario registration, sessions, prompt, and 473-line tool-schema sidecar. The Loader-booted headless snapshot remains the real composition guard: only its parent carries read-only while the deployment default is workspace-write, so removing inheritance makes the child write reach disk and fails the test.

Keeping one discriminating snapshot avoids 662 lines of duplicated fixture data and makes the review evidence correspond to the actual security boundary.
Tianyi Cui 2 miesięcy temu
rodzic
commit
a70f1a2b7a

+ 0 - 45
examples/acp-agent/subagent-inheritance.cordis.snapshot.yml

@@ -1,45 +0,0 @@
-# Keyless replay counterpart of subagent-inheritance.cordis.yml: the same
-# flash pin plus the standard replay swaps (disable the key-requiring adapter,
-# passthrough sandbox runner, insert llm-replay). Patches do not compose
-# across nested includes, so everything applies together over the live tree.
-- id: base
-  name: '@cordisjs/plugin-include'
-  config:
-    path: ./cordis.yml
-    patches:
-      - id: llm-deepseek
-        name: '@deepseek-ai/dsh-llm-deepseek'
-        disabled: true
-      - id: sandbox
-        name: '@deepseek-ai/dsh-sandbox-local'
-        config:
-          runnerCommand:
-            - bash
-            - -c
-            - while [ "$1" != "--" ]; do shift; done; shift; exec "$@"
-            - passthrough-runner
-          runnerFailureSignatures:
-            - 'passthrough-runner: profile rejected'
-      - id: acp-agent
-        name: '@deepseek-ai/dsh-acp-demo'
-        config:
-          provider: deepseek
-          model: deepseek-v4-flash
-          persistenceRoot: !!js process.env.DSH_SNAPSHOT_SESSIONS_ROOT ?? './.sessions'
-          persistenceCompression: none
-          workspaceContext:
-            maxBytes: 65536
-          persona: |
-            You are a coding assistant powered by the {{model}} model. Your working directory is {{cwd}}. Your bash tool runs under a file sandbox — a `[sandbox: file access denied …]` result is policy, not a command bug.
-
-            Verify your work by running the code or tests. Keep answers brief and factual.
-      - insert:
-          - id: llm-replay
-            name: '@deepseek-ai/dsh-llm-replay'
-            config:
-              providers:
-                - id: deepseek
-                  name: DeepSeek
-                  models:
-                    - id: deepseek-v4-flash
-                    - id: deepseek-v4-pro

+ 0 - 25
examples/acp-agent/subagent-inheritance.cordis.yml

@@ -1,25 +0,0 @@
-# Subagent-under-confinement snapshot overlay: pin the recorded model to
-# deepseek-v4-flash so this scenario's request headers match the recorded
-# sandbox-class corpus (cordis.yml ships deepseek-v4-pro for live use). The
-# read-only policy itself comes from the scenario's DSH_PERMISSION_MODE env —
-# the automation protocol has no session-scoped picker, so deployment policy
-# is the lever ([downgrade rationale in the scenario table]). A config patch
-# replaces the whole target config, so base fields are restated verbatim.
-- id: base
-  name: '@cordisjs/plugin-include'
-  config:
-    path: ./cordis.yml
-    patches:
-      - id: acp-agent
-        name: '@deepseek-ai/dsh-acp-demo'
-        config:
-          provider: deepseek
-          model: deepseek-v4-flash
-          persistenceRoot: !!js process.env.DSH_SNAPSHOT_SESSIONS_ROOT ?? './.sessions'
-          persistenceCompression: !!js "process.env.DSH_SNAPSHOT === undefined ? 'zstd' : 'none'"
-          workspaceContext:
-            maxBytes: 65536
-          persona: |
-            You are a coding assistant powered by the {{model}} model. Your working directory is {{cwd}}. Your bash tool runs under a file sandbox — a `[sandbox: file access denied …]` result is policy, not a command bug.
-
-            Verify your work by running the code or tests. Keep answers brief and factual.

+ 0 - 17
examples/acp-agent/tests/acp.snapshot.ts

@@ -38,7 +38,6 @@ const SESSION_QUERY_CONFIG = fileURLToPath(new URL('../session-query.cordis.yml'
 const PTY_CONFIG = fileURLToPath(new URL('../pty.cordis.yml', import.meta.url))
 const DEPTH_TWO_CONFIG = fileURLToPath(new URL('../depth-two.cordis.yml', import.meta.url))
 const SESSION_SANDBOX_ROOT_CONFIG = fileURLToPath(new URL('../session-sandbox-root.cordis.yml', import.meta.url))
-const SUBAGENT_INHERITANCE_CONFIG = fileURLToPath(new URL('../subagent-inheritance.cordis.yml', import.meta.url))
 const RETRY_CONFIG = fileURLToPath(new URL('../retry.cordis.yml', import.meta.url))
 const SESSION_TITLE_CONFIG = fileURLToPath(new URL('../session-title.cordis.yml', import.meta.url))
 const LSP_CONFIG = fileURLToPath(new URL('./lsp.cordis.yml', import.meta.url))
@@ -264,22 +263,6 @@ const SCENARIOS: Scenario[] = [
     headerClass: 'sandbox',
     env: { DSH_PERMISSION_MODE: 'workspace-write' },
   },
-  // Delegation under a read-only DEPLOYMENT policy: the child's real write is
-  // denied by the fs fence and its denial report surfaces in the parent
-  // transcript. This is the strength the automation protocol can express — no
-  // session-scoped picker exists to tighten only the parent, so the
-  // parent-override-vs-wider-default half of the inheritance contract stays
-  // pinned by the real-wall unit suite (the subagent policy-inheritance Agent
-  // Note records the downgrade).
-  {
-    name: 'subagent-sandbox-inheritance',
-    hasModelTurn: true,
-    recorded: true,
-    pinsHeader: true,
-    headerClass: 'subagent-inheritance',
-    configPath: SUBAGENT_INHERITANCE_CONFIG,
-    env: { DSH_PERMISSION_MODE: 'read-only' },
-  },
   // Unlike ordinary snapshots, this session cwd is outside the platform temp
   // roots that workspace-write always grants. The overlay points the
   // deployment fallback at /tmp, so a successful relative write proves the

+ 0 - 14
examples/acp-agent/tests/snapshots/subagent-sandbox-inheritance/input.json

@@ -1,14 +0,0 @@
-{
-  "steps": [
-    {
-      "op": "initialize"
-    },
-    {
-      "op": "newSession"
-    },
-    {
-      "op": "prompt",
-      "text": "Use the subagent tool exactly once to delegate this subtask to a child agent: 'Use the write tool exactly once with file_path set to exactly the relative path inherited.txt (never an absolute path) and content escaped. If the write is denied, do not retry and do not request escalation; reply with the single word CHILD_DENIED and the exact denial marker line from the tool result. If it succeeds, reply with the single word CHILD_WROTE.' After the subagent returns, state in one short sentence whether the child was denied by the sandbox, quoting the denial marker if there is one, then reply with the single word PARENT_DONE and stop. Do not use the bash or write tools yourself and do not request escalation."
-    }
-  ]
-}

+ 0 - 30
examples/acp-agent/tests/snapshots/subagent-sandbox-inheritance/session.1.jsonl

@@ -1,30 +0,0 @@
-{"type":"session","version":0,"id":"7fcdaf99-35c9-4ad6-a872-fc04fbfe4ee6","createdAt":1784961244926,"cwd":"/var/folders/2g/b32ct0qn1d728l_v6tdkjytr0000gn/T/acp-snap-cwd-XKPvGB","parentSession":"03d6a514-045b-4f61-9a8f-1b5165c3a648","delegationDepth":1}
-{"type":"turn/start","seq":0,"time":1784961244928,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
-{"type":"user/message","seq":1,"time":1784961244928,"data":{"content":[{"type":"text","text":"You have access to the write tool. Use the write tool exactly once with file_path set to exactly the relative path \"inherited.txt\" (never an absolute path) and content set to \"Child agent wrote this file.\" (escaped as needed). \n\nIf the write is denied by the sandbox (look for \"[sandbox: file access denied\" in the result), do NOT retry and do NOT request escalation. Reply with the single word CHILD_DENIED followed by a space and then the exact denial marker line from the tool result.\n\nIf the write succeeds, reply with the single word CHILD_WROTE.\n\nDo not use any other tools or do anything else."}],"source":{"kind":"user"},"role":"user","id":"794cb9e6-5770-40bf-a0c6-69da4a71fe01"},"surfaceOp":"append"}
-{"type":"session/title","seq":2,"time":1784961244929,"data":{"title":"You have access to the","messageSeqs":[1],"source":{"kind":"fallback"}}}
-{"type":"step/start","seq":3,"time":1784961244932,"data":{"turn":1,"step":1}}
-{"type":"request/header","seq":4,"time":1784961244933,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
-{"type":"assistant/chunk","seq":5,"time":1784961245908,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
-{"type":"reasoning-chunks","seq0":6,"time0":1784961245909,"data":{"turn":1,"step":1,"index":0,"dt":[145,8,0,1,0,46,1,0,0,16,1,0,30,1,32,1,0,0,0,1,29,0,1,34,0,1,0,30,1,30,0,0,32,33,1,0,0,1,0,28,33,1,0,0,33,0,1,0,0,31,0,1,0,0,30,0],"texts":["The"," user"," wants"," me"," to"," use"," the"," write"," tool"," exactly"," once"," with"," file","_path","=\"","inher","ited",".txt","\""," and"," content","=\"","Child"," agent"," wrote"," this"," file",".\"."," If"," denied",","," I"," should"," reply"," with"," CH","ILD","_D","EN","IED"," followed"," by"," the"," denial"," marker","."," If"," successful",","," reply"," with"," CH","ILD","_W","RO","TE","."]}}
-{"type":"assistant/chunk","seq":63,"time":1784961246638,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
-{"type":"tool-call-chunks","seq0":64,"time0":1784961246639,"data":{"turn":1,"step":1,"index":1,"dt":[1,0,31,1,0,0,31,1,0,0,1,63,1,0,0,1,32,1,0,0,0,1,32,0,1],"id":"call_00_7iJutQqZ95RcVbXUefTC5135","name":"write","args":["","{","\"","file","_path","\"",": ","\"","inher","ited",".txt","\"",", ","\"","content","\"",": ","\"","Child"," agent"," wrote"," this"," file",".","\"","}"]}}
-{"type":"assistant/chunk","seq":90,"time":1784961246903,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to use the write tool exactly once with file_path=\"inherited.txt\" and content=\"Child agent wrote this file.\". If denied, I should reply with CHILD_DENIED followed by the denial marker. If successful, reply with CHILD_WROTE."}}}}
-{"type":"assistant/chunk","seq":91,"time":1784961246903,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_7iJutQqZ95RcVbXUefTC5135","name":"write","arguments":"{\"file_path\": \"inherited.txt\", \"content\": \"Child agent wrote this file.\"}"}}}}
-{"type":"assistant/chunk","seq":92,"time":1784961246903,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":5377,"outputTokens":123,"cacheReadTokens":0,"reasoningTokens":57}}}}
-{"type":"assistant/chunk","seq":93,"time":1784961246904,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
-{"type":"assistant/message","seq":94,"time":1784961246905,"data":{"turn":1,"step":1,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The user wants me to use the write tool exactly once with file_path=\"inherited.txt\" and content=\"Child agent wrote this file.\". If denied, I should reply with CHILD_DENIED followed by the denial marker. If successful, reply with CHILD_WROTE."},{"type":"tool-call","id":"call_00_7iJutQqZ95RcVbXUefTC5135","name":"write","arguments":"{\"file_path\": \"inherited.txt\", \"content\": \"Child agent wrote this file.\"}"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"aacd17fa-e534-4a19-8e8a-db123fc0b7b1"},"usage":{"inputTokens":5377,"outputTokens":123,"cacheReadTokens":0,"reasoningTokens":57}},"sourceEventSeqs":[5,6,7,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,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],"surfaceOp":"append"}
-{"type":"tool/call","seq":95,"time":1784961246905,"data":{"turn":1,"step":1,"callId":"call_00_7iJutQqZ95RcVbXUefTC5135","name":"write","arguments":"{\"file_path\": \"inherited.txt\", \"content\": \"Child agent wrote this file.\"}"}}
-{"type":"tool/result","seq":96,"time":1784961246918,"data":{"turn":1,"step":1,"message":{"source":{"kind":"tool","callId":"call_00_7iJutQqZ95RcVbXUefTC5135"},"content":[{"type":"tool-result","toolCallId":"call_00_7iJutQqZ95RcVbXUefTC5135","content":[{"type":"text","text":"Error: [sandbox: file access denied under read-only mode]\n[sandbox: escalation available — retry this exact operation once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]"}],"isError":true}],"role":"user","id":"2c6046db-7862-4010-b614-4fd09850bedb"},"error":{"name":"FsError","code":"FS_SANDBOX_DENIED"}},"sourceEventSeqs":[95],"surfaceOp":"append"}
-{"type":"step/end","seq":97,"time":1784961246924,"data":{"turn":1,"step":1}}
-{"type":"step/start","seq":98,"time":1784961246925,"data":{"turn":1,"step":2}}
-{"type":"assistant/chunk","seq":99,"time":1784961247934,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
-{"type":"reasoning-chunks","seq0":100,"time0":1784961247934,"data":{"turn":1,"step":2,"index":0,"dt":[119,43,2,1,0,20,0,0,1,0,33,1,0,0,0,0,36,0,0,1,32,2,1,0,0,0,25,0,0,0,1,36,1,0,0,0,25,1,0,0,0,0,31,1,0,0,0,34,1,0,1,0,0,33,0,0,1,32,1,0,0,0,32,0,1,0,0,35,0,0,0,0,1,32,1,0,0,0,1,32,38,0,0,1,0,29,1,0,0,1,0,30,0,0,1,0,32,0,0,1,36,1,28,1,0,0,0,1,34,1,0,0,0,0,29,1,0,0,0],"texts":["The"," write"," was"," denied"," by"," the"," sand","box"," under"," read","-only"," mode","."," According"," to"," the"," instructions",":"," \"","If"," the"," write"," is"," denied"," by"," the"," sand","box"," (","look"," for"," \"[","sand","box",":"," file"," access"," denied","\""," in"," the"," result","),"," do"," NOT"," ret","ry"," and"," do"," NOT"," request"," escalation","."," Reply"," with"," the"," single"," word"," CH","ILD","_D","EN","IED"," followed"," by"," a"," space"," and"," then"," the"," exact"," denial"," marker"," line"," from"," the"," tool"," result",".\"\n\n","The"," denial"," marker"," line"," is",":"," \"[","sand","box",":"," file"," access"," denied"," under"," read","-only"," mode","]","\"\n\n","So"," I"," should"," reply",":"," CH","ILD","_D","EN","IED"," [","sand","box",":"," file"," access"," denied"," under"," read","-only"," mode","]"]}}
-{"type":"assistant/chunk","seq":220,"time":1784961248883,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
-{"type":"text-chunks","seq0":221,"time0":1784961248884,"data":{"turn":1,"step":2,"index":1,"dt":[0,0,0,0,0,30,1,0,0,0,0,35,0,0,0,1],"texts":["CH","ILD","_D","EN","IED"," [","sand","box",":"," file"," access"," denied"," under"," read","-only"," mode","]"]}}
-{"type":"assistant/chunk","seq":238,"time":1784961248951,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The write was denied by the sandbox under read-only mode. According to the instructions: \"If the write is denied by the sandbox (look for \"[sandbox: file access denied\" in the result), do NOT retry and do NOT request escalation. Reply with the single word CHILD_DENIED followed by a space and then the exact denial marker line from the tool result.\"\n\nThe denial marker line is: \"[sandbox: file access denied under read-only mode]\"\n\nSo I should reply: CHILD_DENIED [sandbox: file access denied under read-only mode]"}}}}
-{"type":"assistant/chunk","seq":239,"time":1784961248951,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}}}}
-{"type":"assistant/chunk","seq":240,"time":1784961248951,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":188,"outputTokens":138,"cacheReadTokens":5376,"reasoningTokens":120}}}}
-{"type":"assistant/chunk","seq":241,"time":1784961248951,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
-{"type":"assistant/message","seq":242,"time":1784961248952,"data":{"turn":1,"step":2,"message":{"role":"assistant","content":[{"type":"reasoning","text":"The write was denied by the sandbox under read-only mode. According to the instructions: \"If the write is denied by the sandbox (look for \"[sandbox: file access denied\" in the result), do NOT retry and do NOT request escalation. Reply with the single word CHILD_DENIED followed by a space and then the exact denial marker line from the tool result.\"\n\nThe denial marker line is: \"[sandbox: file access denied under read-only mode]\"\n\nSo I should reply: CHILD_DENIED [sandbox: file access denied under read-only mode]"},{"type":"text","text":"CHILD_DENIED [sandbox: file access denied under read-only mode]"}],"source":{"kind":"model","provider":"deepseek","model":"deepseek-v4-flash"},"id":"7fb28534-4a44-435d-8d15-403f7ee1bebd"},"usage":{"inputTokens":188,"outputTokens":138,"cacheReadTokens":5376,"reasoningTokens":120}},"sourceEventSeqs":[99,100,101,102,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,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241],"surfaceOp":"append"}
-{"type":"step/end","seq":243,"time":1784961248963,"data":{"turn":1,"step":2}}
-{"type":"turn/end","seq":244,"time":1784961248963,"data":{"turn":1,"reason":{"kind":"completed"}}}

Plik diff jest za duży
+ 0 - 7
examples/acp-agent/tests/snapshots/subagent-sandbox-inheritance/session.jsonl


+ 0 - 4
examples/acp-agent/tests/snapshots/subagent-sandbox-inheritance/stdout.expected.jsonl

@@ -1,4 +0,0 @@
-{"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":"The child was denied by the sandbox – denial marker: `[sandbox: file access denied under read-only mode]`.\n\nPARENT_DONE"}}}}
-{"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}}

+ 0 - 24
examples/acp-agent/tests/snapshots/subagent-sandbox-inheritance/system-prompt.expected.md

@@ -1,24 +0,0 @@
-You are an AI agent powered by the DeepSeek Harness SDK.
-
-You are a coding assistant powered by the deepseek-v4-flash model. Your working directory is {{cwd}}. Your bash tool runs under a file sandbox — a `[sandbox: file access denied …]` result is policy, not a command bug.
-
-Verify your work by running the code or tests. Keep answers brief and factual.
-
-
-Use the read tool — not shell commands like cat — to inspect text files. Results include line numbers. Use offset and limit to continue reading large files.
-
-Use the write tool to create files or completely replace file contents. Existing files are overwritten, so read an existing file first (the default fs-policy requires it) and prefer edit for targeted changes.
-
-Use the edit tool for targeted changes to existing UTF-8 text files. It replaces literal old_string with new_string; by default old_string must appear exactly once. If old_string appears multiple times, provide a more specific old_string or set replace_all to true. Read the file first (the default fs-policy requires it), unless you just created or edited it in this session.
-
-Check the [exit code: N] marker on every bash result; investigate failures before moving on.
-
-Track every background task id you start. You are notified in-session when a task finishes — do not busy-poll or sleep on one; keep working on independent steps and do not duplicate a running task's work. Before giving a final answer, collect every still-relevant task with task_output (set wait: true only when you are genuinely blocked on it), and task_kill tasks that stopped mattering.
-
-Use goal tools for one long-running completion objective in the current session. create_goal may infer goal intent from a direct human request in any language; do not create a goal for routine single-turn work. Call get_goal before update_goal and copy its exact goal_id and revision. After session resume or fork, an active goal is disarmed: when a human asks to continue or resume in any wording or language, use update_goal action resume to rearm it. Mark complete only when the objective is actually achieved. Mark blocked only after the same blocking condition persists for at least 3 consecutive goal rounds, and report that concrete condition in blocked_reason; difficulty, uncertainty, or useful remaining work is not blocked.
-
-<!-- dsh-user-approval-policy:ask -->
-
-Use the workflow tool ONLY when the user explicitly asks for a workflow or for large multi-agent orchestration: you write a JavaScript script (the tool description documents the exact format) that fans work out across many subagents with phases and structured results. For one or two delegations, prefer plain subagent calls.
-
-Use the ralph tool ONLY when the direct human explicitly asks for a Ralph loop or fresh-agent iterative execution. Each Ralph round starts a fresh child with no conversation seed and uses the shared workspace as durable memory. Completion and blockers are worker reports, not independent evaluation. Use same-session goal tools for ordinary long-running objectives, and plain subagents or workflows for bounded delegation and fan-out.

Plik diff jest za duży
+ 0 - 365
examples/acp-agent/tests/snapshots/subagent-sandbox-inheritance/tool-schemas.expected.json


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików