Merge origin/master into worktree/contain-result-observer-rejections
This commit is contained in:
@@ -10,7 +10,7 @@ The harness needs one internal language for messages that the loop, session log,
|
||||
|
||||
Own the vocabulary: messages are arrays of typed content blocks (`text`, `reasoning`, `tool-call`, `tool-result`), with the union derived from the merge-extensible `ContentBlockMap` so plugins add block types via declaration merging. The same merge-extensible-map pattern types every "stringly" field (`MessageSource`, `FinishReason`, `TurnTrigger`, `TurnEndReason`). Streaming is a raw chunk protocol; `BlockAssembler` is the single shared assembly implementation. Adapters translate to provider wire formats — mapping cost lives in adapters, where it belongs.
|
||||
|
||||
In-session context injection (`context/message`, `steering/message`) renders as tagged user-role envelopes (the system-reminder pattern) rather than a new role, so adapters carry zero burden. Live-adapter validation confirms this rendering for current DeepSeek behavior; a future provider-specific mismatch belongs in that adapter rather than a new canonical role.
|
||||
In-session context injection (`context/message`) and mid-turn steering (`steering/message`) originally rendered as tagged user-role envelopes (the system-reminder pattern) rather than a new role, so adapters carry zero burden. Both now project as plain user content with no wrapper; see [the injected-content-envelope Agent Note](../simplification/2026-07-20-unwrap-injected-content-envelopes.md). Live-adapter validation confirms this rendering for current DeepSeek behavior; a future provider-specific mismatch belongs in that adapter rather than a new canonical role.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ After a successful first-party `read`, `write`, or `edit` call, the `tools/post-
|
||||
|
||||
A content edit appends `Updated instructions from: <path>`, states that the new content replaces the previous content, and includes the complete current file. If precedence changes from one candidate to another, the message also names the previous path and says it no longer applies. If no candidate remains, the plugin appends `Instructions removed: <path>` and states that the previously loaded instructions no longer apply.
|
||||
|
||||
Dynamic messages use a raw `context/message` envelope because the plugin owns the complete system-reminder framing. Core context injection therefore supports `envelope: 'raw'`; callers that omit it retain the canonical `<context source="...">` wrapper. `context/message.meta` carries opaque JSON state that is persisted but never rendered to the model.
|
||||
Dynamic messages carry their complete system-reminder framing in `content`, and every `context/message` reaches the model verbatim as a user-role message (there is no core wrapper to opt out of). `context/message.meta` carries opaque JSON state that is persisted but never rendered to the model.
|
||||
|
||||
Shell commands are not discovery triggers. Local bash calls start fresh shells, and inferring reached paths from arbitrary command strings would require shell semantics the prompt plugin does not own.
|
||||
|
||||
@@ -76,7 +76,7 @@ There is intentionally no watcher. Detection occurs at the next successful struc
|
||||
|
||||
## Consequences
|
||||
|
||||
Workspace guidance is isolated per session and shared by both product front doors and every tool presentation mode. Initial instructions benefit from stable prefix caching, while nested and changed content remains durable and replayable. The generic session/agent context contract includes optional raw framing and JSON metadata, both propagated through prompt-submit and post-tool `additionalContexts` arrays without flattening entries.
|
||||
Workspace guidance is isolated per session and shared by both product front doors and every tool presentation mode. Initial instructions benefit from stable prefix caching, while nested and changed content remains durable and replayable. The generic session/agent context contract carries JSON metadata propagated through prompt-submit and post-tool `additionalContexts` arrays without flattening entries.
|
||||
|
||||
Repository text remains untrusted input. Lower-authority user-role framing, explicit precedence language, delimiter escaping, and symlink rejection reduce risk but do not eliminate prompt injection. Permission and sandbox layers treat workspace files as data rather than authority.
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ The canonical surface separates transformable policy, around-dispatch control, a
|
||||
- `agent/session-start(agent, source)` — emit, once before turn 1, carrying a `SessionStartSource` (`startup` for a fresh/forked create, `resume` for a reloaded persisted session; `clear`/`compact` reserved). A pure notification — it CANNOT block startup (a deliberate gap: a bridge logs/injects, it does not gate startup). A listener seeds context via `agent.inject()`.
|
||||
- `agent/prompt-submit(agent, content, source, next) → PromptDecision` — waterfall, fired per drained queued message inside the open turn, before the `user/message` append. `allow` (optionally rewriting the prompt `content` or attaching separately sourced `additionalContexts[]`) or `block` (dropping the prompt; the loop appends a durable `prompt/blocked` in its place — see the dispatch note below).
|
||||
|
||||
**`agent/turn-continuation`** receives and returns a `ContinuationDecision`. A `{action:'continue', reason?}` may carry model-facing content and source recorded as next-step steering in the same turn — the typed twin of the `/goal` step-end-steer pattern. It is not a `context/message`, so its type does not offer a context envelope or durable context metadata.
|
||||
**`agent/turn-continuation`** receives and returns a `ContinuationDecision`. A `{action:'continue', reason?}` may carry model-facing content and source recorded as next-step steering in the same turn — the typed twin of the `/goal` step-end-steer pattern. It is not a `context/message`, so its type does not offer durable context metadata.
|
||||
|
||||
### The tool pipeline gives each phase one kind of authority
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ The default is composition config (`cordis.yml`) — operator-owned, process-wid
|
||||
|
||||
```ts
|
||||
interface SessionEventMap {
|
||||
'bash/sandbox-mode': { mode: 'read-only' | 'workspace-write' | 'danger-full-access' }
|
||||
'sandbox/mode': { mode: 'read-only' | 'workspace-write' | 'danger-full-access' }
|
||||
'approval/policy': { policy: 'ask' | 'never' }
|
||||
}
|
||||
```
|
||||
@@ -111,9 +111,7 @@ Sandbox mode is not narrated in the prompt; denial results report the mode when
|
||||
|
||||
#### In-process tools
|
||||
|
||||
fs/web/todo execute in-process, so their sandbox semantics are policy at their seams: the fs intent gates deciding by the shared mode vocabulary (§ Deferred phases, cross-family) make `read-only` a real boundary instead of a bash-only approximation — until then the contract says so honestly. No generic per-tool sandbox runtime: a host-mediated tool leaves the process only by returning declarative effects the host validates, which is a rewrite, not a wrapper.
|
||||
|
||||
FIXME: Revisit this tool-local boundary. The follow-up design needs to determine whether sandboxing becomes a global harness capability that applies uniformly to every tool, instead of expressing in-process enforcement independently at each tool seam.
|
||||
fs/web/todo execute in-process, so their sandbox semantics are policy at their seams. The fs seam now enforces the shared mode vocabulary through a sandboxed provider (`dsh-fs-sandbox` fences write/edit by mode; see [the cross-family fs sandbox RFC](2026-07-14-cross-family-fs-sandbox.md)), so `read-only`/`workspace-write` are real boundaries for the filesystem tools, not a bash-only approximation. web/todo remain unfenced (web's only effect is network, outside the file-effect mode vocabulary). No generic per-tool sandbox runtime: a host-mediated tool leaves the process only by returning declarative effects the host validates, which is a rewrite, not a wrapper — the follow-up settled on one shared policy home (`ctx.sandboxPolicy`) with per-seam enforcement, not a uniform wrapper.
|
||||
|
||||
### Testing
|
||||
|
||||
@@ -126,8 +124,7 @@ FIXME: Revisit this tool-local boundary. The follow-up design needs to determine
|
||||
|
||||
Each phase gets its full design when picked up, validated against the code at that time, and lands with unit, real-API e2e, and snapshot coverage at the tiers it touches.
|
||||
|
||||
- **Per-session workspace root** — the executor's write boundary stays config-fixed for its lifetime while each ACP session has its own cwd; a per-session root rides the same per-call policy carrier once designed.
|
||||
- **Cross-family boundary** — the fs intent gates decide by the shared mode, making `read-only`/`workspace-write` real boundaries beyond bash.
|
||||
- **Per-session workspace root** — the executor's write boundary stays config-fixed for its lifetime while each ACP session has its own cwd; a per-session root rides the same per-call policy carrier once designed. Centralizing the root on `ctx.sandboxPolicy` (the [cross-family fs sandbox RFC](2026-07-14-cross-family-fs-sandbox.md)) is the groundwork.
|
||||
- **Second consumer** — `subagent-acp` optionally confines child agents (per-call policy; unconfined default — a child agent must write its own persistence).
|
||||
- **More environments** — an environment-coherent capability group example (e.g. bash+fs against one container).
|
||||
- **Windows chain** — `PLATFORM_CHAINS.win32` is reserved and empty (fail-closed); filling it means a confinement runner from the AppContainer/restricted-token family, shipped from its own repository on the `node-addon-landlock-run` template, plus its profile dialect and denial/runner-failure signatures.
|
||||
@@ -171,7 +168,7 @@ What shipped pins — the tiers in Testing hold each:
|
||||
Costs and accepted limits:
|
||||
|
||||
- **The one-wrapper illusion is given up knowingly.** A `tools/pre-execute` wrapper plus prompt conventions does not solve sandbox approval — the correct design costs structured denials, native runner probes, per-call policy carriage, and consistent cross-family enforcement, and this design pays it.
|
||||
- **`read-only` is not yet a cross-family boundary.** Until the fs intent gates decide by the shared mode, the claim holds for bash only; the contract says so honestly (§ In-process tools).
|
||||
- **`read-only` became a cross-family boundary through a follow-up.** This RFC shipped bash-only enforcement; the [cross-family fs sandbox RFC](2026-07-14-cross-family-fs-sandbox.md) extends the same mode vocabulary to the filesystem tools through a sandboxed `ctx.fs` provider and relocates the mode/root config and the `sandbox/mode` override to `ctx.sandboxPolicy` (§ In-process tools).
|
||||
- **Windows has no backend.** Its chain slot is reserved empty — fail-closed, never a fallthrough; filling it is a deferred phase.
|
||||
- **The Seatbelt rung leans on Apple's deprecated-but-shipped `sandbox-exec` CLI.** As darwin's sole candidate it is selected without probing, so a future removal surfaces at execution as the runner-failure classification — re-thrown `SANDBOX_UNAVAILABLE`, the command never runs; fail closed, never open.
|
||||
- **Landlock confinement is only as complete as the running kernel's ABI.** Reported as `enforcement: 'partial'` rather than refused — the deliberate trade that keeps the fallback available on older-kernel hosts.
|
||||
@@ -191,9 +188,9 @@ Costs and accepted limits:
|
||||
- **What happens on a platform with no backend — Windows today?** `confine()` throws the fail-closed `SANDBOX_UNAVAILABLE` and the command never spawns; `win32` is a reserved EMPTY chain, pinned by test to fail closed identically until a Windows runner fills it (§ Deferred phases).
|
||||
- **`bwrap` is installed on my host but unusable (disabled unprivileged userns, an LSM denying `mount`) — what happens?** The chain probe is functional — it builds and enforces a real profile rather than checking `--version` — so a present-but-unusable `bwrap` fails its probe, selection falls to the registry-installed Landlock launcher, and the verdict is cached for the provider's lifetime.
|
||||
- **Does the sandbox restrict network or process visibility?** No — `SandboxMode` claims FILE effects only; the bwrap profile deliberately does not unshare pid, and no backend claims network. Whether network restriction becomes its own knob is left open in § The seam.
|
||||
- **Which tools actually run confined?** OS subprocesses through `ctx.bash` — the bash tools, and hook commands transitively. fs/web/todo execute in-process, where an `execve` wrapper is mechanically meaningless; their `read-only` semantics arrive with the cross-family deferred phase, and until then the contract says bash-only honestly.
|
||||
- **Which tools actually run confined?** OS subprocesses through `ctx.bash` — the bash tools, and hook commands transitively — plus the filesystem tools (`read`/`write`/`edit`) through the sandboxed `ctx.fs` provider (the [cross-family fs sandbox RFC](2026-07-14-cross-family-fs-sandbox.md)): bash confines via the OS runner, fs via an in-process path fence, both keying off the same `ctx.sandboxPolicy` mode. web/todo stay in-process and unfenced (web's only effect is network, outside the file-effect mode vocabulary).
|
||||
- **Does a granted escalation persist?** No. The grant is consumed by the exact foreground or background call that asked; every neighboring call keeps its own effective mode. A later background denial surfaces through `task_output` and may ground a new exact-command retry.
|
||||
- **When does an editor's mode switch take effect?** Mid-turn: appended immediately, honored by the very next call's stamp. Idle: held on the bridge's session record, anchored at the next turn's `agent/prompt-submit`, with N flips coalescing to at most one event (none if net-zero); a crash before anchoring reverts it and `session/load` reports the truth. The model is not told — its next command simply behaves under the new mode.
|
||||
- **When does an editor's mode switch take effect?** Mid-turn: appended immediately, honored by the very next call's stamp. Idle: held on the bridge's session record, anchored at the next `agent/prompt-submit` inside its open turn, with N flips coalescing to at most one event (none if net-zero); a crash before anchoring reverts it and `session/load` reports the truth. The model is not told — its next command simply behaves under the new mode.
|
||||
- **What survives a restart — and what if the operator changed the config default while the process was down?** Overrides replay from the session log (`effective = fold ?? config`), so a resumed session keeps its modes with zero catch-up machinery; a default that drifted offline changes behavior the same way a switch does (the approval policy, being stated, is additionally narrated with operator/config attribution).
|
||||
- **What does `enforcement: 'partial'` on a result mean?** The selected backend enforces the subset its kernel ABI governs — e.g. Landlock before ABI v3 does not govern path truncate — and says so structurally instead of refusing the host; the probe's report line distinguishes the cases. The bwrap and Seatbelt profiles govern every promised file effect by construction, so they always report `full`.
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# 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
|
||||
2026-07-14-cross-family-fs-sandbox.md: 9b6312e5994469606bd1645902fc798f70258580
|
||||
2026-07-14-cross-family-fs-sandbox.zh.md: d4816e03d94bdf12b2db875d71dccb7db3a2c0d7
|
||||
@@ -0,0 +1,94 @@
|
||||
# Agent Note: Cross-family file sandbox — one policy home, a sandboxed fs provider, and fs escalation parity
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-14-cross-family-fs-sandbox.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
`SandboxMode` claims file effects, but originally only `ctx.bash` enforced it. The fs tools (`write`/`edit`) mutate the host filesystem in-process through `ctx.fs`, where an OS argv wrapper is mechanically meaningless — [the sandbox Agent Note](2026-07-06-sandbox.md) § In-process tools records this and left cross-family enforcement as a deferred phase with an open question: whether in-process enforcement stays per-seam or becomes a uniform harness capability. This Agent Note is that phase, and answers it: one shared policy home, per-seam enforcement at each family's correct altitude.
|
||||
|
||||
The gap was not read-only-shaped. A confined coding agent's product mode is `workspace-write`: bash may already write under the workspace root while everything outside is denied, so an fs enforcement that could only deny-all would be strictly worse than disabling the fs tools — the model would attempt an in-workspace `write`, be denied, and learn to detour through `bash` heredocs. Cross-family enforcement therefore speaks the full mode ladder, including the path-containment judgment `workspace-write` requires (canonical targets; `..`/symlink/absolute-path escapes) and the same escalation lever bash carries.
|
||||
|
||||
A second enforcing family also exposed an ownership problem in the original layout. The deployment default (`mode` + `workspaceRoot`) was configured on `dsh-bash-sandbox`, and the per-session override event was `bash/sandbox-mode`, folded and written by `dsh-bash`'s session-mode kit. With fs enforcing the same policy, either fs reads bash's config and events (a capability family depending on a sibling's plugin config) or each family carries its own copy — and two copies of `workspaceRoot` drift into exactly the split world the sandbox RFC warns about: bash confined to one root while fs fences another.
|
||||
|
||||
## Decision
|
||||
|
||||
Three coordinated pieces, all composed from the leaf `cordis.yml`, none touching `agent-loop`.
|
||||
|
||||
### `ctx.sandboxPolicy` — one home for mode and workspace root
|
||||
|
||||
`packages/sandbox/sandbox-policy/` (`@deepseek-ai/dsh-sandbox-policy`) registers `ctx.sandboxPolicy`, the single owner of the deployment's sandbox policy:
|
||||
|
||||
- `Config`: `mode` (the closed `SandboxMode` union, default `read-only`) and `workspaceRoot` (default the process cwd, resolved absolute). Misconfiguration fails loud at load.
|
||||
- The per-session override event `sandbox/mode`, with its pure fold (`effectiveSandboxMode(events)`), its write path (`setSandboxMode(session, mode)`), and `SANDBOX_MODES`. The event is policy state — consumed by two families — so it lives here, not in either capability's seam. Its shape and log-only semantics match the `approval/*` precedent.
|
||||
- `defaultMode` / `workspaceRoot` accessors the enforcing implementations read for their resolve fallback and boundary.
|
||||
|
||||
`dsh-bash-sandbox` carries no sandbox config of its own — it injects `sandboxPolicy` and reads the default from it; its `resolve()` precedence is unchanged (escalation grant > per-call stamp > default). `dsh-tool-bash` and `dsh-tool-fs` fold the session's `sandbox/mode` with `effectiveSandboxMode` to stamp each call; `dsh-permission` presets and the ACP bridge write through the relocated setter. The seam that owns bash execution no longer depends on `dsh-session` at all — the session dependency moved to the policy package with the fold.
|
||||
|
||||
### `dsh-fs-sandbox` — enforcement inside the provider
|
||||
|
||||
`packages/fs/fs-sandbox/` (`@deepseek-ai/dsh-fs-sandbox`) mirrors the `bash-local`/`bash-sandbox` split: `SandboxedFileSystem extends LocalFileSystem`, registered as `ctx.fs`, injecting `sandboxPolicy`. Reads (`resolve`/`stat`/`readText`/`streamText`/`listDir`) pass through untouched — every mode permits reading. The two mutations enforce by mode before delegating to the inherited atomic write:
|
||||
|
||||
- `read-only` denies `writeText`/`editText` outright.
|
||||
- `workspace-write` fences the canonicalized target against the writable-root set — `writableRoots(policy)` in `dsh-sandbox`: the workspace root plus the platform temp areas (`/tmp`, `os.tmpdir()`), each realpathed — the SAME set the Seatbelt profile grants, so the fs fence is the fourth dialect of one mode meaning alongside the bwrap/Landlock/Seatbelt profiles, and "the write tool cannot write `/tmp` but bash can" asymmetries cannot arise. Containment is prefix-inclusion on real paths; the target is re-canonicalized (`resolve` realpaths the deepest existing ancestor) immediately before delegating, so an ancestor symlink swapped since the tool resolved it is caught.
|
||||
- `danger-full-access` delegates unfenced.
|
||||
|
||||
A denial is the structured `FS_SANDBOX_DENIED` carrying the effective mode — distinct from `FS_PERMISSION_DENIED` (a host EACCES is the world refusing; this is policy refusing). No text inference: an in-process fence knows exactly what it denied. The per-call carrier is a trailing optional `sandboxMode` on `writeText`/`editText` (the filesystem twin of `BashExecRequest.sandboxMode`); the seam stays session-free (the caller stamps, exactly as `resolve` takes a cwd), and the bare local backend carries-and-ignores it. `FileSystem.sandboxMode` is the capability fact (`undefined` on the base and `fs-local`, the default on `SandboxedFileSystem`), so the tool layer advertises escalation from composition truth.
|
||||
|
||||
The threat model is stated in the package README: a policy fence in trusted code over model-controlled paths, not a kernel boundary — the operations are the seam's own, only the target path is untrusted, so canonicalize-then-contain is the complete answer to this surface (the `code-runtime` "containment, not a security boundary" precedent). Kernel-grade isolation of untrusted CODE stays `ctx.bash`'s job. The residual resolve-to-syscall race is narrowed by the in-place re-canonicalization and eliminated only by platform primitives (`openat2` `RESOLVE_BENEATH`) not worth their portability cost here.
|
||||
|
||||
### Tool parity — one denial marker, one escalation flow
|
||||
|
||||
`dsh-tool-fs` stamps the effective mode onto each mutation and maps `FS_SANDBOX_DENIED` to the marker the model already knows from bash: `[sandbox: file access denied under <mode> mode]`. When `ctx.fs.sandboxMode` reports a confining mode at registration, `write` and `edit` advertise the same `sandbox_permissions` + `justification` fields, teach the same same-turn retry, and resolve the same `ctx.approval` request before executing — the four outcomes and their verbatim fail-closed texts carried over from [the sandbox Agent Note](2026-07-06-sandbox.md) § Escalation (strict widening checked at execution against the call's effective mode; a grant consumed by the one call that asked; no new session events).
|
||||
|
||||
The shared pieces live in `dsh-sandbox`, which owns the mode types: `WIDER_MODES`, the escalation-target enum, the argument-pairing validation, the denial/hint marker builders, and `approveEscalation` — the ordered fail-closed choreography. `approveEscalation` takes a minimal STRUCTURAL approver (`EscalationApprover`, generic over the agent and call-id types), not the approval service type, so `dsh-sandbox` gains no dependency on the approval or agent packages: each tool passes its own `ctx.approval`, agent, call id, and tool name as ingredients. `dsh-tool-bash` and `dsh-tool-fs` both use these; the cross-file duplication gate holds the single-sourcing honest.
|
||||
|
||||
The [`examples/acp-agent`](../../../../examples/acp-agent/cordis.yml) composition loads `dsh-sandbox-policy` and `dsh-fs-sandbox`, moves the `mode`/`workspaceRoot` config to the policy entry, and drops the old gating that disabled the fs stack under confined modes; `fs-policy` (read-before-edit) composes orthogonally on top. The system prompt still states no sandbox mode — the marker teaches the boundary at the moment it matters, per the sandbox Agent Note's live evidence.
|
||||
|
||||
### The enforcement point: provider, not intent gate
|
||||
|
||||
The sandbox Agent Note's original cross-family sketch put fs enforcement on the `fs/write-intent`/`fs/edit-intent` events. This Agent Note enforces in the provider instead, on two mechanical facts: the intent slots are single-decision first-wins (occupied by `dsh-fs-policy`, whose contract names a second decider a misconfiguration), and the intent events are dispatched only by `dsh-tool-fs` — a direct `ctx.fs` caller (a cordis-mounted plugin, a custom tool) bypasses them, where provider-level enforcement covers every caller by construction. The sandbox Agent Note's deferred-phase wording is updated to match in the same change.
|
||||
|
||||
### Out of scope
|
||||
|
||||
- **Network policy for `ctx.web`** — `SandboxMode` claims file effects only; a web-only network knob while bash `curl` runs free would be a false boundary. Revisit when a bash backend enforces network (bwrap `--unshare-net`, Landlock ABI v4+).
|
||||
- **The `subagent-acp` consumer** and **per-session workspace root** — unchanged deferred phases of the sandbox RFC; centralizing the root in `ctx.sandboxPolicy` is groundwork for the latter, not its design.
|
||||
- **A uniform per-tool sandbox runtime** — remains rejected for the reasons in the sandbox RFC.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Enforce on the `fs/*` intent events (the sandbox Agent Note's original sketch)** — rejected on the two mechanical facts in § The enforcement point: single-slot first-wins already occupied, and a bypass for direct `ctx.fs` callers. Provider-level enforcement covers every caller and mirrors bash's swap-the-implementation shape.
|
||||
- **Enforce in `tools/pre-execute`** — rejected: the listener sees the model's raw path string before `resolve()`, so it would re-implement cwd defaulting and symlink canonicalization and still race the real resolve. Disqualifying for `workspace-write`, a judgment over canonical paths.
|
||||
- **Inline checks in `dsh-tool-fs`** — rejected: covers only the tool path (same bypass as the intent events) and duplicates resolve knowledge one layer above where the canonical target already exists.
|
||||
- **A `mode` flag on `dsh-fs-local` instead of a sibling backend** — rejected: the capability fact must be composition truth the way `dsh-bash-local` vs `dsh-bash-sandbox` is; a config flag makes the tool's advertisement conditional on configuration, and the bash family already establishes the sibling-package shape.
|
||||
- **Kernel-enforced fs mutations via a confined helper subprocess** — rejected: a process per write; `editText`'s read-match-write critical section would have to move wholesale into the child to stay atomic; and the threat surface (trusted operations, untrusted path argument) does not need a kernel — the fence in trusted code is the complete answer, while untrusted-code isolation stays on `ctx.bash`.
|
||||
- **Per-family policy config with a load-time consistency check** — rejected: two homes for one fact, patched by a check that must enumerate every future enforcing family; the policy service makes drift inexpressible instead of detected.
|
||||
- **Keep the override event in `dsh-bash` as `bash/sandbox-mode`** — rejected: the event is policy state consumed by two families; leaving it bash-named forces `dsh-fs-sandbox` to depend on bash vocabulary. Pre-release, the rename is a same-change move with snapshot re-records, no shims.
|
||||
- **Escalation choreography imported from the approval/agent packages into `dsh-sandbox`** — rejected: it would invert the layering (a base vocabulary package depending on UI/agent packages). The structural approver keeps the logic single-sourced in `dsh-sandbox` while the dependencies stay in the tool layer that already holds them.
|
||||
- **A consolidated mutation-options object on the fs seam** (the shape first sketched for the per-call carrier) — rejected on friction: it churns every `writeText`/`editText` caller and splits `signal` across an options bag for mutations while reads keep it positional. A trailing optional `sandboxMode` matches bash's carry-and-ignore pattern and keeps `signal` symmetric across the seam.
|
||||
- **Extra writable-root grants on `SandboxPolicy` now** — deferred unchanged: `writableRoots()` derives from the mode meaning today; ad-hoc grants are an escalation-scope question the sandbox RFC left open.
|
||||
|
||||
## Consequences
|
||||
|
||||
What shipped — the tiers in § Testing hold each:
|
||||
|
||||
- Under `read-only`, `write`/`edit` return the `[sandbox: file access denied under read-only mode]` marker and the disk is untouched; `read`/`listDir` behave identically to `dsh-fs-local`.
|
||||
- Under `workspace-write`, mutations land under the workspace root and the temp areas and are denied outside; the containment matrix — `..` traversal, absolute paths outside, a pre-existing symlinked directory inside pointing out, and a new file created under such a symlink — denies every escape on real disks.
|
||||
- A denied fs mutation retried once with `sandbox_permissions` + `justification` prompts through the composed approval chain; a grant runs exactly that call under the wider mode and the write lands; rejected/cancelled/unavailable each produce their verbatim fail-closed text and mutate nothing.
|
||||
- One `permission` preset switch governs both families: after a session switches modes, the next bash call and the next fs mutation both honor the new mode from the same `sandbox/mode` fold.
|
||||
- A direct `ctx.fs.writeText` with no per-call stamp is confined at the deployment default.
|
||||
- The escalation fields on `write`/`edit` exist exactly when the mounted `ctx.fs` confines, absent under `dsh-fs-local`.
|
||||
- `agent-loop` is untouched — everything rides `ctx.sandboxPolicy`, the `ctx.fs` seam, `SessionEventMap` merging, and the tool-execution pipeline.
|
||||
|
||||
Costs and accepted limits:
|
||||
|
||||
- **The fs fence is a policy boundary, not a kernel one.** Its threat surface is model-chosen paths, not adversarial host processes; the residual resolve-to-syscall TOCTOU is narrowed, not eliminated, and the README says so. Kernel boundaries remain bash's.
|
||||
- **`dsh-bash-sandbox` gains a hard dependency on `ctx.sandboxPolicy`.** Every sandboxed composition adds one `cordis.yml` entry or fails loud at load — the intended pre-release foundation move; the examples update in the same change.
|
||||
- **Fence-vs-runner parity is derived, not asserted.** The fs fence and the Seatbelt profile both take their writable set from `writableRoots`, and a parity unit test pins the sets; a runner profile changing its writable set without that function would drift.
|
||||
- **The marker and escalation teaching now serve two families.** A wording change is a coordinated edit behind one builder in `dsh-sandbox`; the duplication gate and pinned snapshots hold it single-sourced, at the cost that fs and bash cannot deliberately diverge in phrasing without splitting the builder.
|
||||
|
||||
## Testing
|
||||
|
||||
- Unit: `dsh-sandbox` pins the escalation ladder, the marker builders, the argument-pairing validation, and `approveEscalation`'s ordered fail-closed sequence (non-widening, no-approval, no-agent, each outcome), plus `writableRoots`/`canonicalPath`. `dsh-sandbox-policy` pins the default accessors, the fold/setter, the load-time mode rejection, and HMR safety. `dsh-fs-sandbox` pins the per-mode fence and the containment matrix (inside, temp area, absolute-outside, `..`, symlinked-out directory, new file under one, path-equals-root, root-ending-in-separator) on a real filesystem, plus the per-call override and HMR safety. `dsh-tool-fs` pins advertisement gating, the mode stamp, the fold, denial-marker mapping, and the full escalation matrix (grant, reject, no-service, no-agent, pairing, non-confining guard). `dsh-tool-bash`, `dsh-bash-sandbox`, and `dsh-permission` migrate to the relocated policy/kit.
|
||||
- Snapshot: the acp-agent example composes `dsh-sandbox-policy` + `dsh-fs-sandbox`; the pinned header carries the fs escalation fields and the `sandbox/mode` event name, re-recorded once.
|
||||
@@ -0,0 +1,94 @@
|
||||
# Agent Note: 跨家族文件沙箱——统一策略归属、沙箱化 fs 提供方、fs 升级对等
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-14-cross-family-fs-sandbox.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
`SandboxMode` 声明的是文件效果,但最初只有 `ctx.bash` 执行它。fs 工具(`write`/`edit`)在进程内经由 `ctx.fs` 变更宿主文件系统,那里的 OS argv 包装在机制上毫无意义——[沙箱 RFC](2026-07-06-sandbox.md) § In-process tools 记录了这一点,并把跨家族执行留作一个延后阶段,附带一个未决问题:进程内执行是各 seam 各自表达,还是变成一个统一的 harness 能力。本 Agent Note 就是那个阶段,并给出答案:一个共享的策略归属,在每个家族各自正确的高度上做 per-seam 执行。
|
||||
|
||||
这个缺口不是 read-only 形状的。一个受限编码 agent 的产品模式是 `workspace-write`:bash 已经可以在工作区根目录下写入,而其外的一切都被拒绝,所以一个只能全部拒绝的 fs 执行会严格劣于禁用 fs 工具——模型会尝试在工作区内 `write`,被拒,然后学会绕道 `bash` heredoc。因此跨家族执行必须讲完整的模式阶梯,包括 `workspace-write` 要求的路径包含判定(规范化目标;`..`/符号链接/绝对路径逃逸),以及与 bash 相同的升级杠杆。
|
||||
|
||||
第二个执行家族还暴露了原布局中的一个归属问题。部署默认值(`mode` + `workspaceRoot`)配置在 `dsh-bash-sandbox` 上,而 per-session 覆盖事件是 `bash/sandbox-mode`,由 `dsh-bash` 的 session-mode 工具集折叠与写入。当 fs 执行同一套策略时,要么 fs 读取 bash 的配置与事件(一个能力家族依赖同级插件的配置),要么各家族各持一份副本——两份 `workspaceRoot` 会漂移进沙箱 RFC 警告过的那个割裂世界:bash 受限于一个根,而 fs 围栏另一个根。
|
||||
|
||||
## Decision
|
||||
|
||||
三个相互协调的部分,全部在叶子 `cordis.yml` 中组合,均不触及 `agent-loop`。
|
||||
|
||||
### `ctx.sandboxPolicy`——mode 与工作区根的统一归属
|
||||
|
||||
`packages/sandbox/sandbox-policy/`(`@deepseek-ai/dsh-sandbox-policy`)注册 `ctx.sandboxPolicy`,即部署沙箱策略的唯一所有者:
|
||||
|
||||
- `Config`:`mode`(封闭的 `SandboxMode` 联合,默认 `read-only`)与 `workspaceRoot`(默认进程 cwd,解析为绝对路径)。配置错误在加载时高声失败。
|
||||
- per-session 覆盖事件 `sandbox/mode`,连同它的纯折叠(`effectiveSandboxMode(events)`)、写入路径(`setSandboxMode(session, mode)`)与 `SANDBOX_MODES`。该事件是策略状态——被两个家族消费——所以它住在这里,而不在任一能力的 seam 里。它的形状与仅日志(log-only)语义遵循 `approval/*` 的先例。
|
||||
- `defaultMode` / `workspaceRoot` 访问器,供执行实现读取其 resolve 回退值与边界。
|
||||
|
||||
`dsh-bash-sandbox` 自身不再携带任何沙箱配置——它注入 `sandboxPolicy` 并从中读取默认值;其 `resolve()` 优先级不变(升级授权 > per-call 盖章 > 默认)。`dsh-tool-bash` 与 `dsh-tool-fs` 用 `effectiveSandboxMode` 折叠会话的 `sandbox/mode` 以对每次调用盖章;`dsh-permission` 预设与 ACP bridge 经由迁移后的 setter 写入。拥有 bash 执行的那个 seam 不再依赖 `dsh-session`——会话依赖随折叠一起迁到了策略包。
|
||||
|
||||
### `dsh-fs-sandbox`——在提供方内部执行
|
||||
|
||||
`packages/fs/fs-sandbox/`(`@deepseek-ai/dsh-fs-sandbox`)镜像 `bash-local`/`bash-sandbox` 的拆分:`SandboxedFileSystem extends LocalFileSystem`,注册为 `ctx.fs`,注入 `sandboxPolicy`。读取(`resolve`/`stat`/`readText`/`streamText`/`listDir`)原样透传——每种模式都允许读。两个变更操作在委托给继承来的原子写之前按模式执行:
|
||||
|
||||
- `read-only` 直接拒绝 `writeText`/`editText`。
|
||||
- `workspace-write` 把规范化后的目标围栏于可写根集合——`dsh-sandbox` 中的 `writableRoots(policy)`:工作区根加上平台临时目录(`/tmp`、`os.tmpdir()`),各自 realpath——与 Seatbelt profile 授予的是同一个集合,所以 fs 围栏是这一个模式含义在 bwrap/Landlock/Seatbelt profile 之外的第四种方言,因此不会出现「write 工具不能写 `/tmp` 而 bash 能」的不对称。包含判定是对真实路径的前缀包含;目标在委托前被立即重新规范化(`resolve` 对最深的既有祖先做 realpath),因此自工具解析该目标以来被换出的祖先符号链接会被捕获。
|
||||
- `danger-full-access` 不加围栏地委托。
|
||||
|
||||
拒绝是结构化的 `FS_SANDBOX_DENIED`,携带生效模式——区别于 `FS_PERMISSION_DENIED`(宿主 EACCES 是世界在拒绝;这里是策略在拒绝)。无文本推断:进程内围栏确切知道它拒绝了什么。per-call 载体是 `writeText`/`editText` 上一个末尾可选的 `sandboxMode`(文件系统侧对应 `BashExecRequest.sandboxMode`);该 seam 保持无会话依赖(由调用方盖章,正如 `resolve` 接收一个 cwd),而裸的本地后端携带并忽略它。`FileSystem.sandboxMode` 是能力事实(在基类与 `fs-local` 上为 `undefined`,在 `SandboxedFileSystem` 上为默认值),所以工具层按组合真相来宣告升级。
|
||||
|
||||
威胁模型写在包 README 里:一道位于可信代码中、针对模型可控路径的策略围栏,而非内核边界——操作是 seam 自身的,只有目标路径不可信,所以「先规范化再判包含」是对这个面的完整答案(`code-runtime` 的「containment, not a security boundary」先例)。对不可信代码的内核级隔离仍是 `ctx.bash` 的职责。resolve 到系统调用之间残留的竞态被就地重新规范化收窄,只有平台原语(`openat2` `RESOLVE_BENEATH`)能彻底消除它,而那在此不值其可移植性代价。
|
||||
|
||||
### 工具对等——一个拒绝标记、一条升级流程
|
||||
|
||||
`dsh-tool-fs` 把生效模式盖章到每次变更上,并将 `FS_SANDBOX_DENIED` 映射为模型已从 bash 认识的标记:`[sandbox: file access denied under <mode> mode]`。当 `ctx.fs.sandboxMode` 在注册时报告一个受限模式,`write` 与 `edit` 宣告相同的 `sandbox_permissions` + `justification` 字段,教授相同的同回合重试,并在执行前解析相同的 `ctx.approval` 请求——四种结果及其逐字的 fail-closed 文案沿用自[沙箱 RFC](2026-07-06-sandbox.md) § Escalation(严格加宽在执行时针对调用的生效模式检查;授权由发起它的那一次调用消费;无任何新会话事件)。
|
||||
|
||||
共享部分住在 `dsh-sandbox`,它拥有模式类型:`WIDER_MODES`、升级目标枚举、参数配对校验、拒绝/提示标记构造器,以及 `approveEscalation`——有序的 fail-closed 编排。`approveEscalation` 接收一个最小的结构化 approver(`EscalationApprover`,对 agent 与 call-id 类型泛型化),而非审批服务类型,所以 `dsh-sandbox` 不获得对 approval 或 agent 包的依赖:每个工具把自己的 `ctx.approval`、agent、call id 与工具名作为原料传入。`dsh-tool-bash` 与 `dsh-tool-fs` 都使用它们;跨文件重复检测门禁确保单一来源不走样。
|
||||
|
||||
[`examples/acp-agent`](../../../../examples/acp-agent/cordis.yml) 组合加载 `dsh-sandbox-policy` 与 `dsh-fs-sandbox`,把 `mode`/`workspaceRoot` 配置移到策略条目,并去掉在受限模式下禁用整个 fs 栈的旧门控;`fs-policy`(read-before-edit)正交地叠加其上。系统提示仍然不陈述沙箱模式——标记会在真正重要的那一刻教会模型边界,依据沙箱 RFC 的线上证据。
|
||||
|
||||
### 执行点:提供方,而非 intent gate
|
||||
|
||||
沙箱 RFC 最初的跨家族草图把 fs 执行放在 `fs/write-intent`/`fs/edit-intent` 事件上。本 Agent Note 改为在提供方中执行,基于两个机制性事实:intent 槽是单决策、先到先得(已被 `dsh-fs-policy` 占据,其契约称第二个决策者为配置错误),且 intent 事件只由 `dsh-tool-fs` 派发——一个直连 `ctx.fs` 的调用方(一个 cordis 挂载插件、一个自定义工具)会绕过它们,而提供方级执行按构造覆盖每一个调用方。沙箱 RFC 的延后阶段措辞在同一变更中被更新以匹配。
|
||||
|
||||
### 范围之外
|
||||
|
||||
- **`ctx.web` 的网络策略**——`SandboxMode` 只声明文件效果;在 bash `curl` 畅通时给一个仅限 web 的网络旋钮会是一道假边界。待某个 bash 后端能执行网络(bwrap `--unshare-net`、Landlock ABI v4+)时再议。
|
||||
- **`subagent-acp` 消费者** 与 **per-session 工作区根**——沙箱 RFC 未变的延后阶段;把根集中到 `ctx.sandboxPolicy` 是后者的铺垫,而非其设计。
|
||||
- **统一的 per-tool 沙箱运行时**——因沙箱 RFC 中的理由继续否决。
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **在 `fs/*` intent 事件上执行(沙箱 RFC 的原始草图)**——因 § 执行点 中的两个机制性事实被否决:单槽先到先得且已被占据,以及对直连 `ctx.fs` 调用方的绕过。提供方级执行覆盖每一个调用方,并镜像 bash 的换实现形态。
|
||||
- **在 `tools/pre-execute` 中执行**——否决:监听器在 `resolve()` 之前看到模型的原始路径字符串,因此它会重新实现 cwd 默认化与符号链接规范化,并且仍与真正的 resolve 竞态。对 `workspace-write`(一个对规范路径的判定)而言是取消资格级的。
|
||||
- **在 `dsh-tool-fs` 中做内联检查**——否决:只覆盖工具路径(与 intent 事件同样的绕过),并在规范目标已存在之上重复了一层 resolve 知识。
|
||||
- **在 `dsh-fs-local` 上加一个 `mode` 标志而非同级后端**——否决:能力事实必须是组合真相,正如 `dsh-bash-local` 对 `dsh-bash-sandbox`;一个配置标志会让工具的宣告取决于配置,而 bash 家族已经确立了同级包形态。
|
||||
- **经受限 helper 子进程做内核级 fs 变更**——否决:每次写一个进程;`editText` 的读-匹配-写临界区不得不整体搬进子进程才能保持原子;而威胁面(可信操作、不可信路径参数)不需要内核——可信代码中的围栏就是完整答案,而不可信代码隔离仍在 `ctx.bash`。
|
||||
- **带加载期一致性校验的 per-family 策略配置**——否决:一个事实两个归属,靠一个必须枚举每个未来执行家族的校验来打补丁;策略服务让漂移不可表达,而非被检测到。
|
||||
- **把覆盖事件留在 `dsh-bash` 里作 `bash/sandbox-mode`**——否决:该事件是被两个家族消费的策略状态;保留 bash 命名会迫使 `dsh-fs-sandbox` 依赖 bash 词汇。预发布阶段,该改名是同一变更内的迁移,附带快照重录,无任何 shim。
|
||||
- **把升级编排从 approval/agent 包导入 `dsh-sandbox`**——否决:那会倒置分层(一个基础词汇包依赖 UI/agent 包)。结构化 approver 让逻辑单一来源于 `dsh-sandbox`,而依赖留在本就持有它们的工具层。
|
||||
- **fs seam 上一个合并的 mutation-options 对象**(per-call 载体最初草拟的形状)——因摩擦被否决:它会搅动每一个 `writeText`/`editText` 调用方,并把 `signal` 拆进变更专用的选项包,而读取仍保持位置参数。一个末尾可选的 `sandboxMode` 匹配 bash 的携带并忽略模式,并使 `signal` 在整个 seam 上保持对称。
|
||||
- **现在就在 `SandboxPolicy` 上加额外的可写根授权**——照旧延后:`writableRoots()` 如今由模式含义推导;临时授权是沙箱 RFC 留下的升级作用域问题。
|
||||
|
||||
## Consequences
|
||||
|
||||
已交付的部分——§ Testing 的各层各自钉住:
|
||||
|
||||
- 在 `read-only` 下,`write`/`edit` 返回 `[sandbox: file access denied under read-only mode]` 标记,磁盘不受触动;`read`/`listDir` 与 `dsh-fs-local` 行为一致。
|
||||
- 在 `workspace-write` 下,变更落在工作区根与临时目录下,其外被拒;包含矩阵——`..` 穿越、指向外部的绝对路径、一个既有的、指向外部的工作区内符号链接目录,以及在这样一个符号链接下新建的文件——在真实磁盘上拒绝每一种逃逸。
|
||||
- 一个被拒的 fs 变更,携带 `sandbox_permissions` + `justification` 重试一次,会经组合的审批链提示;一次授权让恰好那一次调用在更宽的模式下运行且写入落盘;rejected/cancelled/unavailable 各自产生其逐字的 fail-closed 文案且不做任何变更。
|
||||
- 一次 `permission` 预设切换同时管辖两个家族:会话切换模式后,下一次 bash 调用与下一次 fs 变更都从同一个 `sandbox/mode` 折叠遵循新模式。
|
||||
- 一次无 per-call 盖章的直连 `ctx.fs.writeText` 会被围栏于部署默认值。
|
||||
- `write`/`edit` 上的升级字段恰好在被挂载的 `ctx.fs` 受限时存在,在 `dsh-fs-local` 下不存在。
|
||||
- `agent-loop` 未被触动——一切都骑在 `ctx.sandboxPolicy`、`ctx.fs` seam、`SessionEventMap` 合并,以及工具执行管线之上。
|
||||
|
||||
代价与接受的限制:
|
||||
|
||||
- **fs 围栏是策略边界,而非内核边界。** 它的威胁面是模型选定的路径,而非对抗性宿主进程;resolve 到系统调用之间残留的 TOCTOU 被收窄而非消除,README 已如实声明。内核边界仍属 bash。
|
||||
- **`dsh-bash-sandbox` 获得对 `ctx.sandboxPolicy` 的硬依赖。** 每个沙箱化组合要么加一个 `cordis.yml` 条目,要么在加载时高声失败——这是有意的预发布奠基之举;示例在同一变更内更新。
|
||||
- **围栏与 runner 的对等是推导出来的,而非断言的。** fs 围栏与 Seatbelt profile 都从 `writableRoots` 取其可写集合,一个对等单元测试钉住这些集合;一个 runner profile 若在不经该函数的情况下改变其可写集合便会漂移。
|
||||
- **标记与升级教学如今服务于两个家族。** 措辞改动是 `dsh-sandbox` 中一个构造器背后的协调编辑;重复检测门禁与钉住的快照维持单一来源,代价是 fs 与 bash 无法在不拆分该构造器的情况下有意地在措辞上分道。
|
||||
|
||||
## Testing
|
||||
|
||||
- 单元:`dsh-sandbox` 钉住升级阶梯、标记构造器、参数配对校验,以及 `approveEscalation` 的有序 fail-closed 序列(非加宽、无 approval、无 agent、各结果),外加 `writableRoots`/`canonicalPath`。`dsh-sandbox-policy` 钉住默认访问器、折叠/setter、加载期模式拒绝,以及 HMR 安全。`dsh-fs-sandbox` 在真实文件系统上钉住 per-mode 围栏与包含矩阵(内部、临时目录、绝对路径-外部、`..`、指向外部的符号链接目录、其下的新建文件、路径等于根、以分隔符结尾的根),外加 per-call 覆盖与 HMR 安全。`dsh-tool-fs` 钉住宣告门控、模式盖章、折叠、拒绝标记映射,以及完整的升级矩阵(授权、拒绝、无服务、无 agent、配对、非受限守卫)。`dsh-tool-bash`、`dsh-bash-sandbox` 与 `dsh-permission` 迁移到迁移后的策略/工具集。
|
||||
- 快照:acp-agent 示例组合 `dsh-sandbox-policy` + `dsh-fs-sandbox`;被钉住的 header 携带 fs 升级字段与 `sandbox/mode` 事件名,一次性重录。
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
||||
# 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
|
||||
2026-07-20-unwrap-injected-content-envelopes.md: 32642660f7bcea748c349933b99552b1974922c5
|
||||
2026-07-20-unwrap-injected-content-envelopes.zh.md: a01a51e12cecca5bc46526ccca61dbe90eb3136f
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Agent Note: Project injected content verbatim, dropping the XML envelopes
|
||||
|
||||
Status: implemented
|
||||
|
||||
English | [中文](2026-07-20-unwrap-injected-content-envelopes.zh.md)
|
||||
|
||||
## Problem
|
||||
|
||||
Two families of injected session content rendered into the model transcript wrapped in XML envelopes: `steering/message` as `<steering source="…">…</steering>` and `context/message` as `<context source="…">…</context>` (the latter with a `'raw'` opt-out that skipped the wrapper). The envelopes aimed to tell the model "this is injected, not the user speaking."
|
||||
|
||||
Two problems:
|
||||
|
||||
- **No model is trained on these tags.** `<steering>` and `<context>` are arbitrary markup no model was taught to read, so the framing adds tokens without a reliable effect and can actively mislead — recorded transcripts show a model treating a `<steering>` instruction as third-party metadata and refusing it while answering only the original prompt.
|
||||
- **The session surface is the wrong layer for framing.** The surface projects the durable log into the model transcript; deciding how content is worded is not its job. A caller that wants a particular frame formats its own content before injecting it — which the one heavy producer (`workspace-context`) already does, owning its complete `<system-reminder>` frame and opting out of the `<context>` wrapper with `envelope: 'raw'`. The remaining tag machinery (`ContextEnvelope`, an `envelope` field threaded through `InjectOptions`, `HookContext`, the `context/message` event, and the loop) served a distinction that belongs to the caller.
|
||||
|
||||
## Decision
|
||||
|
||||
Injected session content projects verbatim; the caller owns any framing. `deriveEventMessage` renders `user/message`, `context/message`, and `steering/message` through one shared case returning `{ role: 'user', content: event.data.content }`; their content blocks reach the model unchanged. `context/message`'s `source`/`meta` and `steering/message`'s `turn` stay in the durable event log but do not render.
|
||||
|
||||
The `ContextEnvelope` type and every `envelope` field are removed — `context/message` in `SessionEventMap`, `InjectOptions`, `HookContext`, and the `inject()`/`additionalContexts` plumbing in `dsh-agent-loop`. `workspace-context` no longer requests `'raw'`; its self-framed content renders as before. The `renderTagged`/`renderContextEnvelope` helpers are deleted. `context/message.meta` still carries durable, model-hidden JSON state.
|
||||
|
||||
The `source` attribution the envelopes carried is not lost — it remains on the durable events; it simply no longer renders into the transcript.
|
||||
|
||||
## Alternatives considered
|
||||
|
||||
- **Keep the `<context>` envelope, unwrap only steering** — leaves the `ContextEnvelope`/`envelope` machinery alive for a framing bit no model reads, and keeps the inconsistency that the main producer already opts out of.
|
||||
- **Keep the envelope field for plugin-sourced content only** — splits one projection into two on `source.kind` for no observed benefit; a plugin steering the agent (hook-bridge continuation reasons) also wants the instruction followed, not labeled.
|
||||
- **Move the unwrapping into adapters** — the canonical projection is the model-visible contract ("model-visible ⟺ logged"); per-adapter divergence on framing would make the derived transcript adapter-dependent. Framing that a caller genuinely wants belongs in the caller's content, not in an adapter.
|
||||
|
||||
## Consequences
|
||||
|
||||
- Mid-turn steering and injected context reach the model with the same weight as an ordinary user prompt.
|
||||
- The transcript no longer distinguishes injected content from a user message; consumers that need the distinction read the durable event log, which keeps the event types, `source`, and `meta` intact.
|
||||
- The `hook-{cc,codex}-stop-continue` ACP snapshots were re-recorded: the old recordings captured the model refusing steering as third-party metadata, the fix's exact failure mode.
|
||||
- The [content-block-vocabulary Agent Note](../architecture/2026-06-11-content-block-vocabulary.md)'s tagged-envelope clause is amended to point here.
|
||||
|
||||
## Deferred
|
||||
|
||||
`workspace-context` already frames its own content: it emits a complete `<system-reminder>…</system-reminder>` block as the message content instead of leaning on a surface-level wrapper. That caller-owned pattern is the one to keep — the surface passes content through verbatim, and any framing lives in the producer's own content.
|
||||
|
||||
Two framing paths existed — caller-baked framing (`workspace-context`'s `<system-reminder>`) and surface-level wrapping (`<context>`/`<steering>` added by `deriveEventMessage`). This change removes the second, leaving only caller-owned framing. If labeled framing is wanted again, unify it through the event's `meta` map — the producer-attached, model-hidden metadata field — consumed by a dedicated renderer or adapter, rather than re-hardcoding a tag in `deriveEventMessage`. A producer declares the frame it wants in `meta`; one renderer applies it; the session-surface projection stays a verbatim pass-through.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
# Agent Note: 注入内容逐字投影,去除 XML 封套
|
||||
|
||||
Status: implemented
|
||||
|
||||
[English](2026-07-20-unwrap-injected-content-envelopes.md) | 中文
|
||||
|
||||
## 问题
|
||||
|
||||
两类注入的会话内容在渲染进模型 transcript(文本记录)时被包在 XML 封套里:`steering/message` 包成 `<steering source="…">…</steering>`,`context/message` 包成 `<context source="…">…</context>`(后者有一个 `'raw'` 退出选项可跳过封套)。这些封套意在告诉模型「这是注入内容,不是用户在说话」。
|
||||
|
||||
两个问题:
|
||||
|
||||
- **没有模型在这些标签上训练过。** `<steering>` 和 `<context>` 是任何模型都未被教会去读的任意标记,因此这层框架只是徒增 token 而没有可靠效果,还可能起反作用——已录制的 transcript 显示,模型会把 `<steering>` 指令当成第三方元数据而拒绝服从,只回答原始提示。
|
||||
- **session 表层是承载框架的错误层次。** 表层的职责是把持久日志投影为模型 transcript;决定内容如何措辞并不是它的事。想要特定框架的调用方可以在注入前自行格式化内容——唯一的重度生产方(`workspace-context`)本就这样做,它自带完整的 `<system-reminder>` 框架,并用 `envelope: 'raw'` 退出 `<context>` 封套。剩下的标签机制(`ContextEnvelope` 类型,以及贯穿 `InjectOptions`、`HookContext`、`context/message` 事件和 agent loop 的 `envelope` 字段)所服务的区分,本应归属调用方。
|
||||
|
||||
## 决策
|
||||
|
||||
注入的会话内容逐字投影,框架由调用方自行负责。`deriveEventMessage` 通过一个共享分支渲染 `user/message`、`context/message` 和 `steering/message`,都返回 `{ role: 'user', content: event.data.content }`;它们的内容块原样到达模型。`context/message` 的 `source`/`meta` 和 `steering/message` 的 `turn` 保留在持久事件日志中,但不渲染。
|
||||
|
||||
`ContextEnvelope` 类型和所有 `envelope` 字段都被移除——包括 `SessionEventMap` 中的 `context/message`、`InjectOptions`、`HookContext`,以及 `dsh-agent-loop` 中 `inject()`/`additionalContexts` 的相关管线。`workspace-context` 不再请求 `'raw'`;它自带框架的内容渲染方式不变。`renderTagged`/`renderContextEnvelope` 辅助函数被删除。`context/message.meta` 仍携带持久的、对模型隐藏的 JSON 状态。
|
||||
|
||||
封套曾携带的 `source` 归属并未丢失——它仍保留在持久事件上;只是不再渲染进 transcript。
|
||||
|
||||
## 权衡的替代方案
|
||||
|
||||
- **保留 `<context>` 封套,只对 steering 去封套** —— 会为一个没有模型会读的框架位保留 `ContextEnvelope`/`envelope` 机制,并保留主要生产方本就退出的那种不一致。
|
||||
- **仅对插件来源的内容保留 envelope 字段** —— 会按 `source.kind` 把一条投影拆成两条,却没有观察到任何收益;插件引导 agent(智能体)时(钩子桥接器的轮次续行原因)同样希望指令被遵从,而不是被贴标签。
|
||||
- **把去封套的逻辑移入适配器** —— 规范投影就是模型可见契约(「模型可见 ⟺ 已记录」);让各适配器在框架上各行其是,会使派生的 transcript 依赖于适配器。调用方确实想要的框架应放进调用方自己的内容里,而不是适配器。
|
||||
|
||||
## 结果
|
||||
|
||||
- 中途引导与注入的 context 以与普通用户提示相同的权重到达模型。
|
||||
- transcript 不再区分注入内容与用户消息;需要这一区分的消费方读取持久事件日志,其中事件类型、`source` 和 `meta` 完整保留。
|
||||
- `hook-{cc,codex}-stop-continue` ACP 快照已重新录制:旧录制捕获的是模型把 steering 当作第三方元数据而拒绝服从,正是本次修复针对的失败模式。
|
||||
- [内容块词汇表 Agent Note](../architecture/2026-06-11-content-block-vocabulary.md) 中关于带标签封套的条款已修订为指向本文。
|
||||
|
||||
## 推迟事项
|
||||
|
||||
`workspace-context` 已经自行为内容加框架:它把一个完整的 `<system-reminder>…</system-reminder>` 块作为消息内容发出,而不依赖表层封套。这种调用方自有的模式才是应保留的——表层逐字透传内容,任何框架都住在生产方自己的内容里。
|
||||
|
||||
曾经存在两条框架路径——调用方自行加框架(`workspace-context` 的 `<system-reminder>`),以及表层封套(`deriveEventMessage` 加上的 `<context>`/`<steering>`)。本次变更移除了后者,只留下调用方自有的框架。如果未来又需要带标签的框架,应由事件的 `meta` map(生产方附加、对模型隐藏的元数据字段)来统一它,交给专门的渲染器或适配器消费,而不是在 `deriveEventMessage` 中重新硬编码标签。生产方在 `meta` 中声明所需的框架,由一个渲染器统一施加;session 表层的投影始终保持逐字透传。
|
||||
@@ -27,6 +27,7 @@ A harness is one [Cordis](cordis-primer.md) context. Packages add services (`ctx
|
||||
| `ctx.tokenMeter` | [`llm/token-meter`](../packages/llm/token-meter/README.md) | singleton replay-aware request/surface pressure |
|
||||
| `ctx.bash` | [`bash/`](../packages/bash/README.md) | foreground/background command execution |
|
||||
| `ctx.sandbox` | [`sandbox/`](../packages/sandbox/README.md) | same-world process confinement (argv wrapping, per-call policy) |
|
||||
| `ctx.sandboxPolicy` | [`sandbox/`](../packages/sandbox/README.md) | shared sandbox policy home |
|
||||
| `ctx.codeRuntime` | [`code-runtime/`](../packages/code-runtime/README.md) | model-written program execution |
|
||||
| `ctx.fs` | [`fs/`](../packages/fs/README.md) | filesystem provider primitives and policy events |
|
||||
| `ctx.skills` | [`skill/`](../packages/skill/README.md) | skill provider registry and progressive disclosure |
|
||||
|
||||
@@ -60,6 +60,9 @@ flowchart LR
|
||||
pkg_sandbox["sandbox"]
|
||||
svc_sandbox["ctx.sandbox<br/>Process-sandbox seam"]
|
||||
pkg_sandbox_local["sandbox-local"]
|
||||
pkg_sandbox_policy["sandbox-policy"]
|
||||
svc_sandboxPolicy["ctx.sandboxPolicy<br/>Sandbox policy home"]
|
||||
pkg_fs_sandbox["fs-sandbox"]
|
||||
pkg_approval["approval"]
|
||||
svc_approval["ctx.approval<br/>Approval seam"]
|
||||
pkg_permission["permission"]
|
||||
@@ -109,6 +112,7 @@ flowchart LR
|
||||
pkg_compact_basic --> svc_compact
|
||||
pkg_fs --> svc_fs
|
||||
pkg_fs_local --> svc_fs
|
||||
pkg_fs_sandbox --> svc_fs
|
||||
pkg_llm --> svc_llm
|
||||
pkg_llm_deepseek --> svc_llm
|
||||
pkg_llm_pi_ai --> svc_llm
|
||||
@@ -116,6 +120,7 @@ flowchart LR
|
||||
pkg_permission --> svc_permission
|
||||
pkg_sandbox --> svc_sandbox
|
||||
pkg_sandbox_local --> svc_sandbox
|
||||
pkg_sandbox_policy --> svc_sandboxPolicy
|
||||
pkg_session --> svc_sessions
|
||||
pkg_session_persistence --> svc_sessionPersistence
|
||||
pkg_session_persistence_jsonl --> svc_sessionPersistence
|
||||
@@ -162,6 +167,8 @@ flowchart LR
|
||||
svc_llm --> pkg_compact_basic
|
||||
svc_permission --> pkg_acp
|
||||
svc_sandbox --> pkg_bash_sandbox
|
||||
svc_sandboxPolicy --> pkg_bash_sandbox
|
||||
svc_sandboxPolicy --> pkg_fs_sandbox
|
||||
svc_sessionPersistence --> pkg_acp
|
||||
svc_sessionPersistence --> pkg_agent_loop
|
||||
svc_sessionPersistence --> pkg_hooks_claude
|
||||
@@ -220,10 +227,11 @@ flowchart LR
|
||||
| `ctx.bash` | `seam` | [`bash`](../packages/bash/bash) | [`bash-local`](../packages/bash/bash-local), [`bash-sandbox`](../packages/bash/bash-sandbox) | [`tool-bash`](../packages/bash/tool-bash), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) | - | The model-facing bash tools and hook bridges consume this seam; sandboxed or remote executors replace bash-local without touching them. |
|
||||
| `ctx.bashEnv` | `core` | [`tool-bash`](../packages/bash/tool-bash) | - | - | - | Plugins declare effect-scoped DSH_* facts; tool-bash collects one trusted snapshot per execution and the executor rebuilds the namespace. |
|
||||
| `ctx.sandbox` | `seam` | [`sandbox`](../packages/sandbox/sandbox) | [`sandbox-local`](../packages/sandbox/sandbox-local) | [`bash-sandbox`](../packages/bash/bash-sandbox) | - | Consumers hand over the exact argv they are about to spawn; same-world backends wrap it under a per-call policy and report enforcement. |
|
||||
| `ctx.sandboxPolicy` | `core` | [`sandbox-policy`](../packages/sandbox/sandbox-policy) | - | [`bash-sandbox`](../packages/bash/bash-sandbox), [`fs-sandbox`](../packages/fs/fs-sandbox) | - | The one home for the deployment default mode + workspace root; only the sandboxed executor and provider read the service (the tool layers use the pure `sandbox/mode` fold it also exports). Both enforcing families read it so bash and fs cannot confine to different roots. |
|
||||
| `ctx.approval` | `seam` | `approval` | [`acp`](../packages/ui/acp) | [`tools`](../packages/core/tools), [`tool-bash`](../packages/bash/tool-bash) | - | One-shot permission decisions dispatched over the `approval/request` waterfall; answerers are listeners (the ACP bridge for its own agents), absence fails closed to `unavailable`. |
|
||||
| `ctx.permission` | `core` | [`permission`](../packages/ui/permission) | - | [`acp`](../packages/ui/acp) | - | User-facing preset table (`workspace-write`/`danger-full-access`) bundling the sandbox-mode and approval-policy knobs; a switch writes one `permission/preset` event through to both knob events. |
|
||||
| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | [`code-runtime-worker`](../packages/code-runtime/code-runtime-worker) | [`tools`](../packages/core/tools) | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the tool registry consumes it for Code Mode). |
|
||||
| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-policy`](../packages/fs/fs-policy) | tool-fs executes read/write/edit through ctx.fs; fs-policy contributes observed-state checks through the fs/* event gate. |
|
||||
| `ctx.fs` | `seam` | [`fs`](../packages/fs/fs) | [`fs-local`](../packages/fs/fs-local), [`fs-sandbox`](../packages/fs/fs-sandbox) | [`tool-fs`](../packages/fs/tool-fs) | [`fs-policy`](../packages/fs/fs-policy) | tool-fs executes read/write/edit through ctx.fs; fs-sandbox fences mutations by the shared sandbox mode; fs-policy contributes observed-state checks through the fs/* event gate. |
|
||||
| `ctx.compact` | `seam` | [`compact`](../packages/compact/compact) | [`compact-basic`](../packages/compact/compact-basic) | [`compact-basic`](../packages/compact/compact-basic) | - | The basic backend consumes post-step pressure and request-error recovery events; a model-facing compact tool remains deferred. |
|
||||
| `ctx.subagents` | `seam` | [`subagent`](../packages/subagent/subagent) | [`subagent-spawn`](../packages/subagent/subagent-spawn), [`subagent-fork`](../packages/subagent/subagent-fork), [`subagent-acp`](../packages/subagent/subagent-acp) | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
|
||||
| `ctx.tasks` | `core` | [`tasks`](../packages/tasks/tasks) | - | [`tool-bash`](../packages/bash/tool-bash), [`tool-subagent`](../packages/subagent/tool-subagent), [`tool-tasks`](../packages/tasks/tool-tasks) | - | Producers (tool-bash background commands, tool-subagent background delegations) register running work; tool-tasks is the model-facing control surface that reads, lists, and kills it. |
|
||||
|
||||
+56
-20
@@ -185,28 +185,21 @@ Source: [`packages/bash/bash-local/src/index.ts:17`](../packages/bash/bash-local
|
||||
|
||||
## `@deepseek-ai/dsh-bash-sandbox`
|
||||
|
||||
Requires: `sandbox`
|
||||
Requires: `sandbox` · `sandboxPolicy`
|
||||
|
||||
```ts config-catalog
|
||||
/**
|
||||
* Plugin config: the local executor's knobs plus the sandbox policy. All
|
||||
* optional — `static Config` supplies the defaults (`mode: 'read-only'` is the
|
||||
* fail-safe default; an example that wants a workspace-writable agent opts in
|
||||
* explicitly). The runner choice is not configured here: which platform
|
||||
* backend confines the command is the `ctx.sandbox` provider's config.
|
||||
* Plugin config: the local executor's knobs, verbatim. The sandbox policy —
|
||||
* the default mode and the `workspace-write` boundary root — is NOT here: it
|
||||
* lives on `ctx.sandboxPolicy` (`@deepseek-ai/dsh-sandbox-policy`), the one
|
||||
* home both enforcing families read, so bash and fs can never confine to
|
||||
* different roots. The runner choice is likewise the `ctx.sandbox` provider's
|
||||
* config, not this executor's.
|
||||
*/
|
||||
export interface Config extends LocalConfig {
|
||||
/** File-sandbox mode commands run under (default: `read-only`). */
|
||||
mode?: SandboxMode
|
||||
/**
|
||||
* Root directory `workspace-write` mode may write under (default: the
|
||||
* executor's default working directory — `cwd`, else `process.cwd()`).
|
||||
*/
|
||||
workspaceRoot?: string
|
||||
}
|
||||
export type Config = LocalConfig
|
||||
```
|
||||
|
||||
Depends on: [`LocalConfig`](#deepseek-aidsh-bash-local) · [`SandboxMode`](core-data-structures/sandbox.md)
|
||||
Depends on: [`LocalConfig`](#deepseek-aidsh-bash-local)
|
||||
|
||||
Source: [`packages/bash/bash-sandbox/src/index.ts:27`](../packages/bash/bash-sandbox/src/index.ts)
|
||||
|
||||
@@ -322,6 +315,24 @@ export interface Config {
|
||||
|
||||
Source: [`packages/fs/fs-local/src/index.ts:38`](../packages/fs/fs-local/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-fs-sandbox`
|
||||
|
||||
Requires: `sandboxPolicy`
|
||||
|
||||
```ts config-catalog
|
||||
/**
|
||||
* Plugin config: the local backend's knobs, verbatim (only `cwd`, the resolve
|
||||
* base for relative paths). The sandbox default (mode + `workspace-write`
|
||||
* boundary root) is NOT here — it lives on `ctx.sandboxPolicy`, the one home
|
||||
* both enforcing families share.
|
||||
*/
|
||||
export type Config = LocalConfig
|
||||
```
|
||||
|
||||
Depends on: [`LocalConfig`](#deepseek-aidsh-fs-local)
|
||||
|
||||
Source: [`packages/fs/fs-sandbox/src/index.ts:49`](../packages/fs/fs-sandbox/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-hooks-claude`
|
||||
|
||||
Requires: `bash`
|
||||
@@ -598,7 +609,7 @@ export interface Config {
|
||||
|
||||
/** One preset's sandbox/approval bundle and optional client presentation. */
|
||||
export interface PresetSpec {
|
||||
/** The `bash/sandbox-mode` value the preset writes through. */
|
||||
/** The `sandbox/mode` value the preset writes through. */
|
||||
sandbox: SandboxMode
|
||||
/** The `approval/policy` value the preset writes through. */
|
||||
approval: ApprovalPolicy
|
||||
@@ -611,7 +622,7 @@ export interface PresetSpec {
|
||||
|
||||
Depends on: [`ApprovalPolicy`](core-data-structures/approval.md) · [`SandboxMode`](core-data-structures/sandbox.md)
|
||||
|
||||
Source: [`packages/ui/permission/src/index.ts:80`](../packages/ui/permission/src/index.ts)
|
||||
Source: [`packages/ui/permission/src/index.ts:83`](../packages/ui/permission/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-repeat-tool-guard`
|
||||
|
||||
@@ -673,6 +684,31 @@ export interface Config {
|
||||
|
||||
Source: [`packages/sandbox/sandbox-local/src/index.ts:19`](../packages/sandbox/sandbox-local/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-sandbox-policy`
|
||||
|
||||
```ts config-catalog
|
||||
/**
|
||||
* Plugin config: the deployment's sandbox default. All optional — `Config`
|
||||
* supplies the defaults (`mode: 'read-only'` is the fail-safe default; a
|
||||
* deployment that wants a workspace-writable agent opts in explicitly). The
|
||||
* runner choice is NOT here (it is the `ctx.sandbox` provider's config), nor
|
||||
* is any per-family knob: this is the one shared policy home.
|
||||
*/
|
||||
export interface Config {
|
||||
/** File-sandbox mode a session starts from (default: `read-only`). */
|
||||
mode?: SandboxMode
|
||||
/**
|
||||
* Absolute root directory `workspace-write` may write under (default:
|
||||
* `process.cwd()`). Both enforcing families fence against this SAME root.
|
||||
*/
|
||||
workspaceRoot?: string
|
||||
}
|
||||
```
|
||||
|
||||
Depends on: [`SandboxMode`](core-data-structures/sandbox.md)
|
||||
|
||||
Source: [`packages/sandbox/sandbox-policy/src/index.ts:44`](../packages/sandbox/sandbox-policy/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-session-persistence-jsonl`
|
||||
|
||||
Requires: `sessions`
|
||||
@@ -1028,7 +1064,7 @@ export interface Config {
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/bash/tool-bash/src/index.ts:39`](../packages/bash/tool-bash/src/index.ts)
|
||||
Source: [`packages/bash/tool-bash/src/index.ts:40`](../packages/bash/tool-bash/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-tool-cordis`
|
||||
|
||||
@@ -1066,7 +1102,7 @@ export interface Config {
|
||||
}
|
||||
```
|
||||
|
||||
Source: [`packages/fs/tool-fs/src/index.ts:22`](../packages/fs/tool-fs/src/index.ts)
|
||||
Source: [`packages/fs/tool-fs/src/index.ts:24`](../packages/fs/tool-fs/src/index.ts)
|
||||
|
||||
## `@deepseek-ai/dsh-tool-fs-search`
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ A fully configured agent and live session were published. Setup is composition-o
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:147`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:143`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/disposed` — emit
|
||||
|
||||
@@ -53,7 +53,7 @@ An agent left the registry; AgentLoop emits this after driver quiescence but bef
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:156`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:152`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/error` — emit
|
||||
|
||||
@@ -75,7 +75,7 @@ A step or turn errored. The loop reports a failure here (plus the logger) even w
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:311`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:307`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/post-step` — serial
|
||||
|
||||
@@ -98,7 +98,7 @@ Awaited serial checkpoint after the response, real or synthetic tool results, in
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:264`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:260`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/pre-step` — serial
|
||||
|
||||
@@ -121,7 +121,7 @@ Awaited serial checkpoint before `step/start`; appends land outside the pending
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:204`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:200`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/prompt-submit` — waterfall
|
||||
|
||||
@@ -142,7 +142,7 @@ Allow, rewrite, or block one drained prompt before it becomes a user message. Ca
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [MessageSource](../core-data-structures/core.md) · [PromptDecision](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:214`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:210`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/queued` — emit
|
||||
|
||||
@@ -163,7 +163,7 @@ Detached, frozen content entered the agent's inbox. Source defaults have already
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [MessageSource](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:175`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:171`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/request` — waterfall
|
||||
|
||||
@@ -186,7 +186,7 @@ Replace the frozen call configuration. Model-visible content must use logged cha
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [LlmCallConfig](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:226`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:222`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/request-error` — waterfall
|
||||
|
||||
@@ -211,7 +211,7 @@ Recover a model-request failure after its failed step has closed. `retry` opens
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [RequestError](../core-data-structures/core.md) · [RequestErrorDecision](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:278`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:274`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/session-prefix` — waterfall
|
||||
|
||||
@@ -237,7 +237,7 @@ Compose request-only messages placed before derived history. The frozen result i
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:241`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:237`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/session-start` — emit
|
||||
|
||||
@@ -259,7 +259,7 @@ The session lifecycle began, once before the first turn. Use `agent.inject()` to
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md) · [SessionStartSource](../core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:188`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:184`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/status` — emit
|
||||
|
||||
@@ -279,7 +279,7 @@ Agent status changed (`idle` ⇄ `running`, or → `disposed`). `send()` does no
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [AgentStatus](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:165`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:161`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/step-result` — waterfall
|
||||
|
||||
@@ -301,7 +301,7 @@ Waterfall: post-process the assembled assistant Message before tool dispatch (va
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [Message](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:252`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:248`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/turn-continuation` — waterfall
|
||||
|
||||
@@ -322,7 +322,7 @@ Override whether the turn continues. The default continues after tool calls or s
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [ContinuationDecision](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:288`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:284`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
### `agent/turn-stop` — serial
|
||||
|
||||
@@ -343,7 +343,7 @@ Monotonic terminal-stop checkpoint after continuation and steering are folded; a
|
||||
|
||||
Types: [Agent](../core-data-structures/core.md) · [ContinuationStop](../core-data-structures/core.md) · [Scoped](../core-data-structures/scope.md)
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts:298`](../../packages/core/agent/src/types.ts)
|
||||
Source: [`packages/core/agent/src/types.ts:294`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
## `agent-loop/*`
|
||||
|
||||
@@ -408,7 +408,7 @@ Single-slot decision for the next FileSystem.editText. Calling `next()` yields a
|
||||
|
||||
Types: [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md)
|
||||
|
||||
Source: [`packages/fs/fs/src/index.ts:61`](../../packages/fs/fs/src/index.ts)
|
||||
Source: [`packages/fs/fs/src/index.ts:62`](../../packages/fs/fs/src/index.ts)
|
||||
|
||||
### `fs/observed` — emit
|
||||
|
||||
@@ -428,7 +428,7 @@ Record a successful observation. Listeners must be synchronous recorders: throws
|
||||
|
||||
Types: [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md)
|
||||
|
||||
Source: [`packages/fs/fs/src/index.ts:70`](../../packages/fs/fs/src/index.ts)
|
||||
Source: [`packages/fs/fs/src/index.ts:71`](../../packages/fs/fs/src/index.ts)
|
||||
|
||||
### `fs/write-intent` — waterfall
|
||||
|
||||
@@ -448,7 +448,7 @@ Single-slot decision for the next FileSystem.writeText. Calling `next()` yields
|
||||
|
||||
Types: [FsTarget](../core-data-structures/filesystem.md) · [FsWriteIntent](../core-data-structures/filesystem.md)
|
||||
|
||||
Source: [`packages/fs/fs/src/index.ts:53`](../../packages/fs/fs/src/index.ts)
|
||||
Source: [`packages/fs/fs/src/index.ts:54`](../../packages/fs/fs/src/index.ts)
|
||||
|
||||
## `llm/*`
|
||||
|
||||
|
||||
@@ -286,7 +286,7 @@ abstract start(spec: BashExecSpec): BashProcess
|
||||
|
||||
Types: [BashExecRequest](../core-data-structures/bash.md) · [BashExecSpec](../core-data-structures/bash.md) · [BashProcess](../core-data-structures/bash.md) · [BashRunResult](../core-data-structures/bash.md)
|
||||
|
||||
Source: [`packages/bash/bash/src/index.ts:49`](../../packages/bash/bash/src/index.ts)
|
||||
Source: [`packages/bash/bash/src/index.ts:48`](../../packages/bash/bash/src/index.ts)
|
||||
|
||||
## `ctx.bashEnv` — `BashEnvRegistry`
|
||||
|
||||
@@ -317,7 +317,7 @@ list(): BashEnvVariableInfo[]
|
||||
|
||||
Types: [DshEnvironment](../core-data-structures/bash.md) · [ToolExecution](../core-data-structures/tools.md)
|
||||
|
||||
Source: [`packages/bash/tool-bash/src/index.ts:102`](../../packages/bash/tool-bash/src/index.ts)
|
||||
Source: [`packages/bash/tool-bash/src/index.ts:103`](../../packages/bash/tool-bash/src/index.ts)
|
||||
|
||||
## `ctx.codeRuntime` — `CodeRuntime` (abstract seam)
|
||||
|
||||
@@ -458,9 +458,12 @@ abstract listDir(target: FsTarget, signal?: AbortSignal): Promise<FsDirEntry[]>
|
||||
* @param content - the full new file content.
|
||||
* @param expected - the write intent guarding the write; omit for unconditional.
|
||||
* @param signal - aborts before the atomic rename takes effect.
|
||||
* @param sandboxMode - the per-call sandbox mode this write runs under; a
|
||||
* sandboxing backend fences the write by it, the bare backend ignores it.
|
||||
* Omit to leave the backend its own default.
|
||||
* @returns the outcome, including the version the write produced.
|
||||
*/
|
||||
abstract writeText(target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal): Promise<FsWriteOutcome>
|
||||
abstract writeText( target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal, sandboxMode?: SandboxMode, ): Promise<FsWriteOutcome>
|
||||
|
||||
/**
|
||||
* Atomically edit literal text. When supplied, the version guard is checked
|
||||
@@ -470,14 +473,17 @@ abstract writeText(target: FsTarget, content: string, expected?: FsWriteIntent,
|
||||
* @param edit - the literal search/replace request.
|
||||
* @param expected - the version guard; omit for an unconditional edit.
|
||||
* @param signal - aborts before the atomic rename takes effect.
|
||||
* @param sandboxMode - the per-call sandbox mode this edit runs under; a
|
||||
* sandboxing backend fences the edit by it, the bare backend ignores it.
|
||||
* Omit to leave the backend its own default.
|
||||
* @returns the outcome, including the version the edit produced.
|
||||
*/
|
||||
abstract editText(target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal): Promise<FsEditOutcome>
|
||||
abstract editText( target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal, sandboxMode?: SandboxMode, ): Promise<FsEditOutcome>
|
||||
```
|
||||
|
||||
Types: [FsDirEntry](../core-data-structures/filesystem.md) · [FsEditOutcome](../core-data-structures/filesystem.md) · [FsEditRequest](../core-data-structures/filesystem.md) · [FsInfo](../core-data-structures/filesystem.md) · [FsPathInfo](../core-data-structures/filesystem.md) · [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md) · [FsWriteIntent](../core-data-structures/filesystem.md) · [FsWriteOutcome](../core-data-structures/filesystem.md)
|
||||
Types: [FsDirEntry](../core-data-structures/filesystem.md) · [FsEditOutcome](../core-data-structures/filesystem.md) · [FsEditRequest](../core-data-structures/filesystem.md) · [FsInfo](../core-data-structures/filesystem.md) · [FsPathInfo](../core-data-structures/filesystem.md) · [FsTarget](../core-data-structures/filesystem.md) · [FsVersion](../core-data-structures/filesystem.md) · [FsWriteIntent](../core-data-structures/filesystem.md) · [FsWriteOutcome](../core-data-structures/filesystem.md) · [SandboxMode](../core-data-structures/sandbox.md)
|
||||
|
||||
Source: [`packages/fs/fs/src/index.ts:80`](../../packages/fs/fs/src/index.ts)
|
||||
Source: [`packages/fs/fs/src/index.ts:81`](../../packages/fs/fs/src/index.ts)
|
||||
|
||||
## `ctx.llm` — `LlmService`
|
||||
|
||||
@@ -569,7 +575,7 @@ set(session: Session, name: string): void
|
||||
|
||||
Types: [Session](../core-data-structures/session.md) · [SessionEvent](../core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/ui/permission/src/index.ts:94`](../../packages/ui/permission/src/index.ts)
|
||||
Source: [`packages/ui/permission/src/index.ts:97`](../../packages/ui/permission/src/index.ts)
|
||||
|
||||
## `ctx.sandbox` — `SandboxProvider` (abstract seam)
|
||||
|
||||
@@ -592,7 +598,13 @@ abstract confine(argv: readonly string[], policy: SandboxPolicy): ConfinedArgv
|
||||
|
||||
Types: [ConfinedArgv](../core-data-structures/sandbox.md) · [SandboxPolicy](../core-data-structures/sandbox.md)
|
||||
|
||||
Source: [`packages/sandbox/sandbox/src/index.ts:111`](../../packages/sandbox/sandbox/src/index.ts)
|
||||
Source: [`packages/sandbox/sandbox/src/index.ts:122`](../../packages/sandbox/sandbox/src/index.ts)
|
||||
|
||||
## `ctx.sandboxPolicy` — `SandboxPolicyService`
|
||||
|
||||
The sandbox-policy service (`ctx.sandboxPolicy`). Owns the deployment default mode and workspace root; enforcing implementations read defaultMode and workspaceRoot, and the tool layers fold each session's `sandbox/mode` override with effectiveSandboxMode on top.
|
||||
|
||||
Source: [`packages/sandbox/sandbox-policy/src/index.ts:60`](../../packages/sandbox/sandbox-policy/src/index.ts)
|
||||
|
||||
## `ctx.sessionPersistence` — `SessionPersistence` (abstract seam)
|
||||
|
||||
@@ -820,7 +832,7 @@ fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId):
|
||||
|
||||
Types: [CreateSessionOptions](../core-data-structures/persistence.md) · [Session](../core-data-structures/session.md) · [SessionId](../core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/session/src/index.ts:577`](../../packages/core/session/src/index.ts)
|
||||
Source: [`packages/core/session/src/index.ts:549`](../../packages/core/session/src/index.ts)
|
||||
|
||||
## `ctx.skills` — `SkillService`
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ interface CollectedOutput {
|
||||
|
||||
## File sandbox: `BashSandboxInfo`
|
||||
|
||||
A sandbox-consuming executor exposes its configured fallback through `BashExecutor.sandboxMode`. The tool layer folds each session's durable `bash/sandbox-mode` override and may replace it for one user-approved strictly wider call. The mode/enforcement vocabulary is owned by the [`@deepseek-ai/dsh-sandbox` seam](sandbox.md); modes govern file effects only.
|
||||
A sandbox-consuming executor exposes its configured fallback through `BashExecutor.sandboxMode`. The tool layer folds each session's durable `sandbox/mode` override (owned by [`@deepseek-ai/dsh-sandbox-policy`](../../packages/sandbox/sandbox-policy/README.md)) and may replace it for one user-approved strictly wider call. The mode/enforcement vocabulary is owned by the [`@deepseek-ai/dsh-sandbox` seam](sandbox.md); modes govern file effects only.
|
||||
|
||||
A sandboxed run reports its mode, conservative denial classification, and enforcement completeness. `runnerFailed` marks a sandbox runner failure before the command ran; foreground execution throws `SANDBOX_UNAVAILABLE`, while a settled background process has only its facts channel.
|
||||
|
||||
|
||||
@@ -338,13 +338,11 @@ The fourteen event variants (`turn/start`, `turn/end`, `step/start`, `step/end`,
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
`InjectOptions` extends ordinary message attribution with context-only framing and durable model-hidden JSON metadata:
|
||||
`InjectOptions` extends ordinary message attribution with durable model-hidden JSON metadata:
|
||||
|
||||
```ts type-equiv
|
||||
/** Options specific to durable synthetic context injection. */
|
||||
interface InjectOptions extends SendOptions {
|
||||
/** Keep the canonical context tag, or send caller-owned framing verbatim. */
|
||||
envelope?: ContextEnvelope
|
||||
/** Opaque JSON state retained in the session event but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
@@ -407,7 +405,7 @@ The process-local initiator carried by `ctx.agents` is the exact `Agent` above,
|
||||
|
||||
## Interception decisions
|
||||
|
||||
Each `agent/*` interception waterfall returns a small, seam-specific typed union — the unified Decision idiom (the tool seams' `PreToolDecision`/`PostToolDecision` in [tools.md](tools.md) follow the same shape). A CC/Codex hook bridge maps its `permissionDecision`/`decision`/`continue`/`additionalContext` fields onto these; a native plugin returns them directly. Prompt and post-tool decisions share one model-facing context shape, `HookContext`, which is `inject()`ed as a `context/message` and therefore carries a REQUIRED `source` (a missing source would default to `{kind:'user'}` and mislabel plugin context as a user prompt). Its optional `envelope` selects the canonical context tag or caller-owned raw framing, while JSON `meta` persists plugin state without exposing it to the model. Both decisions carry `additionalContexts[]` so every entry preserves its own provenance, framing, and metadata. Continuation reasons are steering messages instead and deliberately use the narrower content/source shape.
|
||||
Each `agent/*` interception waterfall returns a small, seam-specific typed union — the unified Decision idiom (the tool seams' `PreToolDecision`/`PostToolDecision` in [tools.md](tools.md) follow the same shape). A CC/Codex hook bridge maps its `permissionDecision`/`decision`/`continue`/`additionalContext` fields onto these; a native plugin returns them directly. Prompt and post-tool decisions share one model-facing context shape, `HookContext`, which is `inject()`ed as a `context/message` and therefore carries a REQUIRED `source` (a missing source would default to `{kind:'user'}` and mislabel plugin context as a user prompt). Its `content` reaches the model verbatim as a user-role message, while JSON `meta` persists plugin state without exposing it to the model. Both decisions carry `additionalContexts[]` so every entry preserves its own provenance and metadata. Continuation reasons are steering messages instead and deliberately use the narrower content/source shape.
|
||||
|
||||
Source: [`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types.ts)
|
||||
|
||||
@@ -416,8 +414,6 @@ Source: [`packages/core/agent/src/types.ts`](../../packages/core/agent/src/types
|
||||
interface HookContext {
|
||||
content: ContentBlock[]
|
||||
source: MessageSource
|
||||
/** Keep the canonical context tag, or use caller-owned framing verbatim. */
|
||||
envelope?: ContextEnvelope
|
||||
/** Opaque JSON state retained in the session event but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
@@ -436,7 +432,7 @@ type PromptDecision =
|
||||
| { kind: 'block'; reason: string }
|
||||
```
|
||||
|
||||
`agent/turn-continuation` returns a `ContinuationDecision` (the loop's default is `continue` when the step had tool calls or steering was injected, else `stop`; a `continue` `reason` is recorded as next-step steering in the same turn and therefore carries no context envelope or metadata — the typed `/goal` pattern):
|
||||
`agent/turn-continuation` returns a `ContinuationDecision` (the loop's default is `continue` when the step had tool calls or steering was injected, else `stop`; a `continue` `reason` is recorded as next-step steering in the same turn and therefore carries no context metadata — the typed `/goal` pattern):
|
||||
|
||||
```ts type-equiv
|
||||
/** Turn continuation override; a continue reason is recorded as next-step steering in the same turn. */
|
||||
|
||||
@@ -241,6 +241,7 @@ type FsErrorCode =
|
||||
| 'FS_NOT_TEXT'
|
||||
| 'FS_NOT_REGULAR_FILE'
|
||||
| 'FS_PERMISSION_DENIED'
|
||||
| 'FS_SANDBOX_DENIED'
|
||||
| 'FS_IO_ERROR'
|
||||
| 'FS_STALE_VERSION'
|
||||
| 'FS_NOT_OBSERVED'
|
||||
@@ -249,7 +250,7 @@ type FsErrorCode =
|
||||
| 'FS_ABORTED'
|
||||
```
|
||||
|
||||
`FS_NOT_DIRECTORY`, `FS_PERMISSION_DENIED`, and `FS_IO_ERROR` are used by directory listing to distinguish an existing non-directory target, a denied listing, and an unexpected backend I/O failure. `FS_NOT_OBSERVED` means the policy plugin has no prior-observation record for this owner (or a `createIfAbsent` hit an existing file). `FS_STALE_VERSION` means the backend version no longer matches the observed one (or an edit hit a missing target). Freshness authorization has no partial/full distinction, so there is no `FS_PARTIAL_OBSERVATION`.
|
||||
`FS_NOT_DIRECTORY`, `FS_PERMISSION_DENIED`, and `FS_IO_ERROR` are used by directory listing to distinguish an existing non-directory target, a denied listing, and an unexpected backend I/O failure. `FS_SANDBOX_DENIED` is a POLICY refusal from a sandbox-enforcing backend (`dsh-fs-sandbox`) — the mode fence denied a write/edit — distinct from `FS_PERMISSION_DENIED` (the host kernel refusing). `FS_NOT_OBSERVED` means the policy plugin has no prior-observation record for this owner (or a `createIfAbsent` hit an existing file). `FS_STALE_VERSION` means the backend version no longer matches the observed one (or an edit hit a missing target). Freshness authorization has no partial/full distinction, so there is no `FS_PARTIAL_OBSERVATION`.
|
||||
|
||||
## The service and the plugin
|
||||
|
||||
|
||||
@@ -4,15 +4,6 @@ The in-memory, event-sourced model of [dsh-session](../../packages/core/session)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts`](../../packages/core/session/src/types.ts)
|
||||
|
||||
## Context framing
|
||||
|
||||
`ContextEnvelope` selects the standard tagged projection or preserves a producer-owned complete frame. The latter changes framing only; the event remains a user-role `context/message` in chronological history.
|
||||
|
||||
```ts type-equiv
|
||||
/** Canonical context-tag framing, or caller-owned framing rendered verbatim. */
|
||||
type ContextEnvelope = 'context' | 'raw'
|
||||
```
|
||||
|
||||
## `SessionEventMap` — the event vocabulary
|
||||
|
||||
The append-only event types. Merge-extensible: a plugin declares extra event types via declaration merging — e.g. the [compaction seam](compaction.md) adds `compact/start` / `compact/summary` / `compact/end`, and `@deepseek-ai/dsh-hook-protocol` adds log-only `hook/invoked` / `hook/result` provenance for a hook bridge. Like `compact/*`, these are NOT `SurfaceEventType`s (no `surfaceOp`). The generated [persistence log event catalog](../persistence-catalog.md) enumerates every member — core and merged — with its payload, surface badge, and declaration site.
|
||||
@@ -52,14 +43,17 @@ interface SessionEventMap {
|
||||
/**
|
||||
* In-session context injection (file-change notices, subdir AGENTS.md,
|
||||
* skill content, cron notifications, …). Rendered into the derived history
|
||||
* as synthetic context — NOT a user prompt. `envelope: 'raw'` lets a caller
|
||||
* own the complete model-facing frame; `meta` is durable JSON state omitted
|
||||
* from the model projection.
|
||||
* as a synthetic user-role message carrying `content` verbatim — NOT a
|
||||
* user prompt. `meta` is durable JSON state omitted from the model
|
||||
* projection; it is also the intended channel for any future framing
|
||||
* directive (a producer declares the frame, a dedicated renderer applies it —
|
||||
* see the deferred note in
|
||||
* ../../../../.agents/notes/implemented/simplification/2026-07-20-unwrap-injected-content-envelopes.md),
|
||||
* so the surface keeps projecting `content` verbatim rather than wrapping it.
|
||||
*/
|
||||
'context/message': {
|
||||
content: ContentBlock[]
|
||||
source: MessageSource
|
||||
envelope?: ContextEnvelope
|
||||
meta?: JsonValue
|
||||
}
|
||||
/** Raw stream chunk — token-level replay fidelity. */
|
||||
@@ -432,8 +426,8 @@ declare class Session {
|
||||
- `user/message` → a user message.
|
||||
- `assistant/message` → an assistant message with the event's provider/model provenance and optional adapter-private replay state. Raw `assistant/chunk` events are replay/UI data and are **skipped** in derivation (the assembled message is authoritative). An **empty-content** `assistant/message` is also skipped — a max-tokens step cut off with no content still records an `assistant/message` to host its usage/provenance, but a content-less assistant turn must not enter the provider transcript.
|
||||
- `tool/result` → a user message carrying a `tool-result` block.
|
||||
- `context/message` → a user-role message at its chronological position. The default `envelope` is `context`, which wraps content as `<context source="…">…</context>`; `envelope: 'raw'` uses caller-owned framing verbatim. Optional JSON `meta` remains in the event log and is never rendered.
|
||||
- `steering/message` → a user-role message wrapped in `<steering source="…">…</steering>` at its chronological position.
|
||||
- `context/message` → a user-role message carrying its `content` verbatim at its chronological position. Optional JSON `meta` remains in the event log and is never rendered.
|
||||
- `steering/message` → a user-role message carrying its content verbatim at its chronological position.
|
||||
|
||||
Everything else (`turn/*`, `step/*`) is structural and does not project into a message. Token usage is observed on `assistant/message.usage` (the step that produced it); an operational error's step number is on `turn/end.reason` for `kind: 'error'`. Because this unreleased format intentionally has no compatibility promise, seed/load validation rejects request headers without provider+model and assistant messages without provider/model provenance instead of guessing a route for historical data.
|
||||
|
||||
|
||||
@@ -180,8 +180,8 @@ A tool body receives the runtime extension. `deferContext()` is the composite-to
|
||||
interface ToolRunContext extends ToolExecution {
|
||||
/**
|
||||
* Defer one nested-dispatch context until this tool's final result reaches
|
||||
* the agent loop. Contexts retain their individual source, envelope, and
|
||||
* metadata and are emitted in call order.
|
||||
* the agent loop. Contexts retain their individual source and metadata and
|
||||
* are emitted in call order.
|
||||
*/
|
||||
deferContext(context: HookContext): void
|
||||
}
|
||||
|
||||
@@ -8,25 +8,25 @@ This matrix shows which packages dispatch each harness-owned event and which pac
|
||||
| Event | Mode | Declared in | Dispatchers | Listeners |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `agent-loop/config-start-failed` | `emit` | [`packages/core/agent-loop/src/index.ts:362`](../packages/core/agent-loop/src/index.ts) | [`agent-loop`](../packages/core/agent-loop) (`events.dispatch`) | [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:147`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:156`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:311`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`tui`](../packages/ui/tui) |
|
||||
| `agent/post-step` | `serial` | [`packages/core/agent/src/types.ts:264`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic) |
|
||||
| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:204`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`time-context`](../packages/context/time-context), [`user-approval`](../packages/ui/user-approval) |
|
||||
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:214`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) |
|
||||
| `agent/queued` | `emit` | [`packages/core/agent/src/types.ts:175`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
|
||||
| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:226`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp) |
|
||||
| `agent/request-error` | `waterfall` | [`packages/core/agent/src/types.ts:278`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compact-basic`](../packages/compact/compact-basic) |
|
||||
| `agent/session-prefix` | `waterfall` | [`packages/core/agent/src/types.ts:241`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
|
||||
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:188`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`stdio`](../packages/ui/stdio) |
|
||||
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:165`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`invariants`](../packages/support/invariants), [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:252`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
|
||||
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:288`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
|
||||
| `agent/turn-stop` | `serial` | [`packages/core/agent/src/types.ts:298`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
|
||||
| `agent/created` | `emit` | [`packages/core/agent/src/types.ts:143`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/disposed` | `emit` | [`packages/core/agent/src/types.ts:152`](../packages/core/agent/src/types.ts) | [`agent`](../packages/core/agent) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/error` | `emit` | [`packages/core/agent/src/types.ts:307`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`tui`](../packages/ui/tui) |
|
||||
| `agent/post-step` | `serial` | [`packages/core/agent/src/types.ts:260`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`compact-basic`](../packages/compact/compact-basic) |
|
||||
| `agent/pre-step` | `serial` | [`packages/core/agent/src/types.ts:200`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`time-context`](../packages/context/time-context), [`user-approval`](../packages/ui/user-approval) |
|
||||
| `agent/prompt-submit` | `waterfall` | [`packages/core/agent/src/types.ts:210`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp), [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) |
|
||||
| `agent/queued` | `emit` | [`packages/core/agent/src/types.ts:171`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | - |
|
||||
| `agent/request` | `waterfall` | [`packages/core/agent/src/types.ts:222`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`acp`](../packages/ui/acp) |
|
||||
| `agent/request-error` | `waterfall` | [`packages/core/agent/src/types.ts:274`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`compact-basic`](../packages/compact/compact-basic) |
|
||||
| `agent/session-prefix` | `waterfall` | [`packages/core/agent/src/types.ts:237`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`tool-skill`](../packages/skill/tool-skill), [`workspace-context`](../packages/context/workspace-context) |
|
||||
| `agent/session-start` | `emit` | [`packages/core/agent/src/types.ts:184`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex), [`stdio`](../packages/ui/stdio) |
|
||||
| `agent/status` | `emit` | [`packages/core/agent/src/types.ts:161`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`emit`) | [`invariants`](../packages/support/invariants), [`stdio`](../packages/ui/stdio), [`tui`](../packages/ui/tui) |
|
||||
| `agent/step-result` | `waterfall` | [`packages/core/agent/src/types.ts:248`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | - |
|
||||
| `agent/turn-continuation` | `waterfall` | [`packages/core/agent/src/types.ts:284`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`waterfall`) | [`hooks-claude`](../packages/hooks/hooks-claude), [`hooks-codex`](../packages/hooks/hooks-codex) |
|
||||
| `agent/turn-stop` | `serial` | [`packages/core/agent/src/types.ts:294`](../packages/core/agent/src/types.ts) | [`agent-loop`](../packages/core/agent-loop) (`serial`) | [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
|
||||
| `approval/request` | `waterfall` | [`packages/ui/user-approval/src/index.ts:31`](../packages/ui/user-approval/src/index.ts) | [`user-approval`](../packages/ui/user-approval) (`waterfall`) | [`acp`](../packages/ui/acp) |
|
||||
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:61`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
|
||||
| `fs/observed` | `emit` | [`packages/fs/fs/src/index.ts:70`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`emit`) | [`fs-policy`](../packages/fs/fs-policy) |
|
||||
| `fs/write-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:53`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
|
||||
| `fs/edit-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:62`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
|
||||
| `fs/observed` | `emit` | [`packages/fs/fs/src/index.ts:71`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`emit`) | [`fs-policy`](../packages/fs/fs-policy) |
|
||||
| `fs/write-intent` | `waterfall` | [`packages/fs/fs/src/index.ts:54`](../packages/fs/fs/src/index.ts) | [`tool-fs`](../packages/fs/tool-fs) (`waterfall`) | [`fs-policy`](../packages/fs/fs-policy) |
|
||||
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:43`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`invariants`](../packages/support/invariants), [`llm-replay`](../packages/support/llm-replay) |
|
||||
| `session/created` | `emit` | [`packages/core/session/src/index.ts:47`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`invariants`](../packages/support/invariants), [`jsonrpc`](../packages/ui/jsonrpc), [`session-persistence`](../packages/session-persistence/session-persistence) |
|
||||
| `session/disposed` | `emit` | [`packages/core/session/src/index.ts:57`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`agent-loop`](../packages/core/agent-loop), [`session-persistence`](../packages/session-persistence/session-persistence) |
|
||||
|
||||
+33
-17
@@ -38,6 +38,7 @@ flowchart TD
|
||||
pkg_fs["fs"]
|
||||
pkg_fs_local["fs-local"]
|
||||
pkg_fs_policy["fs-policy"]
|
||||
pkg_fs_sandbox["fs-sandbox"]
|
||||
pkg_tool_fs["tool-fs"]
|
||||
pkg_tool_fs_search["tool-fs-search"]
|
||||
end
|
||||
@@ -136,6 +137,7 @@ flowchart TD
|
||||
subgraph group_sandbox["packages/sandbox"]
|
||||
pkg_sandbox["sandbox"]
|
||||
pkg_sandbox_local["sandbox-local"]
|
||||
pkg_sandbox_policy["sandbox-policy"]
|
||||
end
|
||||
subgraph group_sdk["packages/sdk"]
|
||||
pkg_helper["helper"]
|
||||
@@ -163,8 +165,6 @@ flowchart TD
|
||||
pkg_session --> pkg_scope
|
||||
pkg_system_prompt --> pkg_llm
|
||||
pkg_system_prompt --> pkg_scope
|
||||
pkg_fs --> pkg_brand
|
||||
pkg_fs --> pkg_llm
|
||||
pkg_web --> pkg_llm
|
||||
pkg_sandbox --> pkg_llm
|
||||
pkg_token_meter --> pkg_llm
|
||||
@@ -175,12 +175,9 @@ flowchart TD
|
||||
pkg_agent --> pkg_session
|
||||
pkg_agent --> pkg_system_prompt
|
||||
pkg_bash --> pkg_sandbox
|
||||
pkg_bash --> pkg_session
|
||||
pkg_fs_local --> pkg_fs
|
||||
pkg_fs_policy --> pkg_fs
|
||||
pkg_skill_local --> pkg_fs
|
||||
pkg_skill_local --> pkg_home
|
||||
pkg_skill_local --> pkg_skill
|
||||
pkg_fs --> pkg_brand
|
||||
pkg_fs --> pkg_llm
|
||||
pkg_fs --> pkg_sandbox
|
||||
pkg_compact --> pkg_llm
|
||||
pkg_compact --> pkg_session
|
||||
pkg_web_fetch_local --> pkg_timeout
|
||||
@@ -196,8 +193,15 @@ flowchart TD
|
||||
pkg_llm_replay --> pkg_session
|
||||
pkg_sandbox_local --> pkg_llm
|
||||
pkg_sandbox_local --> pkg_sandbox
|
||||
pkg_sandbox_policy --> pkg_sandbox
|
||||
pkg_sandbox_policy --> pkg_session
|
||||
pkg_bash_local --> pkg_bash
|
||||
pkg_bash_local --> pkg_timeout
|
||||
pkg_fs_local --> pkg_fs
|
||||
pkg_fs_policy --> pkg_fs
|
||||
pkg_skill_local --> pkg_fs
|
||||
pkg_skill_local --> pkg_home
|
||||
pkg_skill_local --> pkg_skill
|
||||
pkg_compact_basic --> pkg_agent
|
||||
pkg_compact_basic --> pkg_compact
|
||||
pkg_compact_basic --> pkg_llm
|
||||
@@ -244,8 +248,14 @@ flowchart TD
|
||||
pkg_bash_sandbox --> pkg_bash
|
||||
pkg_bash_sandbox --> pkg_bash_local
|
||||
pkg_bash_sandbox --> pkg_sandbox
|
||||
pkg_bash_sandbox --> pkg_sandbox_policy
|
||||
pkg_fs_sandbox --> pkg_fs
|
||||
pkg_fs_sandbox --> pkg_fs_local
|
||||
pkg_fs_sandbox --> pkg_sandbox
|
||||
pkg_fs_sandbox --> pkg_sandbox_policy
|
||||
pkg_permission --> pkg_bash
|
||||
pkg_permission --> pkg_sandbox
|
||||
pkg_permission --> pkg_sandbox_policy
|
||||
pkg_permission --> pkg_session
|
||||
pkg_permission --> pkg_user_approval
|
||||
pkg_agent_loop --> pkg_agent
|
||||
@@ -260,6 +270,7 @@ flowchart TD
|
||||
pkg_tool_bash --> pkg_home
|
||||
pkg_tool_bash --> pkg_llm
|
||||
pkg_tool_bash --> pkg_sandbox
|
||||
pkg_tool_bash --> pkg_sandbox_policy
|
||||
pkg_tool_bash --> pkg_session_persistence
|
||||
pkg_tool_bash --> pkg_system_prompt
|
||||
pkg_tool_bash --> pkg_tasks
|
||||
@@ -267,9 +278,12 @@ flowchart TD
|
||||
pkg_tool_bash --> pkg_user_approval
|
||||
pkg_tool_fs --> pkg_fs
|
||||
pkg_tool_fs --> pkg_llm
|
||||
pkg_tool_fs --> pkg_sandbox
|
||||
pkg_tool_fs --> pkg_sandbox_policy
|
||||
pkg_tool_fs --> pkg_session
|
||||
pkg_tool_fs --> pkg_system_prompt
|
||||
pkg_tool_fs --> pkg_tools
|
||||
pkg_tool_fs --> pkg_user_approval
|
||||
pkg_tool_fs_search --> pkg_bash
|
||||
pkg_tool_fs_search --> pkg_llm
|
||||
pkg_tool_fs_search --> pkg_retention
|
||||
@@ -470,15 +484,12 @@ flowchart TD
|
||||
| [`llm-pi-ai`](../packages/llm/llm-pi-ai) | `llm` | [`llm`](../packages/llm/llm) |
|
||||
| [`session`](../packages/core/session) | `core` | [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
|
||||
| [`system-prompt`](../packages/core/system-prompt) | `core` | [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
|
||||
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm) |
|
||||
| [`web`](../packages/web/web) | `web` | [`llm`](../packages/llm/llm) |
|
||||
| [`sandbox`](../packages/sandbox/sandbox) | `sandbox` | [`llm`](../packages/llm/llm) |
|
||||
| [`token-meter`](../packages/llm/token-meter) | `llm` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
||||
| [`agent`](../packages/core/agent) | `core` | [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
|
||||
| [`bash`](../packages/bash/bash) | `bash` | [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
|
||||
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs) |
|
||||
| [`fs-policy`](../packages/fs/fs-policy) | `fs` | [`fs`](../packages/fs/fs) |
|
||||
| [`skill-local`](../packages/skill/skill-local) | `skill` | [`fs`](../packages/fs/fs), [`home`](../packages/util/home), [`skill`](../packages/skill/skill) |
|
||||
| [`bash`](../packages/bash/bash) | `bash` | [`sandbox`](../packages/sandbox/sandbox) |
|
||||
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
|
||||
| [`compact`](../packages/compact/compact) | `compact` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
||||
| [`web-fetch-local`](../packages/web/web-fetch-local) | `web` | [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
|
||||
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`web`](../packages/web/web) |
|
||||
@@ -488,7 +499,11 @@ flowchart TD
|
||||
| [`session-persistence`](../packages/session-persistence/session-persistence) | `session-persistence` | [`session`](../packages/core/session) |
|
||||
| [`llm-replay`](../packages/support/llm-replay) | `support` | [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
||||
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
|
||||
| [`sandbox-policy`](../packages/sandbox/sandbox-policy) | `sandbox` | [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session) |
|
||||
| [`bash-local`](../packages/bash/bash-local) | `bash` | [`bash`](../packages/bash/bash), [`timeout`](../packages/util/timeout) |
|
||||
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs) |
|
||||
| [`fs-policy`](../packages/fs/fs-policy) | `fs` | [`fs`](../packages/fs/fs) |
|
||||
| [`skill-local`](../packages/skill/skill-local) | `skill` | [`fs`](../packages/fs/fs), [`home`](../packages/util/home), [`skill`](../packages/skill/skill) |
|
||||
| [`compact-basic`](../packages/compact/compact-basic) | `compact` | [`agent`](../packages/core/agent), [`compact`](../packages/compact/compact), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
|
||||
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`spill`](../packages/spill/spill) |
|
||||
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`bash`](../packages/bash/bash), [`session`](../packages/core/session) |
|
||||
@@ -502,11 +517,12 @@ flowchart TD
|
||||
| [`tasks`](../packages/tasks/tasks) | `tasks` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
|
||||
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
|
||||
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/ui/user-approval) |
|
||||
| [`bash-sandbox`](../packages/bash/bash-sandbox) | `bash` | [`bash`](../packages/bash/bash), [`bash-local`](../packages/bash/bash-local), [`sandbox`](../packages/sandbox/sandbox) |
|
||||
| [`permission`](../packages/ui/permission) | `ui` | [`bash`](../packages/bash/bash), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`user-approval`](../packages/ui/user-approval) |
|
||||
| [`bash-sandbox`](../packages/bash/bash-sandbox) | `bash` | [`bash`](../packages/bash/bash), [`bash-local`](../packages/bash/bash-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
|
||||
| [`fs-sandbox`](../packages/fs/fs-sandbox) | `fs` | [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
|
||||
| [`permission`](../packages/ui/permission) | `ui` | [`bash`](../packages/bash/bash), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`user-approval`](../packages/ui/user-approval) |
|
||||
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`tool-bash`](../packages/bash/tool-bash) | `bash` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`home`](../packages/util/home), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
|
||||
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`tool-bash`](../packages/bash/tool-bash) | `bash` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`home`](../packages/util/home), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
|
||||
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`fs`](../packages/fs/fs), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
|
||||
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`bash`](../packages/bash/bash), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
|
||||
| [`tool-skill`](../packages/skill/tool-skill) | `skill` | [`agent`](../packages/core/agent), [`llm`](../packages/llm/llm), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
|
||||
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
|
||||
|
||||
+33
-27
@@ -169,21 +169,6 @@ Types: [ContentBlock](core-data-structures/core.md) · [TokenUsage](core-data-st
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:226`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `bash/*`
|
||||
|
||||
#### `bash/sandbox-mode` — log-only
|
||||
|
||||
```ts persistence-catalog
|
||||
/**
|
||||
* Durable log-only sandbox-mode override; never a surface event or model
|
||||
* message. Execution and ACP option reporting fold the latest event through
|
||||
* {@link effectiveSandboxMode} without adding a prompt notice.
|
||||
*/
|
||||
'bash/sandbox-mode': { mode: SandboxMode }
|
||||
```
|
||||
|
||||
Source: [`packages/bash/bash/src/session-mode.ts:20`](../packages/bash/bash/src/session-mode.ts)
|
||||
|
||||
### `compact/*`
|
||||
|
||||
#### `compact/end` — log-only
|
||||
@@ -244,21 +229,24 @@ Source: [`packages/compact/compact/src/types.ts:22`](../packages/compact/compact
|
||||
/**
|
||||
* In-session context injection (file-change notices, subdir AGENTS.md,
|
||||
* skill content, cron notifications, …). Rendered into the derived history
|
||||
* as synthetic context — NOT a user prompt. `envelope: 'raw'` lets a caller
|
||||
* own the complete model-facing frame; `meta` is durable JSON state omitted
|
||||
* from the model projection.
|
||||
* as a synthetic user-role message carrying `content` verbatim — NOT a
|
||||
* user prompt. `meta` is durable JSON state omitted from the model
|
||||
* projection; it is also the intended channel for any future framing
|
||||
* directive (a producer declares the frame, a dedicated renderer applies it —
|
||||
* see the deferred note in
|
||||
* ../../../../.agents/notes/implemented/simplification/2026-07-20-unwrap-injected-content-envelopes.md),
|
||||
* so the surface keeps projecting `content` verbatim rather than wrapping it.
|
||||
*/
|
||||
'context/message': {
|
||||
content: ContentBlock[]
|
||||
source: MessageSource
|
||||
envelope?: ContextEnvelope
|
||||
meta?: JsonValue
|
||||
}
|
||||
```
|
||||
|
||||
Types: [ContentBlock](core-data-structures/core.md) · [MessageSource](core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:212`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:213`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `hook/*`
|
||||
|
||||
@@ -320,7 +308,7 @@ Source: [`packages/hooks/hook-protocol/src/types.ts:31`](../packages/hooks/hook-
|
||||
'permission/preset': { preset: string }
|
||||
```
|
||||
|
||||
Source: [`packages/ui/permission/src/index.ts:33`](../packages/ui/permission/src/index.ts)
|
||||
Source: [`packages/ui/permission/src/index.ts:36`](../packages/ui/permission/src/index.ts)
|
||||
|
||||
### `prompt/*`
|
||||
|
||||
@@ -336,7 +324,7 @@ Source: [`packages/ui/permission/src/index.ts:33`](../packages/ui/permission/src
|
||||
|
||||
Types: [ContentBlock](core-data-structures/core.md) · [MessageSource](core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:204`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:201`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `request/*`
|
||||
|
||||
@@ -352,6 +340,24 @@ Source: [`packages/core/session/src/types.ts:204`](../packages/core/session/src/
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:251`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `sandbox/*`
|
||||
|
||||
#### `sandbox/mode` — log-only
|
||||
|
||||
```ts persistence-catalog
|
||||
/**
|
||||
* The session's sandbox mode was switched — log-only (like `approval/*`;
|
||||
* NOT a surface event, carries no `surfaceOp`): durable and replayable,
|
||||
* never in the model transcript. The LAST such event is the session's
|
||||
* override ({@link effectiveSandboxMode}); who asked for it is derivable
|
||||
* from position (an event after the log's last `request/header*` was a
|
||||
* runtime switch by the user; see the tool layer's narrator).
|
||||
*/
|
||||
'sandbox/mode': { mode: SandboxMode }
|
||||
```
|
||||
|
||||
Source: [`packages/sandbox/sandbox-policy/src/session-mode.ts:34`](../packages/sandbox/sandbox-policy/src/session-mode.ts)
|
||||
|
||||
### `steering/*`
|
||||
|
||||
#### `steering/message` — surface
|
||||
@@ -374,7 +380,7 @@ Source: [`packages/core/session/src/types.ts:244`](../packages/core/session/src/
|
||||
'step/end': { turn: number; step: number }
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:197`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:194`](../packages/core/session/src/types.ts)
|
||||
|
||||
#### `step/start` — log-only
|
||||
|
||||
@@ -383,7 +389,7 @@ Source: [`packages/core/session/src/types.ts:197`](../packages/core/session/src/
|
||||
'step/start': { turn: number; step: number }
|
||||
```
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:195`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:192`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `todo/*`
|
||||
|
||||
@@ -474,7 +480,7 @@ Source: [`packages/core/session/src/types.ts:242`](../packages/core/session/src/
|
||||
|
||||
Types: [TurnEndReason](core-data-structures/session.md)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:193`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:190`](../packages/core/session/src/types.ts)
|
||||
|
||||
#### `turn/start` — log-only
|
||||
|
||||
@@ -490,7 +496,7 @@ Source: [`packages/core/session/src/types.ts:193`](../packages/core/session/src/
|
||||
|
||||
Types: [TurnTrigger](core-data-structures/session.md)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:187`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:184`](../packages/core/session/src/types.ts)
|
||||
|
||||
### `user/*`
|
||||
|
||||
@@ -503,4 +509,4 @@ Source: [`packages/core/session/src/types.ts:187`](../packages/core/session/src/
|
||||
|
||||
Types: [ContentBlock](core-data-structures/core.md) · [MessageSource](core-data-structures/core.md)
|
||||
|
||||
Source: [`packages/core/session/src/types.ts:199`](../packages/core/session/src/types.ts)
|
||||
Source: [`packages/core/session/src/types.ts:196`](../packages/core/session/src/types.ts)
|
||||
@@ -7,7 +7,7 @@ pnpm run demo:acp # needs DEEPSEEK_API_KEY (repo-root .env or env)
|
||||
pnpm run demo:code-mode acp # the same server in Code Mode: one wire tool, run_code
|
||||
```
|
||||
|
||||
The leaf config loads the ACP app, DeepSeek adapter, sandboxed bash, approval and permission services, model-facing tools, and repeat guard. The app bundles the agent spine, JSONL persistence, and bridge, creates agents on `session/new`, and keeps stdout logger-free. [`fs.cordis.yml`](fs.cordis.yml) adds the unconfined in-process filesystem stack and local tool-result spill storage for its dedicated scenarios; [`code-mode.cordis.yml`](code-mode.cordis.yml) adds `run_code` and its generated TypeScript SDK. See [Code Mode](../../packages/core/tools/README.md#code-mode).
|
||||
The leaf config loads the ACP app, DeepSeek adapter, sandboxed bash, the sandboxed filesystem stack, approval and permission services, model-facing tools, and repeat guard. The app bundles the agent spine, JSONL persistence, and bridge, creates agents on `session/new`, and keeps stdout logger-free. [`fs.cordis.yml`](fs.cordis.yml) adds local tool-result spill storage for its dedicated scenarios; [`code-mode.cordis.yml`](code-mode.cordis.yml) adds `run_code` and its generated TypeScript SDK. See [Code Mode](../../packages/core/tools/README.md#code-mode).
|
||||
|
||||
## stdout is the protocol
|
||||
|
||||
@@ -29,7 +29,7 @@ Add to your Zed `settings.json` under `agent_servers`:
|
||||
}
|
||||
```
|
||||
|
||||
The editor sets each session's `cwd` to the project it opens, and bash uses that directory as its workdir. The current sandbox write boundary is nevertheless fixed when the server starts (`workspaceRoot: process.cwd()`), so launch the server from the workspace it should be allowed to modify; making that root session-scoped is deferred in the [sandbox Agent Note](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md). Filesystem tools are omitted from the confined default because they execute in-process and do not ride the bash sandbox.
|
||||
The editor sets each session's `cwd` to the project it opens, and bash uses that directory as its workdir. The current sandbox write boundary is nevertheless fixed when the server starts (`workspaceRoot: process.cwd()`), so launch the server from the workspace it should be allowed to modify; making that root session-scoped is deferred in the [sandbox Agent Note](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md). The filesystem tools now ride the same sandbox policy through [`@deepseek-ai/dsh-fs-sandbox`](../../packages/fs/fs-sandbox/), so `read`/`write`/`edit` are available under every mode and confined to the same `workspaceRoot`.
|
||||
|
||||
## Snapshot tests (record-once / replay-deterministic)
|
||||
|
||||
@@ -37,11 +37,11 @@ This example hosts the ACP snapshot suite. It replays through `dsh-llm-replay`,
|
||||
|
||||
## Permissions and sandboxing
|
||||
|
||||
The default tree composes [`@deepseek-ai/dsh-sandbox-local`](../../packages/sandbox/sandbox-local/), [`@deepseek-ai/dsh-bash-sandbox`](../../packages/bash/bash-sandbox/), [`@deepseek-ai/dsh-user-approval`](../../packages/ui/user-approval/), and [`@deepseek-ai/dsh-permission`](../../packages/ui/permission/). Bash starts in `workspace-write`; a denied operation returns a structured marker, and a retry with `sandbox_permissions` plus `justification` becomes a one-shot `session/request_permission` prompt in the editor. "Allow once" runs exactly that retry under the wider mode ([sandbox Agent Note § Escalation](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)).
|
||||
The default tree composes [`@deepseek-ai/dsh-sandbox-local`](../../packages/sandbox/sandbox-local/), [`@deepseek-ai/dsh-sandbox-policy`](../../packages/sandbox/sandbox-policy/), [`@deepseek-ai/dsh-bash-sandbox`](../../packages/bash/bash-sandbox/), [`@deepseek-ai/dsh-fs-sandbox`](../../packages/fs/fs-sandbox/), [`@deepseek-ai/dsh-user-approval`](../../packages/ui/user-approval/), and [`@deepseek-ai/dsh-permission`](../../packages/ui/permission/). Bash and the `read`/`write`/`edit` tools start in `workspace-write`; a denied operation returns a structured marker, and a retry with `sandbox_permissions` plus `justification` becomes a one-shot `session/request_permission` prompt in the editor. "Allow once" runs exactly that retry under the wider mode ([sandbox Agent Note § Escalation](../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)).
|
||||
|
||||
- **One session config option is live**: a capable client shows one `Permissions` select. `workspace-write` means workspace-confined bash plus `ask`; `danger-full-access` means unconfined bash plus `never`. Switching writes one `permission/preset` event through to the sandbox-mode and approval-policy events, and `session/load` reports the resumed value.
|
||||
- **Every approval is one-shot**: the choices are `Allow once` and `Reject`; a dismissal, rejection, missing editor, or unavailable runner fails closed.
|
||||
- **The boundary is bash-only and config-fixed today**: in-process filesystem tools are omitted from the confined live default, while the sandbox workspace root remains the server's launch directory.
|
||||
- **The boundary spans bash and the filesystem tools, and is config-fixed today**: bash confines through the OS runner and the `read`/`write`/`edit` tools through an in-process path fence ([`dsh-fs-sandbox`](../../packages/fs/fs-sandbox/)), both keyed to the same `workspaceRoot` — which remains the server's launch directory (a per-session root is deferred).
|
||||
|
||||
`tests/escalation.e2e.ts` boots this default tree keyless, drives the permission select, and—with a key and usable runner—proves both approval outcomes against the filesystem. Most snapshots use that tree and start at `danger-full-access` so bash fixtures remain runner-independent; scenarios that call `read`, `write`, or `edit` use the fixed full-access fs overlay and a separate request-header pin. The permission-switching and escalation inputs select `workspace-write` before exercising the bash policy path. No fixture pins a real denial because kernel error text is backend-specific; real confinement remains covered by the sandbox packages' kernel e2e suites.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Keyless replay counterpart of code-mode-workspace-context.cordis.yml. It
|
||||
# enables the filesystem entries needed by this scenario and swaps in replay.
|
||||
# Keyless replay counterpart of code-mode-workspace-context.cordis.yml. It adds
|
||||
# Code Mode to the default filesystem suite and swaps in replay.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
@@ -23,14 +23,6 @@
|
||||
|
||||
Verify your work by running the code or tests. Keep answers brief and factual.
|
||||
- insert:
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
- id: tool-fs
|
||||
name: '@deepseek-ai/dsh-tool-fs'
|
||||
- id: code-runtime
|
||||
name: '@deepseek-ai/dsh-code-runtime-worker'
|
||||
- id: llm-replay
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Code Mode workspace-context snapshot recording overlay. The scenario needs
|
||||
# filesystem tools to trigger nested instruction discovery after a read.
|
||||
# Code Mode workspace-context snapshot recording overlay. The default filesystem
|
||||
# tools trigger nested instruction discovery after a read.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
@@ -20,13 +20,5 @@
|
||||
|
||||
Verify your work by running the code or tests. Keep answers brief and factual.
|
||||
- insert:
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
- id: tool-fs
|
||||
name: '@deepseek-ai/dsh-tool-fs'
|
||||
- id: code-runtime
|
||||
name: '@deepseek-ai/dsh-code-runtime-worker'
|
||||
@@ -12,6 +12,8 @@ flowchart LR
|
||||
cfg --> plugin_acp_llm_deepseek
|
||||
plugin_acp_sandbox["sandbox<br/>@deepseek-ai/dsh-sandbox-local"]
|
||||
cfg --> plugin_acp_sandbox
|
||||
plugin_acp_sandbox_policy["sandbox-policy<br/>@deepseek-ai/dsh-sandbox-policy"]
|
||||
cfg --> plugin_acp_sandbox_policy
|
||||
plugin_acp_bash["bash<br/>@deepseek-ai/dsh-bash-sandbox"]
|
||||
cfg --> plugin_acp_bash
|
||||
plugin_acp_approval["approval<br/>@deepseek-ai/dsh-user-approval"]
|
||||
@@ -49,6 +51,12 @@ flowchart LR
|
||||
cfg --> plugin_acp_tool_todo
|
||||
plugin_acp_repeat_tool_guard["repeat-tool-guard<br/>@deepseek-ai/dsh-repeat-tool-guard"]
|
||||
cfg --> plugin_acp_repeat_tool_guard
|
||||
plugin_acp_fs_sandbox["fs-sandbox<br/>@deepseek-ai/dsh-fs-sandbox"]
|
||||
cfg --> plugin_acp_fs_sandbox
|
||||
plugin_acp_fs_policy["fs-policy<br/>@deepseek-ai/dsh-fs-policy"]
|
||||
cfg --> plugin_acp_fs_policy
|
||||
plugin_acp_tool_fs["tool-fs<br/>@deepseek-ai/dsh-tool-fs"]
|
||||
cfg --> plugin_acp_tool_fs
|
||||
plugin_acp_hooks_claude["hooks-claude<br/>@deepseek-ai/dsh-hooks-claude"]
|
||||
cfg --> plugin_acp_hooks_claude
|
||||
plugin_acp_hooks_codex["hooks-codex<br/>@deepseek-ai/dsh-hooks-codex"]
|
||||
@@ -59,6 +67,7 @@ flowchart LR
|
||||
| --- | --- |
|
||||
| `llm-deepseek` | `@deepseek-ai/dsh-llm-deepseek` |
|
||||
| `sandbox` | `@deepseek-ai/dsh-sandbox-local` |
|
||||
| `sandbox-policy` | `@deepseek-ai/dsh-sandbox-policy` |
|
||||
| `bash` | `@deepseek-ai/dsh-bash-sandbox` |
|
||||
| `approval` | `@deepseek-ai/dsh-user-approval` |
|
||||
| `permission` | `@deepseek-ai/dsh-permission` |
|
||||
@@ -74,6 +83,9 @@ flowchart LR
|
||||
| `tool-workflow` | `@deepseek-ai/dsh-tool-workflow` |
|
||||
| `tool-todo` | `@deepseek-ai/dsh-tool-todo` |
|
||||
| `repeat-tool-guard` | `@deepseek-ai/dsh-repeat-tool-guard` |
|
||||
| `fs-sandbox` | `@deepseek-ai/dsh-fs-sandbox` |
|
||||
| `fs-policy` | `@deepseek-ai/dsh-fs-policy` |
|
||||
| `tool-fs` | `@deepseek-ai/dsh-tool-fs` |
|
||||
| `hooks-claude` | `@deepseek-ai/dsh-hooks-claude` |
|
||||
| `hooks-codex` | `@deepseek-ai/dsh-hooks-codex` |
|
||||
|
||||
|
||||
@@ -10,17 +10,25 @@
|
||||
apiKey: !!js process.env.DEEPSEEK_API_KEY
|
||||
baseURL: !!js process.env.DEEPSEEK_BASE_URL
|
||||
|
||||
# The default composition confines bash to the workspace and asks before a
|
||||
# wider retry. Snapshots use danger-full-access; DSH_PERMISSION_MODE overrides
|
||||
# both mode and approval policy for deployments and tests.
|
||||
# The default composition confines bash AND the filesystem tools to the
|
||||
# workspace and asks before a wider retry. Snapshot runs select
|
||||
# danger-full-access so the established scenarios remain runner-independent;
|
||||
# DSH_PERMISSION_MODE provides the same explicit deployment/test override
|
||||
# outside the snapshot harness. The sandbox mode + workspace root live on
|
||||
# ctx.sandboxPolicy — the one home both enforcing families (bash, fs) read.
|
||||
- id: sandbox
|
||||
name: '@deepseek-ai/dsh-sandbox-local'
|
||||
|
||||
- id: sandbox-policy
|
||||
name: '@deepseek-ai/dsh-sandbox-policy'
|
||||
config:
|
||||
mode: !!js "process.env.DSH_PERMISSION_MODE ?? (process.env.DSH_SNAPSHOT === undefined ? 'workspace-write' : 'danger-full-access')"
|
||||
workspaceRoot: !!js process.cwd()
|
||||
|
||||
- id: bash
|
||||
name: '@deepseek-ai/dsh-bash-sandbox'
|
||||
config:
|
||||
timeoutMs: 60000
|
||||
mode: !!js "process.env.DSH_PERMISSION_MODE ?? (process.env.DSH_SNAPSHOT === undefined ? 'workspace-write' : 'danger-full-access')"
|
||||
workspaceRoot: !!js process.cwd()
|
||||
|
||||
- id: approval
|
||||
name: '@deepseek-ai/dsh-user-approval'
|
||||
@@ -112,6 +120,22 @@
|
||||
- id: repeat-tool-guard
|
||||
name: '@deepseek-ai/dsh-repeat-tool-guard'
|
||||
|
||||
# The filesystem stack rides the SAME sandbox policy as bash: dsh-fs-sandbox
|
||||
# replaces dsh-fs-local behind ctx.fs and fences write/edit by the effective
|
||||
# mode (read-only denies, workspace-write contains to the workspace + temp
|
||||
# roots, danger-full-access passes through), so read/write/edit are available
|
||||
# under every mode. fs-policy (read-before-edit) composes orthogonally on top.
|
||||
- id: fs-sandbox
|
||||
name: '@deepseek-ai/dsh-fs-sandbox'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
|
||||
- id: tool-fs
|
||||
name: '@deepseek-ai/dsh-tool-fs'
|
||||
|
||||
# `configPath` is read once at load and resolves from the server launch cwd, not
|
||||
# `session/new.cwd`; one `hooks.json` therefore applies to every session and a
|
||||
# project-local file is not discovered. Missing config registers nothing. Hook
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Keyless filesystem snapshots apply the filesystem and replay overlays directly
|
||||
# because include patches cannot target entries behind a nested include.
|
||||
# Keyless filesystem snapshots apply the spill and replay overlays directly
|
||||
# because include patches cannot target entries behind a nested include. The
|
||||
# sandboxed filesystem stack already lives in the base cordis.yml.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
@@ -9,14 +10,6 @@
|
||||
name: '@deepseek-ai/dsh-llm-deepseek'
|
||||
disabled: true
|
||||
- insert:
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
- id: tool-fs
|
||||
name: '@deepseek-ai/dsh-tool-fs'
|
||||
- id: spill-local
|
||||
name: '@deepseek-ai/dsh-spill-local'
|
||||
config:
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
# Filesystem snapshots need the in-process local provider, policy gate, and
|
||||
# model-facing tools. This explicit overlay is always full-access: the session
|
||||
# permission preset controls bash only and cannot confine or unmount these plugins.
|
||||
# Filesystem-scenario overlay: the sandboxed filesystem stack already lives in
|
||||
# the base cordis.yml, so this overlay adds only the local tool-result spill
|
||||
# storage those scenarios exercise.
|
||||
- id: base
|
||||
name: '@cordisjs/plugin-include'
|
||||
config:
|
||||
path: ./cordis.yml
|
||||
patches:
|
||||
- insert:
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
- id: tool-fs
|
||||
name: '@deepseek-ai/dsh-tool-fs'
|
||||
- id: spill-local
|
||||
name: '@deepseek-ai/dsh-spill-local'
|
||||
config:
|
||||
|
||||
@@ -53,24 +53,25 @@ const SCENARIOS: Scenario[] = [
|
||||
// Its prompt and tool-schema sidecars pin the composed header.
|
||||
{ name: 'text-turn', hasModelTurn: true, recorded: true, pinsHeader: true },
|
||||
{ name: 'tool-call-turn', hasModelTurn: true, recorded: true },
|
||||
// The fs overlay only adds the spill stack (the sandboxed filesystem tools
|
||||
// live in the base tree), so these scenarios share the default header class.
|
||||
{
|
||||
name: 'parallel-tool-calls',
|
||||
hasModelTurn: true,
|
||||
recorded: false,
|
||||
headerClass: 'fs',
|
||||
configPath: FS_CONFIG,
|
||||
},
|
||||
{ name: 'bash-spill', hasModelTurn: true, recorded: false, headerClass: 'fs', configPath: FS_CONFIG },
|
||||
{ name: 'bash-spill', hasModelTurn: true, recorded: false, configPath: FS_CONFIG },
|
||||
{ name: 'fs-terminal-card', hasModelTurn: true, recorded: true },
|
||||
{ name: 'todo-plan', hasModelTurn: true, recorded: true },
|
||||
{ name: 'skill-load', hasModelTurn: true, recorded: false, pinsHeader: true, headerClass: 'skill' },
|
||||
{ name: 'workspace-edit', hasModelTurn: true, recorded: true, pinsHeader: true, headerClass: 'fs', configPath: FS_CONFIG },
|
||||
{ name: 'fs-read', hasModelTurn: true, recorded: true, headerClass: 'fs', configPath: FS_CONFIG },
|
||||
{ name: 'fs-write', hasModelTurn: true, recorded: true, headerClass: 'fs', configPath: FS_CONFIG },
|
||||
{ name: 'fs-edit', hasModelTurn: true, recorded: true, headerClass: 'fs', configPath: FS_CONFIG },
|
||||
{ name: 'fs-write-overwrite', hasModelTurn: true, recorded: true, headerClass: 'fs', configPath: FS_CONFIG },
|
||||
{ name: 'fs-read-window', hasModelTurn: true, recorded: true, headerClass: 'fs', configPath: FS_CONFIG },
|
||||
{ name: 'fs-policy-reject', hasModelTurn: true, recorded: true, headerClass: 'fs', configPath: FS_CONFIG },
|
||||
{ name: 'workspace-edit', hasModelTurn: true, recorded: true },
|
||||
{ name: 'fs-read', hasModelTurn: true, recorded: true },
|
||||
{ name: 'fs-write', hasModelTurn: true, recorded: true },
|
||||
{ name: 'fs-edit', hasModelTurn: true, recorded: true },
|
||||
{ name: 'fs-write-overwrite', hasModelTurn: true, recorded: true },
|
||||
{ name: 'fs-read-window', hasModelTurn: true, recorded: true },
|
||||
{ name: 'fs-policy-reject', hasModelTurn: true, recorded: true },
|
||||
{ name: 'multi-turn', hasModelTurn: true, recorded: true },
|
||||
// ACP exposes the adapter catalog as a session-scoped model select. This
|
||||
// scenario pins the default flash request, the switch response, and the
|
||||
@@ -175,6 +176,7 @@ const SCENARIOS: Scenario[] = [
|
||||
{ name: 'permission-switching', hasModelTurn: true, recorded: true, pinsHeader: true, expectedHeaderChanges: 1, headerClass: 'sandbox' },
|
||||
{ name: 'escalation-approved', hasModelTurn: true, recorded: true, headerClass: 'sandbox' },
|
||||
{ name: 'escalation-rejected', hasModelTurn: true, recorded: true, headerClass: 'sandbox' },
|
||||
{ name: 'fs-escalation-approved', hasModelTurn: true, recorded: true, headerClass: 'sandbox' },
|
||||
]
|
||||
|
||||
defineAcpSnapshotSuite({
|
||||
|
||||
+2212
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,12 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working
|
||||
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.
|
||||
@@ -61,6 +67,30 @@ declare const tools: {
|
||||
/** The dynamic mount id returned by cordis_mount (e.g. "dyn-1"). */
|
||||
id: string;
|
||||
}): Promise<string>;
|
||||
/** Edit an existing UTF-8 text file by replacing literal text. */
|
||||
edit(args: {
|
||||
/** Path to edit, resolved by the filesystem backend. */
|
||||
file_path: string;
|
||||
/** Literal text to replace. Must match exactly. */
|
||||
old_string: string;
|
||||
/** Literal replacement text. Use an empty string to delete the match. */
|
||||
new_string: string;
|
||||
/** Replace all matches. Defaults to false; when false, old_string must appear exactly once. */
|
||||
replace_all?: boolean;
|
||||
/** The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval. */
|
||||
sandbox_permissions?: "workspace-write" | "danger-full-access";
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
/** Read a UTF-8 text file and return line-numbered content. */
|
||||
read(args: {
|
||||
/** Path to read, resolved by the filesystem backend. */
|
||||
file_path: string;
|
||||
/** 1-based first line to return. Defaults to 1. */
|
||||
offset?: number;
|
||||
/** Maximum number of lines to return. Defaults to 2000. */
|
||||
limit?: number;
|
||||
}): Promise<string>;
|
||||
/** Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill. */
|
||||
skill(args: {
|
||||
/** The exact skill name from the available skills list. */
|
||||
@@ -139,5 +169,16 @@ declare const tools: {
|
||||
/** Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {"files": [...]}). */
|
||||
args?: Record<string, unknown>;
|
||||
}): Promise<string>;
|
||||
/** Create or fully replace a UTF-8 text file. */
|
||||
write(args: {
|
||||
/** Path to write, resolved by the filesystem backend. */
|
||||
file_path: string;
|
||||
/** Full UTF-8 text content to write. */
|
||||
content: string;
|
||||
/** The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval. */
|
||||
sandbox_permissions?: "workspace-write" | "danger-full-access";
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
}
|
||||
```
|
||||
@@ -102,6 +102,72 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "edit",
|
||||
"description": "Edit an existing UTF-8 text file by replacing literal text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to edit, resolved by the filesystem backend."
|
||||
},
|
||||
"old_string": {
|
||||
"type": "string",
|
||||
"description": "Literal text to replace. Must match exactly."
|
||||
},
|
||||
"new_string": {
|
||||
"type": "string",
|
||||
"description": "Literal replacement text. Use an empty string to delete the match."
|
||||
},
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"old_string",
|
||||
"new_string"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
"description": "Read a UTF-8 text file and return line-numbered content.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to read, resolved by the filesystem backend."
|
||||
},
|
||||
"offset": {
|
||||
"type": "number",
|
||||
"description": "1-based first line to return. Defaults to 1."
|
||||
},
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of lines to return. Defaults to 2000."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "run_code",
|
||||
"description": "Execute a TypeScript program against the available tools. Write the BODY of an async function (erasable syntax only; top-level `await` and `return` work) and call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return comes back — curate it.",
|
||||
@@ -344,6 +410,39 @@
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "write",
|
||||
"description": "Create or fully replace a UTF-8 text file.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to write, resolved by the filesystem backend."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"changes": []
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{"type":"assistant/chunk","seq":8,"time":0,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":9,"time":0,"data":{"turn":1,"step":1,"content":[{"type":"tool-call","id":"call_spill","name":"bash","arguments":"{\"command\":\"node -e \\\"process.stdout.write('SPILL_START-' + 'x'.repeat(2000) + '-SPILL_END')\\\"\",\"description\":\"Print large deterministic output\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[4,5,6,7,8],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":10,"time":0,"data":{"turn":1,"step":1,"callId":"call_spill","name":"bash","arguments":"{\"command\":\"node -e \\\"process.stdout.write('SPILL_START-' + 'x'.repeat(2000) + '-SPILL_END')\\\"\",\"description\":\"Print large deterministic output\"}"}}
|
||||
{"type":"tool/result","seq":11,"time":0,"data":{"turn":1,"step":1,"callId":"call_spill","content":[{"type":"text","text":"SPILL_START-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-SPILL_END\n\n(Omitted 1417 bytes. Full formatted result stored at: /tmp/dsh-acp-snapshot-spill/session-8d519f752b89/93e1b6e8dc7e-bash.txt. Use read with offset/limit, or grep this path to search within it.)"}],"isError":false},"sourceEventSeqs":[10],"surfaceOp":"append"}
|
||||
{"type":"tool/result","seq":11,"time":0,"data":{"turn":1,"step":1,"callId":"call_spill","content":[{"type":"text","text":"SPILL_START-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-SPILL_END\n\n(Omitted 1417 bytes. Full formatted result stored at: /tmp/dsh-acp-snapshot-spill/session-2ef0a5f14624/b5e2b8c5e6a6-bash.txt. Use read with offset/limit, or grep this path to search within it.)"}],"isError":false},"sourceEventSeqs":[10],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":12,"time":0,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":13,"time":0,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":14,"time":0,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
|
||||
@@ -5,6 +5,12 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working
|
||||
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.
|
||||
@@ -44,6 +50,30 @@ declare const tools: {
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact command needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
/** Edit an existing UTF-8 text file by replacing literal text. */
|
||||
edit(args: {
|
||||
/** Path to edit, resolved by the filesystem backend. */
|
||||
file_path: string;
|
||||
/** Literal text to replace. Must match exactly. */
|
||||
old_string: string;
|
||||
/** Literal replacement text. Use an empty string to delete the match. */
|
||||
new_string: string;
|
||||
/** Replace all matches. Defaults to false; when false, old_string must appear exactly once. */
|
||||
replace_all?: boolean;
|
||||
/** The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval. */
|
||||
sandbox_permissions?: "workspace-write" | "danger-full-access";
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
/** Read a UTF-8 text file and return line-numbered content. */
|
||||
read(args: {
|
||||
/** Path to read, resolved by the filesystem backend. */
|
||||
file_path: string;
|
||||
/** 1-based first line to return. Defaults to 1. */
|
||||
offset?: number;
|
||||
/** Maximum number of lines to return. Defaults to 2000. */
|
||||
limit?: number;
|
||||
}): Promise<string>;
|
||||
/** Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill. */
|
||||
skill(args: {
|
||||
/** The exact skill name from the available skills list. */
|
||||
@@ -122,5 +152,16 @@ declare const tools: {
|
||||
/** Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {"files": [...]}). */
|
||||
args?: Record<string, unknown>;
|
||||
}): Promise<string>;
|
||||
/** Create or fully replace a UTF-8 text file. */
|
||||
write(args: {
|
||||
/** Path to write, resolved by the filesystem backend. */
|
||||
file_path: string;
|
||||
/** Full UTF-8 text content to write. */
|
||||
content: string;
|
||||
/** The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval. */
|
||||
sandbox_permissions?: "workspace-write" | "danger-full-access";
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
}
|
||||
```
|
||||
@@ -45,6 +45,72 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "edit",
|
||||
"description": "Edit an existing UTF-8 text file by replacing literal text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to edit, resolved by the filesystem backend."
|
||||
},
|
||||
"old_string": {
|
||||
"type": "string",
|
||||
"description": "Literal text to replace. Must match exactly."
|
||||
},
|
||||
"new_string": {
|
||||
"type": "string",
|
||||
"description": "Literal replacement text. Use an empty string to delete the match."
|
||||
},
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"old_string",
|
||||
"new_string"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
"description": "Read a UTF-8 text file and return line-numbered content.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to read, resolved by the filesystem backend."
|
||||
},
|
||||
"offset": {
|
||||
"type": "number",
|
||||
"description": "1-based first line to return. Defaults to 1."
|
||||
},
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of lines to return. Defaults to 2000."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "run_code",
|
||||
"description": "Execute a TypeScript program against the available tools. Write the BODY of an async function (erasable syntax only; top-level `await` and `return` work) and call tools as `await tools.name(args)` per the declarations in the system prompt. Only what you print or return comes back — curate it.",
|
||||
@@ -287,6 +353,39 @@
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "write",
|
||||
"description": "Create or fully replace a UTF-8 text file.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to write, resolved by the filesystem backend."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"changes": []
|
||||
|
||||
@@ -5,6 +5,12 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working
|
||||
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.
|
||||
@@ -44,6 +50,30 @@ declare const tools: {
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact command needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
/** Edit an existing UTF-8 text file by replacing literal text. */
|
||||
edit(args: {
|
||||
/** Path to edit, resolved by the filesystem backend. */
|
||||
file_path: string;
|
||||
/** Literal text to replace. Must match exactly. */
|
||||
old_string: string;
|
||||
/** Literal replacement text. Use an empty string to delete the match. */
|
||||
new_string: string;
|
||||
/** Replace all matches. Defaults to false; when false, old_string must appear exactly once. */
|
||||
replace_all?: boolean;
|
||||
/** The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval. */
|
||||
sandbox_permissions?: "workspace-write" | "danger-full-access";
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
/** Read a UTF-8 text file and return line-numbered content. */
|
||||
read(args: {
|
||||
/** Path to read, resolved by the filesystem backend. */
|
||||
file_path: string;
|
||||
/** 1-based first line to return. Defaults to 1. */
|
||||
offset?: number;
|
||||
/** Maximum number of lines to return. Defaults to 2000. */
|
||||
limit?: number;
|
||||
}): Promise<string>;
|
||||
/** Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill. */
|
||||
skill(args: {
|
||||
/** The exact skill name from the available skills list. */
|
||||
@@ -122,5 +152,16 @@ declare const tools: {
|
||||
/** Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {"files": [...]}). */
|
||||
args?: Record<string, unknown>;
|
||||
}): Promise<string>;
|
||||
/** Create or fully replace a UTF-8 text file. */
|
||||
write(args: {
|
||||
/** Path to write, resolved by the filesystem backend. */
|
||||
file_path: string;
|
||||
/** Full UTF-8 text content to write. */
|
||||
content: string;
|
||||
/** The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval. */
|
||||
sandbox_permissions?: "workspace-write" | "danger-full-access";
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
}
|
||||
```
|
||||
@@ -86,7 +86,7 @@
|
||||
{"type":"tool/call","seq":84,"time":1783921767208,"data":{"turn":1,"step":1,"callId":"call_00_6APApmaKLRDlXKMdIcWL5139","name":"run_code","arguments":"{\"code\": \"const content = await tools.read({ file_path: \\\"nested/task.txt\\\" });\\nreturn content;\"}"}}
|
||||
{"type":"tool/code-dispatch","seq":85,"time":1783921767270,"data":{"parentCallId":"call_00_6APApmaKLRDlXKMdIcWL5139","subCallId":"call_00_6APApmaKLRDlXKMdIcWL5139:code:1","name":"read","arguments":{"file_path":"nested/task.txt"},"isError":false,"resultSummary":"<path>./nested/task.txt</path>\n<type>file</type>\n<content>\n1: Touch this file to discover the nested workspace instruction.\n\n(End of file - total 1 lines)\n</content>"}}
|
||||
{"type":"tool/result","seq":86,"time":1783921767271,"data":{"turn":1,"step":1,"callId":"call_00_6APApmaKLRDlXKMdIcWL5139","content":[{"type":"text","text":"<path>/var/folders/_g/59jgff8x2gqd39f5vy1wnbfc0000gn/T/acp-snap-cwd-uorU26/nested/task.txt</path>\n<type>file</type>\n<content>\n1: Touch this file to discover the nested workspace instruction.\n\n(End of file - total 1 lines)\n</content>"}],"isError":false,"meta":{"logs":[]}},"sourceEventSeqs":[84],"surfaceOp":"append"}
|
||||
{"type":"context/message","seq":87,"time":1783921767272,"data":{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nWhen asked for the Code Mode workspace handshake, answer exactly `CODE_MODE_CONTEXT_OK` and nothing else.\n\n</system-reminder>"}],"source":{"kind":"plugin","plugin":"workspace-context"},"envelope":"raw","meta":{"kind":"workspace-instructions","version":1,"changes":[{"action":"set","scope":"nested","path":"nested/AGENTS.md","digest":"ae22936ed26dc76b7107005ed6d5e2482a88668a"}]}},"surfaceOp":"append"}
|
||||
{"type":"context/message","seq":87,"time":1783921767272,"data":{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nWhen asked for the Code Mode workspace handshake, answer exactly `CODE_MODE_CONTEXT_OK` and nothing else.\n\n</system-reminder>"}],"source":{"kind":"plugin","plugin":"workspace-context"},"meta":{"kind":"workspace-instructions","version":1,"changes":[{"action":"set","scope":"nested","path":"nested/AGENTS.md","digest":"ae22936ed26dc76b7107005ed6d5e2482a88668a"}]}},"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":88,"time":1783921767272,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":89,"time":1783921767272,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":90,"time":1783921768339,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
|
||||
+8
@@ -60,6 +60,10 @@ declare const tools: {
|
||||
new_string: string;
|
||||
/** Replace all matches. Defaults to false; when false, old_string must appear exactly once. */
|
||||
replace_all?: boolean;
|
||||
/** The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval. */
|
||||
sandbox_permissions?: "workspace-write" | "danger-full-access";
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
/** Read a UTF-8 text file and return line-numbered content. */
|
||||
read(args: {
|
||||
@@ -154,6 +158,10 @@ declare const tools: {
|
||||
file_path: string;
|
||||
/** Full UTF-8 text content to write. */
|
||||
content: string;
|
||||
/** The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval. */
|
||||
sandbox_permissions?: "workspace-write" | "danger-full-access";
|
||||
/** Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access. */
|
||||
justification?: string;
|
||||
}): Promise<string>;
|
||||
}
|
||||
```
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,7 +1,7 @@
|
||||
{"type":"session","version":0,"id":"f3cbd087-fb45-4b32-b0f2-3082d65bfcb4","createdAt":1783860675270,"cwd":"/var/folders/2g/b32ct0qn1d728l_v6tdkjytr0000gn/T/acp-snap-cwd-cbBLh2"}
|
||||
{"type":"turn/start","seq":0,"time":1783860675271,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"permission/preset","seq":1,"time":1783962245380,"data":{"preset":"workspace-write"}}
|
||||
{"type":"bash/sandbox-mode","seq":2,"time":1783962245380,"data":{"mode":"workspace-write"}}
|
||||
{"type":"sandbox/mode","seq":2,"time":1784518116517,"data":{"mode":"workspace-write"}}
|
||||
{"type":"approval/policy","seq":3,"time":1783962245380,"data":{"policy":"ask"}}
|
||||
{"type":"user/message","seq":4,"time":1783962245380,"data":{"content":[{"type":"text","text":"The sandbox already denied writing /tmp/dsh-escalated.txt earlier (it is outside this workspace). Retry it now exactly once: one single bash call with the command printf 'escalated\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt, with sandbox_permissions set to danger-full-access and the justification 'the user asked to write a file outside the workspace'. Do not run it without sandbox_permissions first. I will approve the permission prompt. After the result, reply with the single word DONE and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":5,"time":1783962245382,"data":{"turn":1,"step":1}}
|
||||
@@ -131,8 +131,8 @@
|
||||
{"type":"assistant/chunk","seq":129,"time":1783962245385,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":130,"time":1783962245385,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to run a command with sandbox_permissions set to danger-full-access, no prior run needed, justified as instructed."},{"type":"tool-call","id":"call_00_d0sAHpJ9mYOJi0z7KNy30441","name":"bash","arguments":"{\"command\": \"printf 'escalated\\\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt\", \"description\": \"Write file outside workspace and verify\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to write a file outside the workspace\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":1501,"outputTokens":174,"cacheReadTokens":0,"reasoningTokens":28}},"sourceEventSeqs":[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,94,95,96,97,98,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],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":131,"time":1783962245385,"data":{"turn":1,"step":1,"callId":"call_00_d0sAHpJ9mYOJi0z7KNy30441","name":"bash","arguments":"{\"command\": \"printf 'escalated\\\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt\", \"description\": \"Write file outside workspace and verify\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to write a file outside the workspace\"}"}}
|
||||
{"type":"approval/asked","seq":132,"time":1783962245386,"data":{"id":"4ba41d82-153a-4587-8c22-dd35783c3d87","toolName":"bash","callId":"call_00_d0sAHpJ9mYOJi0z7KNy30441","reason":"escalate sandbox to danger-full-access: the user asked to write a file outside the workspace"}}
|
||||
{"type":"approval/decided","seq":133,"time":1783962245387,"data":{"id":"4ba41d82-153a-4587-8c22-dd35783c3d87","outcome":"allowed-once"}}
|
||||
{"type":"approval/asked","seq":132,"time":1783962245386,"data":{"id":"e2d45b4a-ff48-488d-aeb6-8edc9dc5c3de","toolName":"bash","callId":"call_00_d0sAHpJ9mYOJi0z7KNy30441","reason":"escalate sandbox to danger-full-access: the user asked to write a file outside the workspace"}}
|
||||
{"type":"approval/decided","seq":133,"time":1783962245387,"data":{"id":"e2d45b4a-ff48-488d-aeb6-8edc9dc5c3de","outcome":"allowed-once"}}
|
||||
{"type":"tool/result","seq":134,"time":1783962245399,"data":{"turn":1,"step":1,"callId":"call_00_d0sAHpJ9mYOJi0z7KNy30441","content":[{"type":"text","text":"escalated\n"}],"isError":false},"sourceEventSeqs":[131],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":135,"time":1783962245400,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":136,"time":1783962245400,"data":{"turn":1,"step":2}}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{"type":"session","version":0,"id":"d692fe7f-7079-4ee4-8b06-f44fd026d4ea","createdAt":1783860679475,"cwd":"/var/folders/2g/b32ct0qn1d728l_v6tdkjytr0000gn/T/acp-snap-cwd-Hn29Od"}
|
||||
{"type":"turn/start","seq":0,"time":1783860679476,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"permission/preset","seq":1,"time":1783962246267,"data":{"preset":"workspace-write"}}
|
||||
{"type":"bash/sandbox-mode","seq":2,"time":1783962246267,"data":{"mode":"workspace-write"}}
|
||||
{"type":"sandbox/mode","seq":2,"time":1784518117237,"data":{"mode":"workspace-write"}}
|
||||
{"type":"approval/policy","seq":3,"time":1783962246267,"data":{"policy":"ask"}}
|
||||
{"type":"user/message","seq":4,"time":1783962246267,"data":{"content":[{"type":"text","text":"The sandbox already denied writing /tmp/dsh-escalated.txt earlier (it is outside this workspace). Retry it now exactly once: one single bash call with the command printf 'escalated\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt, with sandbox_permissions set to danger-full-access and the justification 'the user asked to write a file outside the workspace'. Do not run it without sandbox_permissions first. I will reject the permission prompt; after the rejection, do not retry and do not work around it — explain in one short sentence and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":5,"time":1783962246269,"data":{"turn":1,"step":1}}
|
||||
@@ -155,8 +155,8 @@
|
||||
{"type":"assistant/chunk","seq":153,"time":1783962246274,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":154,"time":1783962246274,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to run a specific command with `sandbox_permissions` set to `danger-full-access` and a specific justification. They explicitly said NOT to run it without sandbox_permissions first. Let me do exactly that."},{"type":"tool-call","id":"call_00_WB1vnPomi8yr6MlcFKTj7912","name":"bash","arguments":"{\"command\": \"printf 'escalated\\\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt\", \"description\": \"Write to /tmp and verify, then clean up\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to write a file outside the workspace\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":1509,"outputTokens":198,"cacheReadTokens":0,"reasoningTokens":48}},"sourceEventSeqs":[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,94,95,96,97,98,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],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":155,"time":1783962246274,"data":{"turn":1,"step":1,"callId":"call_00_WB1vnPomi8yr6MlcFKTj7912","name":"bash","arguments":"{\"command\": \"printf 'escalated\\\\n' > /tmp/dsh-escalated.txt && cat /tmp/dsh-escalated.txt && rm /tmp/dsh-escalated.txt\", \"description\": \"Write to /tmp and verify, then clean up\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to write a file outside the workspace\"}"}}
|
||||
{"type":"approval/asked","seq":156,"time":1783962246275,"data":{"id":"84c78b9b-8955-4d4f-8dad-824dcac4d9ed","toolName":"bash","callId":"call_00_WB1vnPomi8yr6MlcFKTj7912","reason":"escalate sandbox to danger-full-access: the user asked to write a file outside the workspace"}}
|
||||
{"type":"approval/decided","seq":157,"time":1783962246275,"data":{"id":"84c78b9b-8955-4d4f-8dad-824dcac4d9ed","outcome":"rejected"}}
|
||||
{"type":"approval/asked","seq":156,"time":1783962246275,"data":{"id":"46c8dba4-52c9-4a6a-b6a6-5f34c95c28df","toolName":"bash","callId":"call_00_WB1vnPomi8yr6MlcFKTj7912","reason":"escalate sandbox to danger-full-access: the user asked to write a file outside the workspace"}}
|
||||
{"type":"approval/decided","seq":157,"time":1783962246275,"data":{"id":"46c8dba4-52c9-4a6a-b6a6-5f34c95c28df","outcome":"rejected"}}
|
||||
{"type":"tool/result","seq":158,"time":1783962246275,"data":{"turn":1,"step":1,"callId":"call_00_WB1vnPomi8yr6MlcFKTj7912","content":[{"type":"text","text":"Error: the user rejected escalating this command to \"danger-full-access\""}],"isError":true},"sourceEventSeqs":[155],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":159,"time":1783962246276,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":160,"time":1783962246276,"data":{"turn":1,"step":2}}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"steps": [
|
||||
{ "op": "initialize" },
|
||||
{ "op": "newSession" },
|
||||
{ "op": "setConfigOption", "configId": "permission", "value": "workspace-write" },
|
||||
{ "op": "prompt", "text": "Use the write tool (NOT bash) to create escalated.md in the current directory containing exactly the single line: escalated. An equivalent write was denied earlier, so make this one single write call with sandbox_permissions set to danger-full-access and the justification 'the user asked to escalate this write'. Do not call write without sandbox_permissions first. I will approve the permission prompt. After the result, reply with exactly the single word DONE." }
|
||||
],
|
||||
"permissionAnswers": [
|
||||
{ "kind": "allow_once" }
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
{"type":"session","version":0,"id":"977a4820-f609-4b48-9039-adcdd921c5fe","createdAt":1784045702340,"cwd":"/var/folders/2g/b32ct0qn1d728l_v6tdkjytr0000gn/T/acp-snap-cwd-vmEGzd"}
|
||||
{"type":"turn/start","seq":0,"time":1784045702342,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"permission/preset","seq":1,"time":1784045702343,"data":{"preset":"workspace-write"}}
|
||||
{"type":"sandbox/mode","seq":2,"time":1784045702343,"data":{"mode":"workspace-write"}}
|
||||
{"type":"approval/policy","seq":3,"time":1784045702343,"data":{"policy":"ask"}}
|
||||
{"type":"user/message","seq":4,"time":1784045702343,"data":{"content":[{"type":"text","text":"Use the write tool (NOT bash) to create escalated.md in the current directory containing exactly the single line: escalated. An equivalent write was denied earlier, so make this one single write call with sandbox_permissions set to danger-full-access and the justification 'the user asked to escalate this write'. Do not call write without sandbox_permissions first. I will approve the permission prompt. After the result, reply with exactly the single word DONE."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":5,"time":1784045702345,"data":{"turn":1,"step":1}}
|
||||
{"type":"request/header","seq":6,"time":1784045702345,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1784045703046,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1784045703046,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1784045703162,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1784045703172,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1784045703172,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1784045703173,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1784045703173,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" create"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1784045703173,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1784045703173,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" file"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1784045703199,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" using"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1784045703225,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1784045703251,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" write"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1784045703252,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" tool"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1784045703252,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1784045703252,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" sand"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1784045703252,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"box"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1784045703252,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"_per"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1784045703277,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"missions"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1784045703278,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1784045703278,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" Let"}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1784045703278,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1784045703278,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" do"}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":1784045703304,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" that"}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":1784045703304,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":31,"time":1784045703356,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"tool-call"}}}
|
||||
{"type":"assistant/chunk","seq":32,"time":1784045703356,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":""}}}
|
||||
{"type":"assistant/chunk","seq":33,"time":1784045703381,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"{"}}}
|
||||
{"type":"assistant/chunk","seq":34,"time":1784045703381,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1784045703381,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"file"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1784045703405,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"_path"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1784045703406,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1784045703406,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1784045703406,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1784045703431,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"es"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1784045703432,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"cal"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1784045703432,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"ated"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1784045703432,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":".md"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1784045703432,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1784045703483,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1784045703483,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1784045703483,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"content"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1784045703483,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1784045703483,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1784045703509,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1784045703509,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"es"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1784045703509,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"cal"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1784045703509,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"ated"}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1784045703509,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1784045703565,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1784045703566,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1784045703566,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"sand"}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1784045703566,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"box"}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1784045703566,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"_per"}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1784045703566,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"missions"}}}
|
||||
{"type":"assistant/chunk","seq":61,"time":1784045703566,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":1784045703591,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":1784045703591,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":64,"time":1784045703591,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"danger"}}}
|
||||
{"type":"assistant/chunk","seq":65,"time":1784045703591,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"-full"}}}
|
||||
{"type":"assistant/chunk","seq":66,"time":1784045703617,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"-access"}}}
|
||||
{"type":"assistant/chunk","seq":67,"time":1784045703618,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":68,"time":1784045703644,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":", "}}}
|
||||
{"type":"assistant/chunk","seq":69,"time":1784045703645,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":1784045703645,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"just"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":1784045703645,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"ification"}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":1784045703669,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":1784045703669,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":": "}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":1784045703669,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":1784045703669,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"the"}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":1784045703696,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":" user"}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":1784045703696,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":" asked"}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":1784045703696,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":" to"}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":1784045703696,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":" escalate"}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":1784045703724,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":" this"}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":1784045703724,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":" write"}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":1784045703724,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"\""}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":1784045703749,"data":{"turn":1,"step":1,"chunk":{"type":"tool-call-delta","index":1,"id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","argumentsDelta":"}"}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":1784045703776,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to create a file using the write tool with sandbox_permissions. Let me do that."}}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":1784045703776,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"tool-call","id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","arguments":"{\"file_path\": \"escalated.md\", \"content\": \"escalated\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to escalate this write\"}"}}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":1784045703776,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3871,"outputTokens":132,"cacheReadTokens":0,"reasoningTokens":23}}}}
|
||||
{"type":"assistant/chunk","seq":87,"time":1784045703776,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"tool-calls"}}}}
|
||||
{"type":"assistant/message","seq":88,"time":1784045703780,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to create a file using the write tool with sandbox_permissions. Let me do that."},{"type":"tool-call","id":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","arguments":"{\"file_path\": \"escalated.md\", \"content\": \"escalated\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to escalate this write\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3871,"outputTokens":132,"cacheReadTokens":0,"reasoningTokens":23}},"sourceEventSeqs":[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],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":89,"time":1784045703780,"data":{"turn":1,"step":1,"callId":"call_00_Fnymmavpr4klMDy4Fdej3227","name":"write","arguments":"{\"file_path\": \"escalated.md\", \"content\": \"escalated\", \"sandbox_permissions\": \"danger-full-access\", \"justification\": \"the user asked to escalate this write\"}"}}
|
||||
{"type":"approval/asked","seq":90,"time":1784045703782,"data":{"id":"c37500b3-c252-4a9b-ad0d-9c4349419b30","toolName":"write","callId":"call_00_Fnymmavpr4klMDy4Fdej3227","reason":"escalate sandbox to danger-full-access: the user asked to escalate this write"}}
|
||||
{"type":"approval/decided","seq":91,"time":1784045703786,"data":{"id":"c37500b3-c252-4a9b-ad0d-9c4349419b30","outcome":"allowed-once"}}
|
||||
{"type":"tool/result","seq":92,"time":1784045703798,"data":{"turn":1,"step":1,"callId":"call_00_Fnymmavpr4klMDy4Fdej3227","content":[{"type":"text","text":"<path>/var/folders/2g/b32ct0qn1d728l_v6tdkjytr0000gn/T/acp-snap-cwd-vmEGzd/escalated.md</path>\n<type>file</type>\n<content>\nCreated file\n</content>"}],"isError":false},"sourceEventSeqs":[89],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":93,"time":1784045703798,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":94,"time":1784045703799,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":95,"time":1784045704512,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":96,"time":1784045704512,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":97,"time":1784045704620,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" file"}}}
|
||||
{"type":"assistant/chunk","seq":98,"time":1784045704645,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" was"}}}
|
||||
{"type":"assistant/chunk","seq":99,"time":1784045704646,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" created"}}}
|
||||
{"type":"assistant/chunk","seq":100,"time":1784045704646,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" successfully"}}}
|
||||
{"type":"assistant/chunk","seq":101,"time":1784045704646,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":102,"time":1784045704646,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" The"}}}
|
||||
{"type":"assistant/chunk","seq":103,"time":1784045704646,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":104,"time":1784045704672,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" asked"}}}
|
||||
{"type":"assistant/chunk","seq":105,"time":1784045704673,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":106,"time":1784045704673,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":107,"time":1784045704673,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":108,"time":1784045704699,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":109,"time":1784045704699,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" exactly"}}}
|
||||
{"type":"assistant/chunk","seq":110,"time":1784045704699,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":111,"time":1784045704726,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":112,"time":1784045704726,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":113,"time":1784045704726,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" D"}}}
|
||||
{"type":"assistant/chunk","seq":114,"time":1784045704754,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":115,"time":1784045704754,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":116,"time":1784045704754,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":117,"time":1784045704754,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"D"}}}
|
||||
{"type":"assistant/chunk","seq":118,"time":1784045704754,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ONE"}}}
|
||||
{"type":"assistant/chunk","seq":119,"time":1784045704755,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The file was created successfully. The user asked me to reply with exactly the single word DONE."}}}}
|
||||
{"type":"assistant/chunk","seq":120,"time":1784045704755,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"DONE"}}}}
|
||||
{"type":"assistant/chunk","seq":121,"time":1784045704755,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":107,"outputTokens":23,"cacheReadTokens":3968,"reasoningTokens":20}}}}
|
||||
{"type":"assistant/chunk","seq":122,"time":1784045704755,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":123,"time":1784045704755,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The file was created successfully. The user asked me to reply with exactly the single word DONE."},{"type":"text","text":"DONE"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":107,"outputTokens":23,"cacheReadTokens":3968,"reasoningTokens":20}},"sourceEventSeqs":[95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":124,"time":1784045704755,"data":{"turn":1,"step":2}}
|
||||
{"type":"turn/end","seq":125,"time":1784045704756,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -0,0 +1,52 @@
|
||||
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1,"agentInfo":{"name":"deepseek-harness-acp","version":"0.0.1"},"agentCapabilities":{"loadSession":true,"promptCapabilities":{"image":false,"audio":false,"embeddedContext":false}},"authMethods":[]}}
|
||||
{"jsonrpc":"2.0","id":2,"result":{"sessionId":"{{sessionId}}","configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"danger-full-access","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
|
||||
{"jsonrpc":"2.0","id":3,"result":{"configOptions":[{"id":"model","name":"Model","description":"Sets this session's provider and model.","category":"model","type":"select","currentValue":"[\"deepseek\",\"deepseek-v4-flash\"]","options":[{"value":"[\"deepseek\",\"deepseek-v4-flash\"]","name":"deepseek-v4-flash"},{"value":"[\"deepseek\",\"deepseek-v4-pro\"]","name":"deepseek-v4-pro"}]},{"id":"permission","name":"Permissions","description":"The session permission preset: each choice bundles a sandbox mode and an approval policy.","category":"mode","type":"select","currentValue":"workspace-write","options":[{"value":"workspace-write","name":"workspace-write","description":"Write inside the workspace and permitted temporary directories; wider retries require approval."},{"value":"danger-full-access","name":"danger-full-access","description":"Full file access without approval prompts."}]}]}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" me"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" create"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" a"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" file"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" using"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" write"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" tool"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" sand"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"box"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"_per"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"missions"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" Let"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" me"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" do"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" that"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call","toolCallId":"call_00_Fnymmavpr4klMDy4Fdej3227","title":"Write escalated.md","kind":"edit","status":"in_progress","locations":[{"path":"escalated.md"}],"content":[{"type":"diff","path":"escalated.md","oldText":null,"newText":"escalated"}]}}}
|
||||
{"jsonrpc":"2.0","id":1,"method":"session/request_permission","params":{"sessionId":"{{sessionId}}","toolCall":{"toolCallId":"call_00_Fnymmavpr4klMDy4Fdej3227"},"options":[{"optionId":"allow-once","name":"Allow once","kind":"allow_once"},{"optionId":"reject-once","name":"Reject","kind":"reject_once"}]}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"tool_call_update","toolCallId":"call_00_Fnymmavpr4klMDy4Fdej3227","status":"completed","content":[{"type":"diff","path":"escalated.md","oldText":null,"newText":"escalated"}],"title":"Write escalated.md"}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" file"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" was"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" created"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" successfully"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" The"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" asked"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" me"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" exactly"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" single"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" word"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" D"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"ONE"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"D"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"ONE"}}}}
|
||||
{"jsonrpc":"2.0","id":4,"result":{"stopReason":"end_turn"}}
|
||||
@@ -55,8 +55,8 @@
|
||||
{"type":"tool/call","seq":53,"time":1783352172557,"data":{"turn":1,"step":1,"callId":"call_00_6k0oGSliVHxGSgqBmMEO4311","name":"bash","arguments":"{\"command\": \"echo HELLO\", \"description\": \"Echo HELLO\"}"}}
|
||||
{"type":"hook/invoked","seq":54,"time":1783352172558,"data":{"turn":1,"point":"PreToolUse","dialect":"claude","handlerId":"claude:PreToolUse:1","matcher":"bash"}}
|
||||
{"type":"hook/result","seq":55,"time":1783352172573,"data":{"turn":1,"point":"PreToolUse","handlerId":"claude:PreToolUse:1","decision":"ask","exitCode":0,"durationMs":14.113374999999905}}
|
||||
{"type":"approval/asked","seq":56,"time":1783962235813,"data":{"id":"9ba4bf87-d8b5-448f-94e6-6a8a0e43a8ad","toolName":"bash","callId":"call_00_6k0oGSliVHxGSgqBmMEO4311","reason":"bash requires manual approval in this session"}}
|
||||
{"type":"approval/decided","seq":57,"time":1783962235813,"data":{"id":"9ba4bf87-d8b5-448f-94e6-6a8a0e43a8ad","outcome":"rejected"}}
|
||||
{"type":"approval/asked","seq":56,"time":1783962235813,"data":{"id":"68f1e09d-f3e5-4e39-8a51-da082ba3ba99","toolName":"bash","callId":"call_00_6k0oGSliVHxGSgqBmMEO4311","reason":"bash requires manual approval in this session"}}
|
||||
{"type":"approval/decided","seq":57,"time":1783962235813,"data":{"id":"68f1e09d-f3e5-4e39-8a51-da082ba3ba99","outcome":"rejected"}}
|
||||
{"type":"tool/result","seq":58,"time":1783962235814,"data":{"turn":1,"step":1,"callId":"call_00_6k0oGSliVHxGSgqBmMEO4311","content":[{"type":"text","text":"Error: the user rejected tool \"bash\""}],"isError":true},"sourceEventSeqs":[53],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":59,"time":1783962235814,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":60,"time":1783962235814,"data":{"turn":1,"step":2}}
|
||||
|
||||
@@ -1,192 +1,67 @@
|
||||
{"type":"session","version":0,"id":"02bb4dcf-ffd6-4111-909b-504c7006d821","createdAt":1783352203365,"cwd":"/tmp/acp-snap-cwd-7YNbji"}
|
||||
{"type":"turn/start","seq":0,"time":1783352203369,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1783352203370,"data":{"content":[{"type":"text","text":"Reply with the single word FIRST and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":2,"time":1783352203371,"data":{"turn":1,"step":1}}
|
||||
{"type":"request/header","seq":3,"time":1783352203372,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"assistant/chunk","seq":4,"time":1783352204036,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":5,"time":1783352204036,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1783352204247,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1783352204282,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1783352204283,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783352204283,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783352204283,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783352204283,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1783352204284,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1783352204317,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1783352204318,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1783352204318,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1783352204318,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1783352204318,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1783352204318,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1783352204353,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1783352204353,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1783352204353,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1783352204353,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1783352204353,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1783352204353,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1783352204393,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with the single word \"FIRST\" and stop."}}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1783352204393,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"FIRST"}}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1783352204393,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":2862,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":17}}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1783352204393,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":29,"time":1783352204396,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with the single word \"FIRST\" and stop."},{"type":"text","text":"FIRST"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":2862,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":17}},"sourceEventSeqs":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":30,"time":1783352204396,"data":{"turn":1,"step":1}}
|
||||
{"type":"hook/invoked","seq":31,"time":1783352204396,"data":{"turn":1,"point":"Stop","dialect":"claude","handlerId":"claude:Stop:1"}}
|
||||
{"type":"hook/result","seq":32,"time":1783352204443,"data":{"turn":1,"point":"Stop","handlerId":"claude:Stop:1","decision":"block","exitCode":2,"stderrSummary":"Also reply with the single word SECOND, then stop.","durationMs":47.01462700000047}}
|
||||
{"type":"steering/message","seq":33,"time":1783352204444,"data":{"turn":1,"content":[{"type":"text","text":"Also reply with the single word SECOND, then stop."}],"source":{"kind":"plugin","plugin":"hooks-claude"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":34,"time":1783352204444,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1783352204945,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1783352204946,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1783352205054,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1783352205086,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1783352205087,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1783352205087,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1783352205087,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1783352205115,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1783352205115,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1783352205116,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1783352205116,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1783352205116,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1783352205143,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" only"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1783352205172,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1783352205172,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" but"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1783352205172,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" there"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1783352205200,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"'s"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1783352205201,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1783352205201,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" steering"}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1783352205201,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" input"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1783352205231,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" telling"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1783352205232,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1783352205232,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1783352205232,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" also"}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1783352205266,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1783352205266,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":61,"time":1783352205266,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":62,"time":1783352205266,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"SEC"}}}
|
||||
{"type":"assistant/chunk","seq":63,"time":1783352205266,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"OND"}}}
|
||||
{"type":"assistant/chunk","seq":64,"time":1783352205267,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\"."}}}
|
||||
{"type":"assistant/chunk","seq":65,"time":1783352205288,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" However"}}}
|
||||
{"type":"assistant/chunk","seq":66,"time":1783352205318,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":67,"time":1783352205319,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":68,"time":1783352205319,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":69,"time":1783352205319,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"'s"}}}
|
||||
{"type":"assistant/chunk","seq":70,"time":1783352205319,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" explicit"}}}
|
||||
{"type":"assistant/chunk","seq":71,"time":1783352205359,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" instruction"}}}
|
||||
{"type":"assistant/chunk","seq":72,"time":1783352205359,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" is"}}}
|
||||
{"type":"assistant/chunk","seq":73,"time":1783352205373,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":74,"time":1783352205373,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":75,"time":1783352205373,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":76,"time":1783352205373,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":77,"time":1783352205401,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":78,"time":1783352205402,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":79,"time":1783352205402,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" FIRST"}}}
|
||||
{"type":"assistant/chunk","seq":80,"time":1783352205430,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":81,"time":1783352205430,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":82,"time":1783352205431,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":83,"time":1783352205431,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" The"}}}
|
||||
{"type":"assistant/chunk","seq":84,"time":1783352205431,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" steering"}}}
|
||||
{"type":"assistant/chunk","seq":85,"time":1783352205431,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" is"}}}
|
||||
{"type":"assistant/chunk","seq":86,"time":1783352205457,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" from"}}}
|
||||
{"type":"assistant/chunk","seq":87,"time":1783352205457,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":88,"time":1783352205458,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" plugin"}}}
|
||||
{"type":"assistant/chunk","seq":89,"time":1783352205485,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":90,"time":1783352205486,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" not"}}}
|
||||
{"type":"assistant/chunk","seq":91,"time":1783352205514,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":92,"time":1783352205515,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":93,"time":1783352205515,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" themselves"}}}
|
||||
{"type":"assistant/chunk","seq":94,"time":1783352205542,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":95,"time":1783352205542,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":96,"time":1783352205542,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" should"}}}
|
||||
{"type":"assistant/chunk","seq":97,"time":1783352205543,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" follow"}}}
|
||||
{"type":"assistant/chunk","seq":98,"time":1783352205543,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":99,"time":1783352205543,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":100,"time":1783352205571,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"'s"}}}
|
||||
{"type":"assistant/chunk","seq":101,"time":1783352205571,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" original"}}}
|
||||
{"type":"assistant/chunk","seq":102,"time":1783352205601,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" instruction"}}}
|
||||
{"type":"assistant/chunk","seq":103,"time":1783352205601,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":".\n\n"}}}
|
||||
{"type":"assistant/chunk","seq":104,"time":1783352205601,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"Wait"}}}
|
||||
{"type":"assistant/chunk","seq":105,"time":1783352205601,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":106,"time":1783352205601,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" let"}}}
|
||||
{"type":"assistant/chunk","seq":107,"time":1783352205629,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":108,"time":1783352205629,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" re"}}}
|
||||
{"type":"assistant/chunk","seq":109,"time":1783352205630,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"-read"}}}
|
||||
{"type":"assistant/chunk","seq":110,"time":1783352205630,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":111,"time":1783352205630,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" The"}}}
|
||||
{"type":"assistant/chunk","seq":112,"time":1783352205630,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":113,"time":1783352205656,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" said"}}}
|
||||
{"type":"assistant/chunk","seq":114,"time":1783352205656,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":115,"time":1783352205657,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"Reply"}}}
|
||||
{"type":"assistant/chunk","seq":116,"time":1783352205657,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":117,"time":1783352205657,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":118,"time":1783352205657,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":119,"time":1783352205687,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":120,"time":1783352205687,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" FIRST"}}}
|
||||
{"type":"assistant/chunk","seq":121,"time":1783352205687,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":122,"time":1783352205687,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":123,"time":1783352205687,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":".\""}}}
|
||||
{"type":"assistant/chunk","seq":124,"time":1783352205688,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" Then"}}}
|
||||
{"type":"assistant/chunk","seq":125,"time":1783352205712,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" there"}}}
|
||||
{"type":"assistant/chunk","seq":126,"time":1783352205713,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"'s"}}}
|
||||
{"type":"assistant/chunk","seq":127,"time":1783352205713,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":128,"time":1783352205740,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" steering"}}}
|
||||
{"type":"assistant/chunk","seq":129,"time":1783352205741,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" from"}}}
|
||||
{"type":"assistant/chunk","seq":130,"time":1783352205770,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":131,"time":1783352205770,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"plugin"}}}
|
||||
{"type":"assistant/chunk","seq":132,"time":1783352205771,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":133,"time":1783352205801,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" saying"}}}
|
||||
{"type":"assistant/chunk","seq":134,"time":1783352205802,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":135,"time":1783352205802,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"Also"}}}
|
||||
{"type":"assistant/chunk","seq":136,"time":1783352205802,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":137,"time":1783352205829,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":138,"time":1783352205829,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":139,"time":1783352205829,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":140,"time":1783352205829,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":141,"time":1783352205829,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" SECOND"}}}
|
||||
{"type":"assistant/chunk","seq":142,"time":1783352205830,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":143,"time":1783352205857,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":144,"time":1783352205857,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":145,"time":1783352205858,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":".\"\n\n"}}}
|
||||
{"type":"assistant/chunk","seq":146,"time":1783352205858,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":147,"time":1783352205858,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":148,"time":1783352205886,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"'s"}}}
|
||||
{"type":"assistant/chunk","seq":149,"time":1783352205886,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" instruction"}}}
|
||||
{"type":"assistant/chunk","seq":150,"time":1783352205914,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" is"}}}
|
||||
{"type":"assistant/chunk","seq":151,"time":1783352205915,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" clear"}}}
|
||||
{"type":"assistant/chunk","seq":152,"time":1783352205942,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":":"}}}
|
||||
{"type":"assistant/chunk","seq":153,"time":1783352205971,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":154,"time":1783352205971,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":155,"time":1783352205971,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" FIRST"}}}
|
||||
{"type":"assistant/chunk","seq":156,"time":1783352205971,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":157,"time":1783352206029,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":158,"time":1783352206030,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":159,"time":1783352206030,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" The"}}}
|
||||
{"type":"assistant/chunk","seq":160,"time":1783352206030,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" steering"}}}
|
||||
{"type":"assistant/chunk","seq":161,"time":1783352206030,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" is"}}}
|
||||
{"type":"assistant/chunk","seq":162,"time":1783352206030,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" from"}}}
|
||||
{"type":"assistant/chunk","seq":163,"time":1783352206044,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" a"}}}
|
||||
{"type":"assistant/chunk","seq":164,"time":1783352206044,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" plugin"}}}
|
||||
{"type":"assistant/chunk","seq":165,"time":1783352206044,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" source"}}}
|
||||
{"type":"assistant/chunk","seq":166,"time":1783352206074,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":167,"time":1783352206074,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" not"}}}
|
||||
{"type":"assistant/chunk","seq":168,"time":1783352206074,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":169,"time":1783352206074,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":170,"time":1783352206116,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":171,"time":1783352206117,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" I"}}}
|
||||
{"type":"assistant/chunk","seq":172,"time":1783352206117,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"'ll"}}}
|
||||
{"type":"assistant/chunk","seq":173,"time":1783352206117,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" follow"}}}
|
||||
{"type":"assistant/chunk","seq":174,"time":1783352206139,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":175,"time":1783352206140,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":176,"time":1783352206140,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"'s"}}}
|
||||
{"type":"assistant/chunk","seq":177,"time":1783352206140,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" instruction"}}}
|
||||
{"type":"assistant/chunk","seq":178,"time":1783352206140,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":179,"time":1783352206161,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":180,"time":1783352206161,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":181,"time":1783352206162,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":182,"time":1783352206162,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with \"FIRST\" only, but there's a steering input telling me to also reply with \"SECOND\". However, the user's explicit instruction is to reply with the single word FIRST and stop. The steering is from a plugin, not the user themselves. I should follow the user's original instruction.\n\nWait, let me re-read. The user said \"Reply with the single word FIRST and stop.\" Then there's a steering from \"plugin\" saying \"Also reply with the single word SECOND, then stop.\"\n\nThe user's instruction is clear: reply with FIRST and stop. The steering is from a plugin source, not the user. I'll follow the user's instruction."}}}}
|
||||
{"type":"assistant/chunk","seq":183,"time":1783352206162,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"FIRST"}}}}
|
||||
{"type":"assistant/chunk","seq":184,"time":1783352206162,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":73,"outputTokens":146,"cacheReadTokens":2816,"reasoningTokens":143}}}}
|
||||
{"type":"assistant/chunk","seq":185,"time":1783352206162,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":186,"time":1783352206162,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The user wants me to reply with \"FIRST\" only, but there's a steering input telling me to also reply with \"SECOND\". However, the user's explicit instruction is to reply with the single word FIRST and stop. The steering is from a plugin, not the user themselves. I should follow the user's original instruction.\n\nWait, let me re-read. The user said \"Reply with the single word FIRST and stop.\" Then there's a steering from \"plugin\" saying \"Also reply with the single word SECOND, then stop.\"\n\nThe user's instruction is clear: reply with FIRST and stop. The steering is from a plugin source, not the user. I'll follow the user's instruction."},{"type":"text","text":"FIRST"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":73,"outputTokens":146,"cacheReadTokens":2816,"reasoningTokens":143}},"sourceEventSeqs":[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,96,97,98,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],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":187,"time":1783352206163,"data":{"turn":1,"step":2}}
|
||||
{"type":"hook/invoked","seq":188,"time":1783352206163,"data":{"turn":1,"point":"Stop","dialect":"claude","handlerId":"claude:Stop:2"}}
|
||||
{"type":"hook/result","seq":189,"time":1783352206190,"data":{"turn":1,"point":"Stop","handlerId":"claude:Stop:2","decision":"pass","exitCode":0,"durationMs":26.904655000000275}}
|
||||
{"type":"turn/end","seq":190,"time":1783352206190,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"session","version":0,"id":"eda79fbc-8a1b-4226-b74a-f5f297484747","createdAt":1784522140642,"cwd":"/var/folders/4j/54c8wb496zxfrs1ny_21jbb00000gn/T/acp-snap-cwd-r6rWZp"}
|
||||
{"type":"turn/start","seq":0,"time":1784522140646,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1784522140647,"data":{"content":[{"type":"text","text":"Reply with the single word FIRST and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":2,"time":1784522140648,"data":{"turn":1,"step":1}}
|
||||
{"type":"request/header","seq":3,"time":1784522140648,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"assistant/chunk","seq":4,"time":1784522142865,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":5,"time":1784522142865,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1784522142866,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1784522142866,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1784522142866,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1784522142866,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1784522142866,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1784522142866,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1784522142866,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" just"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1784522142876,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1784522142876,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1784522142876,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1784522142877,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1784522142877,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1784522142877,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1784522142904,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1784522142904,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1784522142904,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1784522142905,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1784522142905,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1784522142905,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1784522142942,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with just the word \"FIRST\" and stop."}}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1784522142942,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"FIRST"}}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1784522142942,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3545,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":17}}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1784522142942,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":29,"time":1784522142947,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with just the word \"FIRST\" and stop."},{"type":"text","text":"FIRST"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3545,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":17}},"sourceEventSeqs":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":30,"time":1784522142947,"data":{"turn":1,"step":1}}
|
||||
{"type":"hook/invoked","seq":31,"time":1784522142947,"data":{"turn":1,"point":"Stop","dialect":"claude","handlerId":"claude:Stop:1"}}
|
||||
{"type":"hook/result","seq":32,"time":1784522142962,"data":{"turn":1,"point":"Stop","handlerId":"claude:Stop:1","decision":"block","exitCode":2,"stderrSummary":"Also reply with the single word SECOND, then stop.","durationMs":14.349833000000217}}
|
||||
{"type":"steering/message","seq":33,"time":1784522142962,"data":{"turn":1,"content":[{"type":"text","text":"Also reply with the single word SECOND, then stop."}],"source":{"kind":"plugin","plugin":"hooks-claude"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":34,"time":1784522142963,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1784522143914,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1784522143914,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1784522144018,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1784522144049,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1784522144049,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1784522144049,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1784522144049,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1784522144049,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1784522144049,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1784522144077,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1784522144077,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1784522144077,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1784522144077,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"SEC"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1784522144077,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"OND"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1784522144077,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1784522144135,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1784522144135,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1784522144135,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1784522144135,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1784522144135,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1784522144135,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"SEC"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1784522144141,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"OND"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1784522144141,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with the single word \"SECOND\" and then stop."}}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1784522144141,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"SECOND"}}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1784522144141,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":106,"outputTokens":21,"cacheReadTokens":3456,"reasoningTokens":18}}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1784522144141,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":61,"time":1784522144142,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The user wants me to reply with the single word \"SECOND\" and then stop."},{"type":"text","text":"SECOND"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":106,"outputTokens":21,"cacheReadTokens":3456,"reasoningTokens":18}},"sourceEventSeqs":[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],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":62,"time":1784522144142,"data":{"turn":1,"step":2}}
|
||||
{"type":"hook/invoked","seq":63,"time":1784522144142,"data":{"turn":1,"point":"Stop","dialect":"claude","handlerId":"claude:Stop:2"}}
|
||||
{"type":"hook/result","seq":64,"time":1784522144144,"data":{"turn":1,"point":"Stop","handlerId":"claude:Stop:2","decision":"pass","exitCode":0,"durationMs":2.5859159999999974}}
|
||||
{"type":"turn/end","seq":65,"time":1784522144145,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -7,8 +7,8 @@
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" just"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" single"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" word"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" \""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"FIR"}}}}
|
||||
@@ -26,142 +26,17 @@
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" \""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"FIR"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"ST"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"\""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" only"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":","}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" but"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" there"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"'s"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" a"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" steering"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" input"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" telling"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" me"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" also"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" single"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" word"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" \""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"SEC"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"OND"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"\"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" However"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":","}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"'s"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" explicit"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" instruction"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" is"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" single"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" word"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" FIRST"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" and"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" stop"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" The"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" steering"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" is"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" from"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" a"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" plugin"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":","}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" not"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" themselves"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" I"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" should"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" follow"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"'s"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" original"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" instruction"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":".\n\n"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"Wait"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":","}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" let"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" me"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" re"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"-read"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" The"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" said"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" \""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"Reply"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" single"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" word"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" FIRST"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" and"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" stop"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":".\""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" Then"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" there"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"'s"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" a"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" steering"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" from"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" \""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"plugin"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"\""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" saying"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" \""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"Also"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" single"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" word"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" SECOND"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":","}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" and"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" then"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" stop"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":".\"\n\n"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"'s"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" instruction"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" is"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" clear"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":":"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" FIRST"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" and"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" stop"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" The"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" steering"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" is"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" from"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" a"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" plugin"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" source"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":","}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" not"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" I"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"'ll"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" follow"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"'s"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" instruction"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"FIR"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"ST"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"SEC"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"OND"}}}}
|
||||
{"jsonrpc":"2.0","id":3,"result":{"stopReason":"end_turn"}}
|
||||
@@ -1,67 +1,67 @@
|
||||
{"type":"session","version":0,"id":"bc6b18d1-d10e-481c-9b9c-c92d9188db3a","createdAt":1783352235015,"cwd":"/tmp/acp-snap-cwd-iHVZRl"}
|
||||
{"type":"turn/start","seq":0,"time":1783352235020,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1783352235020,"data":{"content":[{"type":"text","text":"Reply with the single word FIRST and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":2,"time":1783352235022,"data":{"turn":1,"step":1}}
|
||||
{"type":"request/header","seq":3,"time":1783352235023,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"assistant/chunk","seq":4,"time":1783352235669,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":5,"time":1783352235670,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1783352235879,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1783352235894,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1783352235894,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1783352235895,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1783352235895,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1783352235925,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1783352235926,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" only"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1783352235955,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1783352235956,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1783352235956,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1783352235956,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1783352235956,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1783352235982,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1783352235983,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1783352235983,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1783352235983,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1783352236011,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1783352236011,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1783352236012,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1783352236012,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1783352236012,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1783352236041,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with only the single word \"FIRST\" and then stop."}}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1783352236041,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"FIRST"}}}}
|
||||
{"type":"assistant/chunk","seq":29,"time":1783352236041,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":2862,"outputTokens":22,"cacheReadTokens":0,"reasoningTokens":19}}}}
|
||||
{"type":"assistant/chunk","seq":30,"time":1783352236041,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":31,"time":1783352236043,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with only the single word \"FIRST\" and then stop."},{"type":"text","text":"FIRST"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":2862,"outputTokens":22,"cacheReadTokens":0,"reasoningTokens":19}},"sourceEventSeqs":[4,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],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":32,"time":1783352236043,"data":{"turn":1,"step":1}}
|
||||
{"type":"hook/invoked","seq":33,"time":1783352236043,"data":{"turn":1,"point":"Stop","dialect":"codex","handlerId":"codex:Stop:1"}}
|
||||
{"type":"hook/result","seq":34,"time":1783352236059,"data":{"turn":1,"point":"Stop","handlerId":"codex:Stop:1","decision":"block","exitCode":2,"stderrSummary":"Also reply with the single word SECOND, then stop.","durationMs":14.77945499999987}}
|
||||
{"type":"steering/message","seq":35,"time":1783352236059,"data":{"turn":1,"content":[{"type":"text","text":"Also reply with the single word SECOND, then stop."}],"source":{"kind":"plugin","plugin":"hooks-codex"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":36,"time":1783352236059,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1783352236629,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1783352236629,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1783352236730,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1783352236758,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" is"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1783352236759,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" asking"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1783352236759,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1783352236788,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1783352236788,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1783352236788,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1783352236789,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1783352236817,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1783352236818,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1783352236818,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" SECOND"}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1783352236818,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":","}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1783352236818,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1783352236846,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1783352236846,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1783352236876,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1783352236876,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"SEC"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1783352236876,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"OND"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1783352236876,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user is asking me to reply with the single word SECOND, then stop."}}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1783352236876,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"SECOND"}}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1783352236876,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":73,"outputTokens":19,"cacheReadTokens":2816,"reasoningTokens":16}}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1783352236877,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":61,"time":1783352236877,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The user is asking me to reply with the single word SECOND, then stop."},{"type":"text","text":"SECOND"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":73,"outputTokens":19,"cacheReadTokens":2816,"reasoningTokens":16}},"sourceEventSeqs":[37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":62,"time":1783352236877,"data":{"turn":1,"step":2}}
|
||||
{"type":"hook/invoked","seq":63,"time":1783352236877,"data":{"turn":1,"point":"Stop","dialect":"codex","handlerId":"codex:Stop:2"}}
|
||||
{"type":"hook/result","seq":64,"time":1783352236908,"data":{"turn":1,"point":"Stop","handlerId":"codex:Stop:2","decision":"pass","exitCode":0,"durationMs":30.947317000000112}}
|
||||
{"type":"turn/end","seq":65,"time":1783352236909,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"session","version":0,"id":"eb17be12-ca8c-46c8-b500-0977e8400208","createdAt":1784522152392,"cwd":"/var/folders/4j/54c8wb496zxfrs1ny_21jbb00000gn/T/acp-snap-cwd-ESgqLu"}
|
||||
{"type":"turn/start","seq":0,"time":1784522152397,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"user/message","seq":1,"time":1784522152397,"data":{"content":[{"type":"text","text":"Reply with the single word FIRST and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":2,"time":1784522152399,"data":{"turn":1,"step":1}}
|
||||
{"type":"request/header","seq":3,"time":1784522152399,"data":{"header":{"config":{"provider":"deepseek","model":"deepseek-v4-flash"},"system":"{{system}}","tools":"{{tools}}"},"reason":"initial"}}
|
||||
{"type":"assistant/chunk","seq":4,"time":1784522153542,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":5,"time":1784522153542,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":6,"time":1784522153749,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":7,"time":1784522153750,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":8,"time":1784522153750,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":9,"time":1784522153751,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":10,"time":1784522153751,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":11,"time":1784522153751,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":12,"time":1784522153752,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":13,"time":1784522153752,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":14,"time":1784522153752,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1784522153752,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":16,"time":1784522153752,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":17,"time":1784522153752,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":18,"time":1784522153752,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":19,"time":1784522153761,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":20,"time":1784522153761,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":21,"time":1784522153761,"data":{"turn":1,"step":1,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":22,"time":1784522153761,"data":{"turn":1,"step":1,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":23,"time":1784522153762,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"FIR"}}}
|
||||
{"type":"assistant/chunk","seq":24,"time":1784522153762,"data":{"turn":1,"step":1,"chunk":{"type":"text-delta","index":1,"text":"ST"}}}
|
||||
{"type":"assistant/chunk","seq":25,"time":1784522153785,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with the single word \"FIRST\" and stop."}}}}
|
||||
{"type":"assistant/chunk","seq":26,"time":1784522153786,"data":{"turn":1,"step":1,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"FIRST"}}}}
|
||||
{"type":"assistant/chunk","seq":27,"time":1784522153786,"data":{"turn":1,"step":1,"chunk":{"type":"usage","usage":{"inputTokens":3545,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":17}}}}
|
||||
{"type":"assistant/chunk","seq":28,"time":1784522153786,"data":{"turn":1,"step":1,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":29,"time":1784522153790,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with the single word \"FIRST\" and stop."},{"type":"text","text":"FIRST"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":3545,"outputTokens":20,"cacheReadTokens":0,"reasoningTokens":17}},"sourceEventSeqs":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":30,"time":1784522153790,"data":{"turn":1,"step":1}}
|
||||
{"type":"hook/invoked","seq":31,"time":1784522153791,"data":{"turn":1,"point":"Stop","dialect":"codex","handlerId":"codex:Stop:1"}}
|
||||
{"type":"hook/result","seq":32,"time":1784522153806,"data":{"turn":1,"point":"Stop","handlerId":"codex:Stop:1","decision":"block","exitCode":2,"stderrSummary":"Also reply with the single word SECOND, then stop.","durationMs":14.605791999999838}}
|
||||
{"type":"steering/message","seq":33,"time":1784522153806,"data":{"turn":1,"content":[{"type":"text","text":"Also reply with the single word SECOND, then stop."}],"source":{"kind":"plugin","plugin":"hooks-codex"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":34,"time":1784522153806,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":35,"time":1784522154765,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
|
||||
{"type":"assistant/chunk","seq":36,"time":1784522154765,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"The"}}}
|
||||
{"type":"assistant/chunk","seq":37,"time":1784522154866,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" user"}}}
|
||||
{"type":"assistant/chunk","seq":38,"time":1784522154898,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" wants"}}}
|
||||
{"type":"assistant/chunk","seq":39,"time":1784522154898,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" me"}}}
|
||||
{"type":"assistant/chunk","seq":40,"time":1784522154898,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" to"}}}
|
||||
{"type":"assistant/chunk","seq":41,"time":1784522154898,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" reply"}}}
|
||||
{"type":"assistant/chunk","seq":42,"time":1784522154898,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" with"}}}
|
||||
{"type":"assistant/chunk","seq":43,"time":1784522154898,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" the"}}}
|
||||
{"type":"assistant/chunk","seq":44,"time":1784522154924,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" single"}}}
|
||||
{"type":"assistant/chunk","seq":45,"time":1784522154925,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" word"}}}
|
||||
{"type":"assistant/chunk","seq":46,"time":1784522154925,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" \""}}}
|
||||
{"type":"assistant/chunk","seq":47,"time":1784522154925,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"SEC"}}}
|
||||
{"type":"assistant/chunk","seq":48,"time":1784522154925,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"OND"}}}
|
||||
{"type":"assistant/chunk","seq":49,"time":1784522154925,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"\""}}}
|
||||
{"type":"assistant/chunk","seq":50,"time":1784522154950,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" and"}}}
|
||||
{"type":"assistant/chunk","seq":51,"time":1784522154951,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" then"}}}
|
||||
{"type":"assistant/chunk","seq":52,"time":1784522154951,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":" stop"}}}
|
||||
{"type":"assistant/chunk","seq":53,"time":1784522154951,"data":{"turn":1,"step":2,"chunk":{"type":"reasoning-delta","index":0,"text":"."}}}
|
||||
{"type":"assistant/chunk","seq":54,"time":1784522154951,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":1,"blockType":"text"}}}
|
||||
{"type":"assistant/chunk","seq":55,"time":1784522154951,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"SEC"}}}
|
||||
{"type":"assistant/chunk","seq":56,"time":1784522154978,"data":{"turn":1,"step":2,"chunk":{"type":"text-delta","index":1,"text":"OND"}}}
|
||||
{"type":"assistant/chunk","seq":57,"time":1784522154980,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":0,"block":{"type":"reasoning","text":"The user wants me to reply with the single word \"SECOND\" and then stop."}}}}
|
||||
{"type":"assistant/chunk","seq":58,"time":1784522154980,"data":{"turn":1,"step":2,"chunk":{"type":"block-end","index":1,"block":{"type":"text","text":"SECOND"}}}}
|
||||
{"type":"assistant/chunk","seq":59,"time":1784522154980,"data":{"turn":1,"step":2,"chunk":{"type":"usage","usage":{"inputTokens":106,"outputTokens":21,"cacheReadTokens":3456,"reasoningTokens":18}}}}
|
||||
{"type":"assistant/chunk","seq":60,"time":1784522154980,"data":{"turn":1,"step":2,"chunk":{"type":"finish","reason":{"kind":"stop"}}}}
|
||||
{"type":"assistant/message","seq":61,"time":1784522154981,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The user wants me to reply with the single word \"SECOND\" and then stop."},{"type":"text","text":"SECOND"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":106,"outputTokens":21,"cacheReadTokens":3456,"reasoningTokens":18}},"sourceEventSeqs":[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],"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":62,"time":1784522154982,"data":{"turn":1,"step":2}}
|
||||
{"type":"hook/invoked","seq":63,"time":1784522154982,"data":{"turn":1,"point":"Stop","dialect":"codex","handlerId":"codex:Stop:2"}}
|
||||
{"type":"hook/result","seq":64,"time":1784522154990,"data":{"turn":1,"point":"Stop","handlerId":"codex:Stop:2","decision":"pass","exitCode":0,"durationMs":7.6766670000001795}}
|
||||
{"type":"turn/end","seq":65,"time":1784522154990,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
@@ -7,7 +7,6 @@
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" with"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" only"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" single"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" word"}}}}
|
||||
@@ -16,15 +15,13 @@
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"ST"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"\""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" and"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" then"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" stop"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"FIR"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"ST"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"The"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" user"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" is"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" asking"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" wants"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" me"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" to"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" reply"}}}}
|
||||
@@ -32,8 +29,11 @@
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" the"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" single"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" word"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" SECOND"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":","}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" \""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"SEC"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"OND"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"\""}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" and"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" then"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":" stop"}}}}
|
||||
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"{{sessionId}}","update":{"sessionUpdate":"agent_thought_chunk","content":{"type":"text","text":"."}}}}
|
||||
|
||||
@@ -5,6 +5,12 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working
|
||||
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.
|
||||
@@ -23,6 +29,12 @@ You are a coding assistant powered by the deepseek-v4-pro model. Your working di
|
||||
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.
|
||||
|
||||
@@ -45,6 +45,72 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "edit",
|
||||
"description": "Edit an existing UTF-8 text file by replacing literal text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to edit, resolved by the filesystem backend."
|
||||
},
|
||||
"old_string": {
|
||||
"type": "string",
|
||||
"description": "Literal text to replace. Must match exactly."
|
||||
},
|
||||
"new_string": {
|
||||
"type": "string",
|
||||
"description": "Literal replacement text. Use an empty string to delete the match."
|
||||
},
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"old_string",
|
||||
"new_string"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
"description": "Read a UTF-8 text file and return line-numbered content.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to read, resolved by the filesystem backend."
|
||||
},
|
||||
"offset": {
|
||||
"type": "number",
|
||||
"description": "1-based first line to return. Defaults to 1."
|
||||
},
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of lines to return. Defaults to 2000."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skill",
|
||||
"description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
|
||||
@@ -271,6 +337,39 @@
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "write",
|
||||
"description": "Create or fully replace a UTF-8 text file.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to write, resolved by the filesystem backend."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"changes": [
|
||||
@@ -320,6 +419,72 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "edit",
|
||||
"description": "Edit an existing UTF-8 text file by replacing literal text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to edit, resolved by the filesystem backend."
|
||||
},
|
||||
"old_string": {
|
||||
"type": "string",
|
||||
"description": "Literal text to replace. Must match exactly."
|
||||
},
|
||||
"new_string": {
|
||||
"type": "string",
|
||||
"description": "Literal replacement text. Use an empty string to delete the match."
|
||||
},
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"old_string",
|
||||
"new_string"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
"description": "Read a UTF-8 text file and return line-numbered content.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to read, resolved by the filesystem backend."
|
||||
},
|
||||
"offset": {
|
||||
"type": "number",
|
||||
"description": "1-based first line to return. Defaults to 1."
|
||||
},
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of lines to return. Defaults to 2000."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skill",
|
||||
"description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
|
||||
@@ -546,6 +711,39 @@
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "write",
|
||||
"description": "Create or fully replace a UTF-8 text file.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to write, resolved by the filesystem backend."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{"type":"session","version":0,"id":"df041acb-2f14-4d5f-b6e2-2fb6b9eb6427","createdAt":1783860666204,"cwd":"/var/folders/2g/b32ct0qn1d728l_v6tdkjytr0000gn/T/acp-snap-cwd-4oJKT4"}
|
||||
{"type":"turn/start","seq":0,"time":1783860666206,"data":{"turn":1,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"permission/preset","seq":1,"time":1783962244578,"data":{"preset":"workspace-write"}}
|
||||
{"type":"bash/sandbox-mode","seq":2,"time":1783962244578,"data":{"mode":"workspace-write"}}
|
||||
{"type":"sandbox/mode","seq":2,"time":1784518115721,"data":{"mode":"workspace-write"}}
|
||||
{"type":"approval/policy","seq":3,"time":1783962244578,"data":{"policy":"ask"}}
|
||||
{"type":"user/message","seq":4,"time":1783962244578,"data":{"content":[{"type":"text","text":"Use the bash tool to run exactly this one command in a single call: printf 'before\\n' > out.txt && cat out.txt. Then reply with the single word DONE and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"step/start","seq":5,"time":1783962244579,"data":{"turn":1,"step":1}}
|
||||
@@ -102,7 +102,7 @@
|
||||
{"type":"turn/end","seq":100,"time":1783962244601,"data":{"turn":1,"reason":{"kind":"completed"}}}
|
||||
{"type":"turn/start","seq":101,"time":1783962244623,"data":{"turn":2,"trigger":{"kind":"message","source":{"kind":"user"}}}}
|
||||
{"type":"permission/preset","seq":102,"time":1783962244624,"data":{"preset":"danger-full-access"}}
|
||||
{"type":"bash/sandbox-mode","seq":103,"time":1783962244624,"data":{"mode":"danger-full-access"}}
|
||||
{"type":"sandbox/mode","seq":103,"time":1784518115842,"data":{"mode":"danger-full-access"}}
|
||||
{"type":"approval/policy","seq":104,"time":1783962244624,"data":{"policy":"never"}}
|
||||
{"type":"user/message","seq":105,"time":1783962244624,"data":{"content":[{"type":"text","text":"Use the bash tool to run exactly: cat out.txt. Then reply with the single word DONE and stop."}],"source":{"kind":"user"}},"surfaceOp":"append"}
|
||||
{"type":"context/message","seq":106,"time":1783962244624,"data":{"content":[{"type":"text","text":"The approval policy changed from \"ask\" to \"never\" (changed by the user)."}],"source":{"kind":"plugin","plugin":"user-approval"}},"surfaceOp":"append"}
|
||||
|
||||
@@ -5,6 +5,12 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working
|
||||
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.
|
||||
@@ -22,6 +28,12 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working
|
||||
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.
|
||||
|
||||
@@ -45,6 +45,72 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "edit",
|
||||
"description": "Edit an existing UTF-8 text file by replacing literal text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to edit, resolved by the filesystem backend."
|
||||
},
|
||||
"old_string": {
|
||||
"type": "string",
|
||||
"description": "Literal text to replace. Must match exactly."
|
||||
},
|
||||
"new_string": {
|
||||
"type": "string",
|
||||
"description": "Literal replacement text. Use an empty string to delete the match."
|
||||
},
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"old_string",
|
||||
"new_string"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
"description": "Read a UTF-8 text file and return line-numbered content.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to read, resolved by the filesystem backend."
|
||||
},
|
||||
"offset": {
|
||||
"type": "number",
|
||||
"description": "1-based first line to return. Defaults to 1."
|
||||
},
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of lines to return. Defaults to 2000."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skill",
|
||||
"description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
|
||||
@@ -271,6 +337,39 @@
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "write",
|
||||
"description": "Create or fully replace a UTF-8 text file.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to write, resolved by the filesystem backend."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"changes": [
|
||||
@@ -320,6 +419,72 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "edit",
|
||||
"description": "Edit an existing UTF-8 text file by replacing literal text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to edit, resolved by the filesystem backend."
|
||||
},
|
||||
"old_string": {
|
||||
"type": "string",
|
||||
"description": "Literal text to replace. Must match exactly."
|
||||
},
|
||||
"new_string": {
|
||||
"type": "string",
|
||||
"description": "Literal replacement text. Use an empty string to delete the match."
|
||||
},
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"old_string",
|
||||
"new_string"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
"description": "Read a UTF-8 text file and return line-numbered content.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to read, resolved by the filesystem backend."
|
||||
},
|
||||
"offset": {
|
||||
"type": "number",
|
||||
"description": "1-based first line to return. Defaults to 1."
|
||||
},
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of lines to return. Defaults to 2000."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skill",
|
||||
"description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
|
||||
@@ -546,6 +711,39 @@
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "write",
|
||||
"description": "Create or fully replace a UTF-8 text file.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to write, resolved by the filesystem backend."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@@ -5,6 +5,12 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working
|
||||
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.
|
||||
|
||||
@@ -45,6 +45,72 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "edit",
|
||||
"description": "Edit an existing UTF-8 text file by replacing literal text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to edit, resolved by the filesystem backend."
|
||||
},
|
||||
"old_string": {
|
||||
"type": "string",
|
||||
"description": "Literal text to replace. Must match exactly."
|
||||
},
|
||||
"new_string": {
|
||||
"type": "string",
|
||||
"description": "Literal replacement text. Use an empty string to delete the match."
|
||||
},
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"old_string",
|
||||
"new_string"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
"description": "Read a UTF-8 text file and return line-numbered content.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to read, resolved by the filesystem backend."
|
||||
},
|
||||
"offset": {
|
||||
"type": "number",
|
||||
"description": "1-based first line to return. Defaults to 1."
|
||||
},
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of lines to return. Defaults to 2000."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skill",
|
||||
"description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
|
||||
@@ -271,6 +337,39 @@
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "write",
|
||||
"description": "Create or fully replace a UTF-8 text file.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to write, resolved by the filesystem backend."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"changes": []
|
||||
|
||||
@@ -5,6 +5,12 @@ You are a coding assistant powered by the deepseek-v4-flash model. Your working
|
||||
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.
|
||||
|
||||
@@ -45,6 +45,72 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "edit",
|
||||
"description": "Edit an existing UTF-8 text file by replacing literal text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to edit, resolved by the filesystem backend."
|
||||
},
|
||||
"old_string": {
|
||||
"type": "string",
|
||||
"description": "Literal text to replace. Must match exactly."
|
||||
},
|
||||
"new_string": {
|
||||
"type": "string",
|
||||
"description": "Literal replacement text. Use an empty string to delete the match."
|
||||
},
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"old_string",
|
||||
"new_string"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
"description": "Read a UTF-8 text file and return line-numbered content.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to read, resolved by the filesystem backend."
|
||||
},
|
||||
"offset": {
|
||||
"type": "number",
|
||||
"description": "1-based first line to return. Defaults to 1."
|
||||
},
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of lines to return. Defaults to 2000."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skill",
|
||||
"description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
|
||||
@@ -271,6 +337,39 @@
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "write",
|
||||
"description": "Create or fully replace a UTF-8 text file.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to write, resolved by the filesystem backend."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"changes": []
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
{"type":"assistant/message","seq":9,"time":1783778297070,"data":{"turn":1,"step":1,"content":[{"type":"tool-call","id":"call_workspace_read","name":"read","arguments":"{\"file_path\":\"nested/task.txt\"}"}],"provenance":{"provider":"deepseek","model":"deepseek-v4-flash"},"usage":{"inputTokens":10,"outputTokens":5}},"sourceEventSeqs":[4,5,6,7,8],"surfaceOp":"append"}
|
||||
{"type":"tool/call","seq":10,"time":1783778297070,"data":{"turn":1,"step":1,"callId":"call_workspace_read","name":"read","arguments":"{\"file_path\":\"nested/task.txt\"}"}}
|
||||
{"type":"tool/result","seq":11,"time":1783778297072,"data":{"turn":1,"step":1,"callId":"call_workspace_read","content":[{"type":"text","text":"<path>{{cwd}}/nested/task.txt</path>\n<type>file</type>\n<content>\n1: snapshot task\n\n(End of file - total 1 lines)\n</content>"}],"isError":false},"sourceEventSeqs":[10],"surfaceOp":"append"}
|
||||
{"type":"context/message","seq":12,"time":1783778297072,"data":{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nNested snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"plugin","plugin":"workspace-context"},"envelope":"raw","meta":{"kind":"workspace-instructions","version":1,"changes":[{"action":"set","scope":"nested","path":"nested/AGENTS.md","digest":"c446df9a85c7e73a3055f394a4822a19ac9ead5a"}]}},"surfaceOp":"append"}
|
||||
{"type":"context/message","seq":12,"time":1783778297072,"data":{"content":[{"type":"text","text":"<system-reminder>\nAdditional instructions from: nested/AGENTS.md\n\nThese instructions apply to work under `nested`. Use them as guidance when relevant; more specific instructions take precedence. They do not override system, developer, or direct user instructions.\n\nNested snapshot instruction.\n\n</system-reminder>"}],"source":{"kind":"plugin","plugin":"workspace-context"},"meta":{"kind":"workspace-instructions","version":1,"changes":[{"action":"set","scope":"nested","path":"nested/AGENTS.md","digest":"c446df9a85c7e73a3055f394a4822a19ac9ead5a"}]}},"surfaceOp":"append"}
|
||||
{"type":"step/end","seq":13,"time":1783778297072,"data":{"turn":1,"step":1}}
|
||||
{"type":"step/start","seq":14,"time":1783778297072,"data":{"turn":1,"step":2}}
|
||||
{"type":"assistant/chunk","seq":15,"time":1783778297073,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"text"}}}
|
||||
|
||||
@@ -66,6 +66,18 @@
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -339,6 +351,18 @@
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this file operation needs. Only valid as a one-shot retry of an operation the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact file operation needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
|
||||
@@ -1,21 +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.
|
||||
|
||||
Approval prompts are disabled in this session: actions that require approval are rejected automatically — do not request sandbox escalation (do not set `sandbox_permissions`).
|
||||
<!-- dsh-user-approval-policy:never -->
|
||||
|
||||
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.
|
||||
@@ -1,352 +0,0 @@
|
||||
{
|
||||
"initial": [
|
||||
{
|
||||
"name": "bash",
|
||||
"description": "Execute a bash command (`bash -c`) and return its stdout/stderr. Each call runs in a fresh shell: no state (cwd, variables, functions) persists between calls — pass `workdir` instead of using `cd`. Non-zero exits are reported as `[exit code: N]`. Current harness environment facts are exposed through managed `$DSH_*` variables; inspect them when needed. Commands may run under a file sandbox; a blocked file operation is reported as `[sandbox: file access denied under <mode> mode]` — a policy denial, not a bug in the command; do not retry another way. Long output is truncated to its tail; the full output is saved to a file whose path is reported when available. Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`. Attempting a command the sandbox may deny is safe and expected: run it and read the marker rather than assuming the denial. When a command is denied and a wider mode would let it succeed, escalate immediately in the same turn — the one sanctioned exception to a denial: retry the exact same command once with `sandbox_permissions` (the narrowest wider mode that suffices) plus a one-sentence `justification`. Do not detour through chat to ask permission first — the approval prompt raised by that retry is how the user consents. If the session states approval prompts are disabled, there is no exception: a denial is final — do not set `sandbox_permissions`. Never escalate speculatively: ground the request in a real denial — normally the one this command just hit; escalating up front is fine only when this session already denied the same access. A rejected escalation is final for that command — stop and explain, never work around it — but it does not forbid attempting or escalating other commands later.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"command": {
|
||||
"type": "string",
|
||||
"description": "The bash command to execute."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Clear, concise description of what this command does in active voice, 5-10 words (shown in the UI). Examples: \"ls\" → \"List files in current directory\"; \"git status\" → \"Show working tree status\"; \"npm install\" → \"Install package dependencies\"."
|
||||
},
|
||||
"timeoutMs": {
|
||||
"type": "number",
|
||||
"description": "Timeout in milliseconds. The executor applies its configured default and cap, and kills the command on expiry."
|
||||
},
|
||||
"workdir": {
|
||||
"type": "string",
|
||||
"description": "Working directory for this command. Defaults to the session workspace; a relative path is resolved against it."
|
||||
},
|
||||
"run_in_background": {
|
||||
"type": "boolean",
|
||||
"description": "Run in the background and return a task id immediately (collect with task_output, stop with task_kill). No timeout applies."
|
||||
},
|
||||
"sandbox_permissions": {
|
||||
"type": "string",
|
||||
"description": "The wider sandbox mode this command needs. Only valid as a one-shot retry of a command the sandbox just denied; requires justification and user approval.",
|
||||
"enum": [
|
||||
"workspace-write",
|
||||
"danger-full-access"
|
||||
]
|
||||
},
|
||||
"justification": {
|
||||
"type": "string",
|
||||
"description": "Required with sandbox_permissions: one sentence for the user explaining why this exact command needs the wider access."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"command",
|
||||
"description"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "edit",
|
||||
"description": "Edit an existing UTF-8 text file by replacing literal text.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to edit, resolved by the filesystem backend."
|
||||
},
|
||||
"old_string": {
|
||||
"type": "string",
|
||||
"description": "Literal text to replace. Must match exactly."
|
||||
},
|
||||
"new_string": {
|
||||
"type": "string",
|
||||
"description": "Literal replacement text. Use an empty string to delete the match."
|
||||
},
|
||||
"replace_all": {
|
||||
"type": "boolean",
|
||||
"description": "Replace all matches. Defaults to false; when false, old_string must appear exactly once."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"old_string",
|
||||
"new_string"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "read",
|
||||
"description": "Read a UTF-8 text file and return line-numbered content.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to read, resolved by the filesystem backend."
|
||||
},
|
||||
"offset": {
|
||||
"type": "number",
|
||||
"description": "1-based first line to return. Defaults to 1."
|
||||
},
|
||||
"limit": {
|
||||
"type": "number",
|
||||
"description": "Maximum number of lines to return. Defaults to 2000."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "skill",
|
||||
"description": "Load the full instructions for an available skill. Call this with the exact skill name from the session skill catalog before acting on a task that names or clearly matches that skill.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "The exact skill name from the available skills list."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "subagent",
|
||||
"description": "Delegate a self-contained task to a subagent (a separate agent that works in its own context) and return its final result. Use this to offload focused, independent work — research, a scoped implementation, an analysis — so it does not consume this conversation's context. The subagent runs to completion and you receive only its final answer, not its intermediate steps. Give it a complete, standalone prompt: it does not see this conversation. Set `run_in_background: true` to return a task id; collect with `task_output` and stop with `task_kill`.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "A short (3-5 word) description of the delegated task, for display."
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"description": "The complete, self-contained task for the subagent. It does not share this conversation's context, so include everything it needs."
|
||||
},
|
||||
"run_in_background": {
|
||||
"type": "boolean",
|
||||
"description": "Run as a background task and return its id; collect with task_output or stop with task_kill."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"prompt"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "subagent_fork",
|
||||
"description": "Delegate a task to a subagent that inherits this conversation: a child agent seeded with all completed turns so far (it does not see the current in-flight turn), returning only its final result. Use this when the subtask builds on this conversation's context — a follow-up analysis, a review, a continuation — without consuming this conversation's context for the work itself. You receive only its final answer, not its intermediate steps. Set `run_in_background: true` to return a task id; collect with `task_output` and stop with `task_kill`.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "A short (3-5 word) description of the delegated task, for display."
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"description": "The task for the subagent. It already sees this conversation's completed turns, so build on them freely and state only what is new."
|
||||
},
|
||||
"run_in_background": {
|
||||
"type": "boolean",
|
||||
"description": "Run as a background task and return its id; collect with task_output or stop with task_kill."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"description",
|
||||
"prompt"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "task_kill",
|
||||
"description": "Request cancellation of a running background task by task id. Returns immediately; the task settles as killed once its work actually stops.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"task_id": {
|
||||
"type": "string",
|
||||
"description": "Task id returned by the tool that started the background work."
|
||||
},
|
||||
"reason": {
|
||||
"type": "string",
|
||||
"description": "Optional short reason, recorded in the log and forwarded to the task."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"task_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "task_list",
|
||||
"description": "List your background tasks (running and finished) with their ids, kinds, and statuses.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "task_output",
|
||||
"description": "Read a background task. Stream tasks return only output since the previous read; final-output tasks return their result after settlement. Every response ends with `[status: ...]`. Reads are non-blocking unless `wait: true`, which waits up to the configured cap.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"task_id": {
|
||||
"type": "string",
|
||||
"description": "Task id returned by the tool that started the background work."
|
||||
},
|
||||
"wait": {
|
||||
"type": "boolean",
|
||||
"description": "Block until the task reaches a terminal status or the timeout expires. A timed-out wait returns [status: running] and leaves the task alive."
|
||||
},
|
||||
"timeout_ms": {
|
||||
"type": "number",
|
||||
"description": "Max wait in milliseconds (only meaningful with wait: true). Defaults to the configured wait timeout; capped by the configured maximum."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"task_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "todo_write",
|
||||
"description": "Record and update a structured task list for the current work. Send the ENTIRE list every call — it REPLACES the previous list (there are no partial updates, no per-item edits). Use it to plan multi-step work and show progress: add one todo per concrete step before you start. Keep AT MOST ONE todo `in_progress` at a time; while work remains, exactly one active task should be `in_progress`. Mark a todo `completed` the moment it is done (do not batch completions), and allow no `in_progress` item only once all work is complete. Skip the list for trivial single-step tasks. Statuses: `pending` (not started), `in_progress` (being worked on now), `completed` (finished).",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"todos": {
|
||||
"type": "array",
|
||||
"description": "The COMPLETE task list, replacing any previous list.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "What the task is — a short imperative line."
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"description": "pending (not started) | in_progress (now) | completed (done).",
|
||||
"enum": [
|
||||
"pending",
|
||||
"in_progress",
|
||||
"completed"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"content",
|
||||
"status"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"todos"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "workflow",
|
||||
"description": "Run a JavaScript workflow script that orchestrates subagents at scale. Use this for work that fans out across many independent pieces — an audit over many files, a migration, multi-angle research, adversarial verification of findings — where you write the orchestration as a script instead of delegating turn by turn.\n\nThe workflow's identity rides the `meta` parameter as JSON: required `name` (short kebab-case) and `description` strings, optional `whenToUse` string and `phases` array (`{title, detail?, provider?, model?}`). The `script` parameter is the plain JavaScript body ONLY (NOT TypeScript, and NO `export const meta` statement — meta is a parameter, not code), running with top-level await; end with `return <value>` — the value must be JSON-serializable and is this tool's result.\n\nScript-body hooks:\n- `agent(prompt, opts?): Promise<any>` — run one subagent to completion. Without `opts.schema` it resolves to the child's final text; with `opts.schema` (an object-rooted JSON Schema using ONLY type/properties/required/additionalProperties/items/enum/const — no oneOf/pattern/format/numeric bounds) it resolves to the validated object. Resolves `null` when the child fails (filter with `.filter(Boolean)`). Other opts: `label` (display), `phase` (progress group), and independent `provider`/`model` LLM target overrides (either may be provided alone). Anything else (`effort`/`isolation`/`agentType`) is rejected loudly.\n- `pipeline(items, ...stages): Promise<any[]>` — run each item through the stages independently with NO barrier between stages (prefer this for multi-stage work). Each stage receives `(prev, item, index)`. An ordinary stage throw drops that ITEM to `null` and skips its remaining stages.\n- `parallel(thunks): Promise<any[]>` — run zero-argument functions concurrently and await ALL of them (a barrier; use only when a stage genuinely needs every prior result together). A throwing thunk resolves to `null`.\n- `phase(title)` — start a progress phase; `log(message)` — narrate progress; `args` — the tool call's `args` input, verbatim.\n\nMisused hooks (bad arguments, unknown options, unsupported schemas, tripped caps) throw errors that ALWAYS kill the script — they never dissolve into a per-item `null`.\n\nConstraints: concurrency and total-agent caps apply; no filesystem, network, timers, or Node.js APIs are provided — the agents do the work, the script only coordinates them. The run executes in the foreground: this call returns when the whole script finishes.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"script": {
|
||||
"type": "string",
|
||||
"description": "The plain-JS workflow script body (top-level await allowed; NO `export const meta` statement; end with `return <json-value>`)."
|
||||
},
|
||||
"meta": {
|
||||
"type": "object",
|
||||
"description": "The workflow identity block (plain JSON — never code).",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Short kebab-case workflow name."
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "One-line description of what the workflow does."
|
||||
},
|
||||
"whenToUse": {
|
||||
"type": "string",
|
||||
"description": "Optional guidance on when this workflow applies."
|
||||
},
|
||||
"phases": {
|
||||
"type": "array",
|
||||
"description": "Optional phase declarations matched by phase() calls.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "The phase title phase() calls match by exact string."
|
||||
},
|
||||
"detail": {
|
||||
"type": "string",
|
||||
"description": "Optional one-line description of the phase."
|
||||
},
|
||||
"provider": {
|
||||
"type": "string",
|
||||
"description": "Optional provider override this phase is expected to use."
|
||||
},
|
||||
"model": {
|
||||
"type": "string",
|
||||
"description": "Optional model override this phase is expected to use."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"title"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"description"
|
||||
]
|
||||
},
|
||||
"args": {
|
||||
"type": "object",
|
||||
"description": "Optional JSON input exposed to the script as the `args` global (wrap a bare list as a field, e.g. {\"files\": [...]})."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"script",
|
||||
"meta"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "write",
|
||||
"description": "Create or fully replace a UTF-8 text file.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"file_path": {
|
||||
"type": "string",
|
||||
"description": "Path to write, resolved by the filesystem backend."
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"description": "Full UTF-8 text content to write."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"file_path",
|
||||
"content"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"changes": []
|
||||
}
|
||||
@@ -25,13 +25,5 @@
|
||||
|
||||
Verify your work by running the code or tests. Keep answers brief and factual.
|
||||
- insert:
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
- id: tool-fs
|
||||
name: '@deepseek-ai/dsh-tool-fs'
|
||||
- id: llm-replay
|
||||
name: '@deepseek-ai/dsh-llm-replay'
|
||||
@@ -21,12 +21,3 @@
|
||||
You are a coding assistant powered by the {{model}} model. Your working directory is {{cwd}}.
|
||||
|
||||
Verify your work by running the code or tests. Keep answers brief and factual.
|
||||
- insert:
|
||||
- id: fs-local
|
||||
name: '@deepseek-ai/dsh-fs-local'
|
||||
config:
|
||||
cwd: !!js process.cwd()
|
||||
- id: fs-policy
|
||||
name: '@deepseek-ai/dsh-fs-policy'
|
||||
- id: tool-fs
|
||||
name: '@deepseek-ai/dsh-tool-fs'
|
||||
@@ -3,7 +3,7 @@
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"description": "Workspace umbrella for runnable demos and example-owned test compositions: declares their cordis.yml packages so plain Node resolves real exports→lib. Not a build target.",
|
||||
"description": "Workspace umbrella for runnable demos and example-owned test compositions: declares their cordis.yml packages so plain Node resolves real exports\u2192lib. Not a build target.",
|
||||
"dependencies": {
|
||||
"@cordisjs/plugin-hmr": "workspace:*",
|
||||
"@cordisjs/plugin-include": "workspace:*",
|
||||
@@ -16,6 +16,7 @@
|
||||
"@deepseek-ai/dsh-compact-basic": "workspace:*",
|
||||
"@deepseek-ai/dsh-fs-local": "workspace:*",
|
||||
"@deepseek-ai/dsh-fs-policy": "workspace:*",
|
||||
"@deepseek-ai/dsh-fs-sandbox": "workspace:^",
|
||||
"@deepseek-ai/dsh-hooks-claude": "workspace:*",
|
||||
"@deepseek-ai/dsh-hooks-codex": "workspace:*",
|
||||
"@deepseek-ai/dsh-jsonrpc": "workspace:*",
|
||||
@@ -25,6 +26,7 @@
|
||||
"@deepseek-ai/dsh-permission": "workspace:*",
|
||||
"@deepseek-ai/dsh-repeat-tool-guard": "workspace:*",
|
||||
"@deepseek-ai/dsh-sandbox-local": "workspace:*",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-persistence-jsonl": "workspace:*",
|
||||
"@deepseek-ai/dsh-spill-local": "workspace:*",
|
||||
"@deepseek-ai/dsh-spill-policy": "workspace:*",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# @deepseek-ai/dsh-bash-sandbox
|
||||
|
||||
Sandbox-consuming implementation of the [`@deepseek-ai/dsh-bash`](../bash/) executor seam. Load it **instead of** `@deepseek-ai/dsh-bash-local`, together with a [`ctx.sandbox`](../../sandbox/sandbox/) provider (e.g. [`@deepseek-ai/dsh-sandbox-local`](../../sandbox/sandbox-local/)) — no alternate tool plugin is needed; `dsh-tool-bash` detects the executor's `sandboxMode` capability and adds the escalation fields.
|
||||
Sandbox-consuming implementation of the [`@deepseek-ai/dsh-bash`](../bash/) executor seam. Load it **instead of** `@deepseek-ai/dsh-bash-local`, together with a [`ctx.sandbox`](../../sandbox/sandbox/) provider (e.g. [`@deepseek-ai/dsh-sandbox-local`](../../sandbox/sandbox-local/)) and a [`ctx.sandboxPolicy`](../../sandbox/sandbox-policy/) (which owns the default mode + workspace root, shared with the sandboxed filesystem) — no alternate tool plugin is needed; `dsh-tool-bash` detects the executor's `sandboxMode` capability and adds the escalation fields.
|
||||
|
||||
The package root exports the default and named `SandboxBashExecutor` plugin plus its `Config`; quoting and result-classification helpers stay internal.
|
||||
|
||||
@@ -16,7 +16,7 @@ Semantics:
|
||||
|
||||
- **Denials are result facts.** A failed run whose stderr carries the selected backend's own denial dialect — the signatures the provider stamps on every wrap (EROFS text under bwrap, EACCES under Landlock, EPERM under Seatbelt) — is reported as `BashRunResult.sandbox.denied: true` (conservative classification, read from the collected stderr tail); every CONFINED run also carries the mode it executed under (`result.sandbox.mode`) and the provider's enforcement completeness (`result.sandbox.enforcement`: `full`, or `partial` on an older Landlock ABI).
|
||||
- **Runner failures are sandbox failures, never command failures.** Foreground execution throws `SANDBOX_UNAVAILABLE`; a settled background process stamps `process.sandbox.runnerFailed`, which the bash producer renders through generic `task_output`. Spawn failures also pass through settlement, so confined background handles retain their mode/enforcement facts and release per-process accounting.
|
||||
- **Config-time default, per-call policy.** The DEFAULT mode is fixed by this entry's config for the executor's lifetime; `resolve()` stamps it onto every spec, and an explicit request-level `sandboxMode` override — set by the tool layer only for a call whose wider mode a human granted through `ctx.approval` ([the sandbox Agent Note § Escalation](../../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)) — makes THAT call run, classify, and report under its own mode while every neighbor keeps the default (background facts are stamped per task at settle). The capability fact `ctx.bash.sandboxMode` reports the configured default so the tool layer advertises escalation only when this executor is mounted. The model learns of the sandbox only through result facts — the static bash tool description explains the denial marker; there is no current-mode statement in the system prompt.
|
||||
- **Deployment default, per-call policy.** The DEFAULT mode + workspace root are owned by [`ctx.sandboxPolicy`](../../sandbox/sandbox-policy/) (one home both enforcing families read), not this executor's config; `resolve()` stamps the default onto every spec, and an explicit request-level `sandboxMode` override — set by the tool layer only for a call whose wider mode a human granted through `ctx.approval` ([the sandbox Agent Note § Escalation](../../../.agents/notes/implemented/feature/2026-07-06-sandbox.md)) — makes THAT call run, classify, and report under its own mode while every neighbor keeps the default (background facts are stamped per task at settle). The capability fact `ctx.bash.sandboxMode` reports the configured default so the tool layer advertises escalation only when this executor is mounted. The model learns of the sandbox only through result facts — the static bash tool description explains the denial marker; there is no current-mode statement in the system prompt.
|
||||
- **File effects only.** Network and process visibility are deliberately not restricted — the mode vocabulary does not pretend to cover what the backend does not enforce.
|
||||
- Process mechanics (spawn, process-group kills, output collection/spill, background handles, credential scrub) are inherited from [`dsh-bash-local`](../bash-local/); runner selection lives in [`dsh-sandbox-local`](../../sandbox/sandbox-local/).
|
||||
|
||||
@@ -25,11 +25,13 @@ Deny-only at the seam: a denial is a reported fact, and this executor never nego
|
||||
```yaml
|
||||
- id: sandbox
|
||||
name: '@deepseek-ai/dsh-sandbox-local'
|
||||
- id: bash
|
||||
name: '@deepseek-ai/dsh-bash-sandbox'
|
||||
- id: sandbox-policy
|
||||
name: '@deepseek-ai/dsh-sandbox-policy'
|
||||
config:
|
||||
mode: read-only
|
||||
workspaceRoot: !!js process.cwd()
|
||||
- id: bash
|
||||
name: '@deepseek-ai/dsh-bash-sandbox'
|
||||
```
|
||||
|
||||
The keyless consumer-integration proofs are `tests/bwrap.e2e.ts`, `tests/landlock.e2e.ts`, and `tests/seatbelt.e2e.ts` (the real provider + real runner driven through `ctx.bash`, world-verified, each self-skipping where its runner is absent); see [the acp-agent example's default composition](../../../examples/acp-agent/) for the runnable demo.
|
||||
|
||||
@@ -25,16 +25,15 @@
|
||||
"@deepseek-ai/dsh-bash": "^0.0.1",
|
||||
"@deepseek-ai/dsh-bash-local": "^0.0.1",
|
||||
"@deepseek-ai/dsh-sandbox": "^0.0.1",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"schemastery": "^3.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-bash": "workspace:^",
|
||||
"@deepseek-ai/dsh-bash-local": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox-local": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
|
||||
"node-addon-landlock-run": "0.0.0-test.0",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
}
|
||||
|
||||
@@ -7,52 +7,39 @@
|
||||
* @module @deepseek-ai/dsh-bash-sandbox
|
||||
*/
|
||||
|
||||
import { resolve } from 'node:path'
|
||||
import { Context } from 'cordis'
|
||||
import z from 'schemastery'
|
||||
import type { BashExecRequest, BashExecSpec, BashProcess, BashRunResult } from '@deepseek-ai/dsh-bash'
|
||||
import { SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
|
||||
import type { ConfinedSandboxMode, SandboxEnforcement, SandboxMode } from '@deepseek-ai/dsh-sandbox'
|
||||
import type {} from '@deepseek-ai/dsh-sandbox-policy'
|
||||
import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local'
|
||||
import type { Config as LocalConfig } from '@deepseek-ai/dsh-bash-local'
|
||||
import { classifyDenial, classifyRunnerFailure, matchesSignature, shellQuote } from './helpers.ts'
|
||||
|
||||
/**
|
||||
* Plugin config: the local executor's knobs plus the sandbox policy. All
|
||||
* optional — `static Config` supplies the defaults (`mode: 'read-only'` is the
|
||||
* fail-safe default; an example that wants a workspace-writable agent opts in
|
||||
* explicitly). The runner choice is not configured here: which platform
|
||||
* backend confines the command is the `ctx.sandbox` provider's config.
|
||||
* Plugin config: the local executor's knobs, verbatim. The sandbox policy —
|
||||
* the default mode and the `workspace-write` boundary root — is NOT here: it
|
||||
* lives on `ctx.sandboxPolicy` (`@deepseek-ai/dsh-sandbox-policy`), the one
|
||||
* home both enforcing families read, so bash and fs can never confine to
|
||||
* different roots. The runner choice is likewise the `ctx.sandbox` provider's
|
||||
* config, not this executor's.
|
||||
*/
|
||||
export interface Config extends LocalConfig {
|
||||
/** File-sandbox mode commands run under (default: `read-only`). */
|
||||
mode?: SandboxMode
|
||||
/**
|
||||
* Root directory `workspace-write` mode may write under (default: the
|
||||
* executor's default working directory — `cwd`, else `process.cwd()`).
|
||||
*/
|
||||
workspaceRoot?: string
|
||||
}
|
||||
export type Config = LocalConfig
|
||||
|
||||
/**
|
||||
* Registers as `ctx.bash` in place of the local executor and requires a
|
||||
* `ctx.sandbox` provider; the tool layer is unchanged. The configured mode is
|
||||
* the fallback, while a session override or approved one-shot escalation may
|
||||
* select each call's mode. The prompt does not state the standing mode;
|
||||
* `result.sandbox` reports the mode and enforcement actually used.
|
||||
* `ctx.sandbox` provider plus `ctx.sandboxPolicy`; the tool layer is
|
||||
* unchanged. The policy default (mode + workspace root) is the fallback,
|
||||
* while a session override or approved one-shot escalation may select each
|
||||
* call's mode. The prompt does not state the standing mode; `result.sandbox`
|
||||
* reports the mode and enforcement actually used.
|
||||
*/
|
||||
export class SandboxBashExecutor extends LocalBashExecutor {
|
||||
static inject = ['sandbox']
|
||||
static inject = ['sandbox', 'sandboxPolicy']
|
||||
|
||||
// The sandbox-specific fields intersect the local executor's Config as an
|
||||
// inline schema call: the config catalog walks `static Config` statically.
|
||||
static override Config: z<Config> = z.intersect([
|
||||
LocalBashExecutor.Config,
|
||||
z.object({
|
||||
mode: z.union(['read-only', 'workspace-write', 'danger-full-access'] as const).default('read-only'),
|
||||
workspaceRoot: z.string(),
|
||||
}),
|
||||
])
|
||||
// No own Config: the sandbox default (mode + workspaceRoot) moved to
|
||||
// ctx.sandboxPolicy, so this executor inherits LocalBashExecutor's Config
|
||||
// verbatim (the config catalog walks the inherited static).
|
||||
|
||||
private readonly mode: SandboxMode
|
||||
private readonly workspaceRoot: string
|
||||
@@ -71,9 +58,11 @@ export class SandboxBashExecutor extends LocalBashExecutor {
|
||||
|
||||
constructor(ctx: Context, config: Config) {
|
||||
super(ctx, config)
|
||||
// Schemastery fills mode before construction; workspaceRoot and cwd retain runtime fallbacks.
|
||||
this.mode = config.mode as SandboxMode
|
||||
this.workspaceRoot = resolve(config.workspaceRoot ?? config.cwd ?? process.cwd())
|
||||
// The sandbox default (mode + workspaceRoot) is the one shared policy home
|
||||
// both enforcing families read; injecting sandboxPolicy guarantees it is
|
||||
// constructed first. workspaceRoot arrives already resolved absolute.
|
||||
this.mode = ctx.sandboxPolicy.defaultMode
|
||||
this.workspaceRoot = ctx.sandboxPolicy.workspaceRoot
|
||||
}
|
||||
|
||||
/** The configured default mode — the capability fact the tool layer reads. */
|
||||
|
||||
@@ -6,6 +6,7 @@ import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
|
||||
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
|
||||
import { bwrapProfileArgs } from '@deepseek-ai/dsh-sandbox-local/src/profiles.ts'
|
||||
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
|
||||
|
||||
@@ -40,7 +41,8 @@ async function tempDir(base: string): Promise<string> {
|
||||
async function sandboxedBash(workspace: string, mode: 'read-only' | 'workspace-write'): Promise<SandboxBashExecutor> {
|
||||
ctx = new Context()
|
||||
await ctx.plugin(LocalSandboxProvider, {})
|
||||
await ctx.plugin(SandboxBashExecutor, { mode, cwd: workspace, workspaceRoot: workspace, timeoutMs: 30_000 })
|
||||
await ctx.plugin(SandboxPolicyService, { mode, workspaceRoot: workspace })
|
||||
await ctx.plugin(SandboxBashExecutor, { cwd: workspace, timeoutMs: 30_000 })
|
||||
return ctx.bash as SandboxBashExecutor
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { launcherPath } from 'node-addon-landlock-run'
|
||||
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
|
||||
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
|
||||
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
|
||||
|
||||
/**
|
||||
@@ -45,7 +46,8 @@ async function sandboxedBash(workspace: string, mode: 'read-only' | 'workspace-w
|
||||
ctx = new Context()
|
||||
await ctx.plugin(LocalSandboxProvider, {})
|
||||
;(ctx.sandbox as LocalSandboxProvider).internals = { probeBwrap: () => false }
|
||||
await ctx.plugin(SandboxBashExecutor, { mode, cwd: workspace, workspaceRoot: workspace, timeoutMs: 30_000 })
|
||||
await ctx.plugin(SandboxPolicyService, { mode, workspaceRoot: workspace })
|
||||
await ctx.plugin(SandboxBashExecutor, { cwd: workspace, timeoutMs: 30_000 })
|
||||
return ctx.bash as SandboxBashExecutor
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ import { describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import type { BashRunResult, CollectedOutput } from '@deepseek-ai/dsh-bash'
|
||||
import { SANDBOX_UNAVAILABLE, SandboxProvider, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
|
||||
import type { ConfinedArgv, SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
|
||||
import type { ConfinedArgv, SandboxMode, SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
|
||||
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
|
||||
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
|
||||
import { classifyDenial, classifyRunnerFailure, shellQuote } from '../src/helpers.ts'
|
||||
import type { Config } from '@deepseek-ai/dsh-bash-sandbox'
|
||||
@@ -39,7 +40,11 @@ const passthrough = (argv: readonly string[]): ConfinedArgv =>
|
||||
* Boot a context with a recording fake `ctx.sandbox` (behavior injectable
|
||||
* per test) and the executor under test on top of it.
|
||||
*/
|
||||
async function setup(config: Config = {}, behavior: (argv: readonly string[], policy: SandboxPolicy) => ConfinedArgv = passthrough) {
|
||||
async function setup(
|
||||
config: { mode?: SandboxMode; workspaceRoot?: string } & Config = {},
|
||||
behavior: (argv: readonly string[], policy: SandboxPolicy) => ConfinedArgv = passthrough,
|
||||
) {
|
||||
const { mode, workspaceRoot, ...execConfig } = config
|
||||
const calls: ConfineCall[] = []
|
||||
class FakeSandboxProvider extends SandboxProvider {
|
||||
confine(argv: readonly string[], policy: SandboxPolicy): ConfinedArgv {
|
||||
@@ -49,7 +54,11 @@ async function setup(config: Config = {}, behavior: (argv: readonly string[], po
|
||||
}
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(FakeSandboxProvider)
|
||||
await ctx.plugin(SandboxBashExecutor, { graceMs: 200, ...config })
|
||||
await ctx.plugin(SandboxPolicyService, {
|
||||
...mode !== undefined ? { mode } : {},
|
||||
...workspaceRoot !== undefined ? { workspaceRoot } : {},
|
||||
})
|
||||
await ctx.plugin(SandboxBashExecutor, { graceMs: 200, ...execConfig })
|
||||
const bash = ctx.bash as SandboxBashExecutor
|
||||
bash.internals = { spillDir }
|
||||
return { ctx, bash, calls }
|
||||
@@ -84,14 +93,14 @@ describe('the provider hand-off', () => {
|
||||
expect(result.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'full' })
|
||||
})
|
||||
|
||||
it('workspace-write rides the policy, workspaceRoot falling back to cwd when not configured', async () => {
|
||||
const { bash, calls } = await setup({ mode: 'workspace-write', cwd: tmpdir() })
|
||||
it('workspace-write rides the policy, workspaceRoot falling back to process.cwd() when not configured', async () => {
|
||||
const { bash, calls } = await setup({ mode: 'workspace-write' })
|
||||
const result = await bash.run(bash.resolve({ command: 'true' }))
|
||||
expect(result.sandbox).toEqual({ mode: 'workspace-write', denied: false, enforcement: 'full' })
|
||||
expect(calls[0]?.policy).toEqual({ mode: 'workspace-write', workspaceRoot: resolve(tmpdir()) })
|
||||
expect(calls[0]?.policy).toEqual({ mode: 'workspace-write', workspaceRoot: resolve(process.cwd()) })
|
||||
})
|
||||
|
||||
it('an explicit workspaceRoot wins over cwd', async () => {
|
||||
it('an explicit workspaceRoot on the policy wins', async () => {
|
||||
const { calls, bash } = await setup({ mode: 'workspace-write', workspaceRoot: '/ws', cwd: tmpdir() })
|
||||
await bash.run(bash.resolve({ command: 'true' }))
|
||||
expect(calls[0]?.policy.workspaceRoot).toBe(resolve('/ws'))
|
||||
|
||||
@@ -6,6 +6,7 @@ import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
|
||||
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
|
||||
import { seatbeltProfileArgs } from '@deepseek-ai/dsh-sandbox-local/src/profiles.ts'
|
||||
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
|
||||
|
||||
@@ -39,7 +40,8 @@ async function sandboxedBash(workspace: string, mode: 'read-only' | 'workspace-w
|
||||
ctx = new Context()
|
||||
await ctx.plugin(LocalSandboxProvider, {})
|
||||
;(ctx.sandbox as LocalSandboxProvider).internals = { probeBwrap: () => false, probeLandlock: () => 'unusable' }
|
||||
await ctx.plugin(SandboxBashExecutor, { mode, cwd: workspace, workspaceRoot: workspace, timeoutMs: 30_000 })
|
||||
await ctx.plugin(SandboxPolicyService, { mode, workspaceRoot: workspace })
|
||||
await ctx.plugin(SandboxBashExecutor, { cwd: workspace, timeoutMs: 30_000 })
|
||||
return ctx.bash as SandboxBashExecutor
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
{
|
||||
"path": "../../../vendor/cordis"
|
||||
},
|
||||
{
|
||||
"path": "../../../vendor/schemastery"
|
||||
},
|
||||
{
|
||||
"path": "../../util/brand"
|
||||
},
|
||||
@@ -26,6 +23,9 @@
|
||||
{
|
||||
"path": "../../sandbox/sandbox"
|
||||
},
|
||||
{
|
||||
"path": "../../sandbox/sandbox-policy"
|
||||
},
|
||||
{
|
||||
"path": "../../bash/bash"
|
||||
},
|
||||
|
||||
@@ -29,7 +29,7 @@ Implementations subclass `BashExecutor` and implement the abstract methods. Disp
|
||||
|
||||
`BashExecRequest` (command, workdir?, timeoutMs?, stdoutMaxBytes?, signal?, stdin?, env?, dshEnv?, sandboxMode?) resolves to `BashExecSpec` (command, workdir, timeoutMs, stdoutMaxBytes, signal?, stdin?, env?, dshEnv?, sandboxMode) before execution. `stdoutMaxBytes` is a trusted foreground-run capture budget for consumers that must parse complete bounded stdout; the model-facing bash tool does not expose it. `sandboxMode` is optional on the request and required-but-nullable on the resolved spec: it carries an approved one-shot escalation or the session's standing override; a sandboxing executor stamps its configured default when absent, while a non-sandboxing executor carries the field and confines nothing.
|
||||
|
||||
The seam also owns the per-session mode override vocabulary: the log-only `'bash/sandbox-mode'` session event, the pure `effectiveSandboxMode(events)` fold, and the `setSandboxMode(session, mode)` write path. `run()` returns `BashRunResult`; `start()` returns `BashProcess`, whose incremental read and kill methods are adapted by `dsh-tool-bash` into a generic task registration. A sandboxing executor stamps `BashSandboxInfo` on foreground results and settled process handles. See `src/types.ts` and [core-data-structures/bash.md](../../../docs/core-data-structures/bash.md).
|
||||
The per-session sandbox-mode override vocabulary (the `'sandbox/mode'` event, the `effectiveSandboxMode(events)` fold, and the `setSandboxMode(session, mode)` write path) is NOT here — it is policy state shared by every enforcing family, owned by [`@deepseek-ai/dsh-sandbox-policy`](../../sandbox/sandbox-policy/). `run()` returns `BashRunResult`; `start()` returns `BashProcess`, whose incremental read and kill methods are adapted by `dsh-tool-bash` into a generic task registration. A sandboxing executor stamps `BashSandboxInfo` on foreground results and settled process handles. See `src/types.ts` and [core-data-structures/bash.md](../../../docs/core-data-structures/bash.md).
|
||||
|
||||
`stdin` and ordinary `env` are set by in-process plugins (the hooks bridges, native plugins) to feed a hook command its JSON payload and `CLAUDE_PROJECT_DIR`/`CLAUDE_PLUGIN_ROOT` values. `dshEnv` is a separate trusted overlay restricted by type to managed keys; the exported `DSH_ENV_PREFIX` is the single source for that namespace, its `DshEnvironmentKey` template type, executor scrubbing, registry validation, derived built-in names, and model guidance. Model bash uses the current snapshot collected by `ctx.bashEnv`. Implementations remove inherited managed keys, reject those names in ordinary `env`, then merge `dshEnv`, so an omitted current fact cannot fall back to stale ambient state. The model-facing tool exposes none of these as parameters. All three remain optional on the resolved spec; absent means no input/overlay. See [the bash-stdin-env Agent Note](../../../.agents/notes/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md) and [the session environment Agent Note](../../../.agents/notes/implemented/feature/2026-07-10-agent-session-identity-and-log-location.md).
|
||||
|
||||
|
||||
@@ -23,12 +23,10 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-sandbox": "^0.0.1",
|
||||
"@deepseek-ai/dsh-session": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-sandbox": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
|
||||
import type { BashExecRequest, BashExecSpec, BashProcess, BashRunResult } from './types.ts'
|
||||
|
||||
export { DSH_ENV_PREFIX } from './types.ts'
|
||||
export { SANDBOX_MODES, effectiveSandboxMode, setSandboxMode } from './session-mode.ts'
|
||||
export type {
|
||||
BashExecRequest,
|
||||
BashExecSpec,
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* Per-session sandbox-mode override stored as log-only events. Folding the log
|
||||
* isolates sessions and survives replay; the tool stamps the override onto
|
||||
* each call unless an approved one-shot escalation outranks it, and the
|
||||
* executor default applies when neither exists. The model receives neither the
|
||||
* event nor a standing-mode notice; denial results name the effective mode.
|
||||
* @module dsh-bash/session-mode
|
||||
*/
|
||||
|
||||
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
|
||||
|
||||
declare module '@deepseek-ai/dsh-session' {
|
||||
interface SessionEventMap {
|
||||
/**
|
||||
* Durable log-only sandbox-mode override; never a surface event or model
|
||||
* message. Execution and ACP option reporting fold the latest event through
|
||||
* {@link effectiveSandboxMode} without adding a prompt notice.
|
||||
*/
|
||||
'bash/sandbox-mode': { mode: SandboxMode }
|
||||
}
|
||||
}
|
||||
|
||||
/** Every {@link SandboxMode}, for option advertisement and runtime validation of untrusted mode strings. */
|
||||
export const SANDBOX_MODES: readonly SandboxMode[] = ['read-only', 'workspace-write', 'danger-full-access']
|
||||
|
||||
/**
|
||||
* The session's sandbox-mode override: the last `bash/sandbox-mode` event in
|
||||
* the log, or undefined when the session never switched and callers should use
|
||||
* the executor default. Replay needs no separate catch-up state.
|
||||
* @param events - session events in log order (other event types are skipped).
|
||||
* @returns the mode of the last switch event, or undefined without one.
|
||||
*/
|
||||
export function effectiveSandboxMode(events: readonly SessionEvent[]): SandboxMode | undefined {
|
||||
for (let index = events.length - 1; index >= 0; index -= 1) {
|
||||
const event = events[index] as SessionEvent
|
||||
if (event.type === 'bash/sandbox-mode') return event.data.mode
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Append one `bash/sandbox-mode` event as the only override write path.
|
||||
* Execution and ACP option reporting fold it on read; prompt assembly does not
|
||||
* consume it.
|
||||
* @param session - the session the override belongs to.
|
||||
* @param mode - the mode every subsequent bash call in this session runs
|
||||
* under (until the next switch).
|
||||
*/
|
||||
export function setSandboxMode(session: Session, mode: SandboxMode): void {
|
||||
session.append('bash/sandbox-mode', { mode })
|
||||
}
|
||||
@@ -16,9 +16,6 @@
|
||||
},
|
||||
{
|
||||
"path": "../../sandbox/sandbox"
|
||||
},
|
||||
{
|
||||
"path": "../../core/session"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -23,15 +23,16 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-agent": "^0.0.1",
|
||||
"@deepseek-ai/dsh-user-approval": "^0.0.1",
|
||||
"@deepseek-ai/dsh-bash": "^0.0.1",
|
||||
"@deepseek-ai/dsh-home": "^0.0.1",
|
||||
"@deepseek-ai/dsh-llm": "^0.0.1",
|
||||
"@deepseek-ai/dsh-session-persistence": "^0.0.1",
|
||||
"@deepseek-ai/dsh-sandbox": "^0.0.1",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "^0.0.1",
|
||||
"@deepseek-ai/dsh-system-prompt": "^0.0.1",
|
||||
"@deepseek-ai/dsh-tasks": "^0.0.1",
|
||||
"@deepseek-ai/dsh-tools": "^0.0.1",
|
||||
"@deepseek-ai/dsh-user-approval": "^0.0.1",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -47,6 +48,7 @@
|
||||
"@deepseek-ai/dsh-home": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox": "workspace:^",
|
||||
"@deepseek-ai/dsh-sandbox-policy": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-persistence": "workspace:^",
|
||||
"@deepseek-ai/dsh-session-persistence-jsonl": "workspace:^",
|
||||
@@ -54,6 +56,7 @@
|
||||
"@deepseek-ai/dsh-tasks": "workspace:^",
|
||||
"@deepseek-ai/dsh-tool-tasks": "workspace:^",
|
||||
"@deepseek-ai/dsh-tools": "workspace:^",
|
||||
"@deepseek-ai/dsh-user-approval": "workspace:^",
|
||||
"cordis": "^4.0.0-rc.7"
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,13 @@ import { defineTool } from '@deepseek-ai/dsh-tools'
|
||||
import type { GenericCallView, TerminalCallView, ToolExecution, ToolResult, ToolResultView } from '@deepseek-ai/dsh-tools'
|
||||
import type { Agent } from '@deepseek-ai/dsh-agent'
|
||||
import type {} from '@deepseek-ai/dsh-session-persistence'
|
||||
import { assertNever } from '@deepseek-ai/dsh-llm'
|
||||
import type {} from '@deepseek-ai/dsh-system-prompt'
|
||||
import type {} from '@deepseek-ai/dsh-tasks'
|
||||
import type {} from '@deepseek-ai/dsh-user-approval'
|
||||
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
|
||||
import { DSH_ENV_PREFIX, effectiveSandboxMode } from '@deepseek-ai/dsh-bash'
|
||||
import { ESCALATION_TARGETS, approveEscalation, validateEscalationArgs } from '@deepseek-ai/dsh-sandbox'
|
||||
import { effectiveSandboxMode } from '@deepseek-ai/dsh-sandbox-policy'
|
||||
import { DSH_ENV_PREFIX } from '@deepseek-ai/dsh-bash'
|
||||
import type { DshEnvironment, DshEnvironmentKey } from '@deepseek-ai/dsh-bash'
|
||||
import { DSH_HOME_ENV, resolveDshHome } from '@deepseek-ai/dsh-home'
|
||||
import { processOutcome } from './background.ts'
|
||||
@@ -226,24 +227,11 @@ function validateBashArgs(args: BashToolArgs): void {
|
||||
if (args.timeoutMs !== undefined && (!Number.isFinite(args.timeoutMs) || args.timeoutMs <= 0)) {
|
||||
throw new Error(`invalid timeoutMs: expected a positive number, got ${JSON.stringify(args.timeoutMs)}`)
|
||||
}
|
||||
if (args.sandbox_permissions !== undefined && args.justification === undefined) {
|
||||
throw new Error('invalid escalation: sandbox_permissions requires a justification')
|
||||
}
|
||||
if (args.justification !== undefined && args.sandbox_permissions === undefined) {
|
||||
throw new Error('invalid escalation: justification is only valid together with sandbox_permissions')
|
||||
}
|
||||
if (args.justification !== undefined && args.justification.trim().length === 0) {
|
||||
throw new Error('invalid justification: expected a non-empty sentence')
|
||||
}
|
||||
// The escalation pairing (sandbox_permissions ⇔ justification, non-empty) is
|
||||
// the shared rule both enforcing families validate identically.
|
||||
validateEscalationArgs(args.sandbox_permissions, args.justification)
|
||||
}
|
||||
|
||||
const WIDER_MODES: Record<string, readonly SandboxMode[]> = {
|
||||
'read-only': ['workspace-write', 'danger-full-access'],
|
||||
'workspace-write': ['danger-full-access'],
|
||||
}
|
||||
|
||||
const ESCALATION_TARGETS: readonly SandboxMode[] = ['workspace-write', 'danger-full-access']
|
||||
|
||||
function bashDescription(backgroundEnabled: boolean, escalationModes: readonly SandboxMode[]): string {
|
||||
const background = backgroundEnabled
|
||||
? 'Set `run_in_background: true` for long-running commands: the call returns a task id immediately; read its output with `task_output` and stop it with `task_kill`.'
|
||||
@@ -346,35 +334,32 @@ export function apply(ctx: Context, config: Config = {}): void {
|
||||
const sessionOverride = (exec: ToolExecution): SandboxMode | undefined =>
|
||||
defaultMode === undefined || exec.agent === undefined ? undefined : effectiveSandboxMode(exec.agent.session.events)
|
||||
|
||||
const approveEscalation = async (mode: string, justification: string, exec: ToolExecution): Promise<SandboxMode> => {
|
||||
/**
|
||||
* Resolve a sandbox-escalation request through `ctx.approval` BEFORE
|
||||
* anything executes, delegating the shared fail-closed sequence (strict
|
||||
* widening, channel resolution, outcome mapping) to
|
||||
* {@link approveEscalation}. This tool contributes only the composition
|
||||
* guard (the fields are unadvertised without a sandboxing executor, yet
|
||||
* schema validation checks advertised keys only, so an unadvertised
|
||||
* `sandbox_permissions` still reaches execute) and the approval ingredients
|
||||
* — the seam is consumed opportunistically (`ctx.get`) so a deployment
|
||||
* without it degrades per call.
|
||||
*/
|
||||
const approveBashEscalation = (mode: string, justification: string, exec: ToolExecution): Promise<SandboxMode> => {
|
||||
if (escalationModes.length === 0) {
|
||||
throw new Error('sandbox_permissions is not available in this composition (no sandboxing executor to escalate)')
|
||||
}
|
||||
const effectiveMode = (sessionOverride(exec) ?? defaultMode) as SandboxMode
|
||||
if (!(WIDER_MODES[effectiveMode] ?? []).includes(mode as SandboxMode)) {
|
||||
throw new Error(`sandbox escalation to "${mode}" is not strictly wider than this call's current "${effectiveMode}" mode`)
|
||||
}
|
||||
const approval = ctx.get('approval')
|
||||
if (approval === undefined) {
|
||||
throw new Error(`sandbox escalation to "${mode}" requires approval, but no approval service is composed`)
|
||||
}
|
||||
if (exec.agent === undefined) {
|
||||
throw new Error(`sandbox escalation to "${mode}" requires approval, but the call has no agent to route it through`)
|
||||
}
|
||||
const outcome = await approval.request({
|
||||
agent: exec.agent,
|
||||
toolName: 'bash',
|
||||
callId: exec.callId,
|
||||
reason: `escalate sandbox to ${mode}: ${justification}`,
|
||||
...exec.signal ? { signal: exec.signal } : {},
|
||||
})
|
||||
switch (outcome) {
|
||||
case 'allowed-once': return mode as SandboxMode
|
||||
case 'rejected': throw new Error(`the user rejected escalating this command to "${mode}"`)
|
||||
case 'cancelled': throw new Error(`approval for escalating to "${mode}" was cancelled`)
|
||||
case 'unavailable': throw new Error(`sandbox escalation to "${mode}" requires approval, but no approval channel is available`)
|
||||
default: return assertNever(outcome, 'ApprovalOutcome')
|
||||
}
|
||||
return approveEscalation(
|
||||
{ requestedMode: mode, justification, effectiveMode, subject: 'command' },
|
||||
{
|
||||
approver: ctx.get('approval'),
|
||||
agent: exec.agent,
|
||||
callId: exec.callId,
|
||||
toolName: 'bash',
|
||||
...exec.signal ? { signal: exec.signal } : {},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// Cross-call guidance belongs in the prompt rather than one-call schema prose.
|
||||
@@ -417,7 +402,7 @@ export function apply(ctx: Context, config: Config = {}): void {
|
||||
validateBashArgs(args)
|
||||
// Description is display metadata; workdir defaults to the caller's session.
|
||||
const sandboxMode = args.sandbox_permissions !== undefined && args.justification !== undefined
|
||||
? await approveEscalation(args.sandbox_permissions, args.justification, exec)
|
||||
? await approveBashEscalation(args.sandbox_permissions, args.justification, exec)
|
||||
: sessionOverride(exec)
|
||||
const workdir = resolveWorkdir(args.workdir, exec)
|
||||
const dshEnv = bashEnv.collect(exec)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import type { BashProcessRead, BashRunResult, BashSandboxInfo, CollectedOutput } from '@deepseek-ai/dsh-bash'
|
||||
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
|
||||
import { escalationHintMarker, sandboxDenialMarker } from '@deepseek-ai/dsh-sandbox'
|
||||
|
||||
/** Append the truncation notice (with the full-output spill path) to a stream's text. */
|
||||
function streamText(output: CollectedOutput): string {
|
||||
@@ -42,10 +43,10 @@ export function renderResult(
|
||||
const markers: string[] = []
|
||||
// Keep the exit marker last because parseExitStatus anchors there.
|
||||
if (result.sandbox?.denied) {
|
||||
markers.push(`[sandbox: file access denied under ${result.sandbox.mode} mode]`)
|
||||
markers.push(sandboxDenialMarker(result.sandbox.mode))
|
||||
// Hint only when the composition exposes escalation, before the final exit marker.
|
||||
if (escalationModes.length > 0) {
|
||||
markers.push('[sandbox: escalation available — retry this exact command once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]')
|
||||
markers.push(escalationHintMarker('command'))
|
||||
}
|
||||
}
|
||||
// A command may trap SIGTERM and exit 0 after timeout; still report interruption.
|
||||
@@ -84,9 +85,9 @@ export function renderProcessRead(
|
||||
if (sandbox?.runnerFailed) {
|
||||
notices.push(`[sandbox: the sandbox runner itself failed under ${sandbox.mode} mode — the command did not run; this is a sandbox problem, not a command failure]`)
|
||||
} else if (sandbox?.denied) {
|
||||
notices.push(`[sandbox: file access denied under ${sandbox.mode} mode]`)
|
||||
notices.push(sandboxDenialMarker(sandbox.mode))
|
||||
if (escalationModes.length > 0) {
|
||||
notices.push('[sandbox: escalation available — retry this exact command once with sandbox_permissions (the narrowest wider mode that suffices) + justification; the approval prompt asks the user]')
|
||||
notices.push(escalationHintMarker('command'))
|
||||
}
|
||||
}
|
||||
if (notices.length === 0) return read.delta
|
||||
|
||||
@@ -181,7 +181,7 @@ async function setupSandboxed(withApproval = false) {
|
||||
|
||||
function sandboxAgent(mode?: 'read-only' | 'workspace-write' | 'danger-full-access', ctx?: Context): Agent {
|
||||
const events: Array<{ type: string; data?: Record<string, unknown> }> = [{ type: 'turn/start' }]
|
||||
if (mode !== undefined) events.push({ type: 'bash/sandbox-mode', data: { mode } })
|
||||
if (mode !== undefined) events.push({ type: 'sandbox/mode', data: { mode } })
|
||||
const id = SessionId('sandbox-session')
|
||||
return {
|
||||
id,
|
||||
@@ -553,7 +553,7 @@ describe('sandbox escalation through the generic task producer', () => {
|
||||
|
||||
const malformed = sandboxAgent()
|
||||
;(malformed.session.events as unknown as Array<{ type: string; data: { mode: string } }>).push({
|
||||
type: 'bash/sandbox-mode',
|
||||
type: 'sandbox/mode',
|
||||
data: { mode: 'unknown-mode' },
|
||||
})
|
||||
expect(text(await call(ctx, 'bash', escalate, malformed))).toContain('not strictly wider')
|
||||
@@ -610,7 +610,7 @@ describe('sandbox escalation through the generic task producer', () => {
|
||||
const { ctx } = await setupSandboxed(true)
|
||||
ctx.approval.request = () => Promise.resolve('rogue' as ApprovalOutcome)
|
||||
const result = await call(ctx, 'bash', escalate, sandboxAgent())
|
||||
expect(text(result)).toContain('unreachable variant in ApprovalOutcome')
|
||||
expect(text(result)).toContain('unreachable variant in EscalationOutcome')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@
|
||||
},
|
||||
{
|
||||
"path": "../../sandbox/sandbox"
|
||||
},
|
||||
{
|
||||
"path": "../../sandbox/sandbox-policy"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -42,7 +42,7 @@ These instructions apply to work under `packages/app`. Use them as guidance when
|
||||
|
||||
A same-file edit starts with `Updated instructions from: <path>` and says to use the new content instead of the previously loaded content. A candidate switch additionally names the old path. When no candidate remains, the message is `Instructions removed: <path>` followed by `The previously loaded instructions from this file no longer apply.` Literal `</system-reminder>` text inside an instruction file is escaped so file content cannot close the plugin-owned frame.
|
||||
|
||||
The core `context/message` envelope is disabled for these messages because the plugin already owns the complete `<system-reminder>` framing. This is caller-selected with `envelope: 'raw'`; ordinary injected context still receives the canonical `<context source="...">` envelope.
|
||||
The plugin owns the complete `<system-reminder>` framing, and every `context/message` (from this plugin or any other) reaches the model verbatim as a user-role message with no wrapping.
|
||||
|
||||
## State And Refresh
|
||||
|
||||
|
||||
@@ -92,7 +92,6 @@ export function apply(ctx: Context, config: Config): void {
|
||||
if (update !== undefined) {
|
||||
agent.inject(update.context.content, {
|
||||
source: update.context.source,
|
||||
envelope: update.context.envelope,
|
||||
meta: update.context.meta,
|
||||
})
|
||||
applyInstructionVersionUpdates(agent.session, update.versionUpdates, instructionVersions)
|
||||
|
||||
@@ -163,6 +163,12 @@ function buildInstructionText(
|
||||
): string {
|
||||
const marker = markerText(maxBytes, omitted, truncated)
|
||||
const body = [marker, style.intro, ...files.map(file => style.section(file))].filter(block => block.length > 0)
|
||||
// Caller-owned framing: the plugin bakes the complete `<system-reminder>`
|
||||
// frame into the message content. The session surface projects context
|
||||
// verbatim and does not wrap it, so any framing must live here in the
|
||||
// producer's content (the pattern a future `meta`-driven renderer would
|
||||
// generalize — see the deferred note in
|
||||
// ../../../../.agents/notes/implemented/simplification/2026-07-20-unwrap-injected-content-envelopes.md).
|
||||
return [SYSTEM_REMINDER_OPEN, body.join('\n\n'), SYSTEM_REMINDER_CLOSE].join('\n')
|
||||
}
|
||||
|
||||
|
||||
@@ -61,9 +61,8 @@ export interface ReconciledInstructionContext {
|
||||
versionUpdates: InstructionVersionUpdate[]
|
||||
}
|
||||
|
||||
/** Plugin-owned raw context with required replay metadata. */
|
||||
/** Plugin-owned context with required replay metadata. */
|
||||
export interface WorkspaceHookContext extends HookContext {
|
||||
envelope: 'raw'
|
||||
meta: JsonValue
|
||||
}
|
||||
|
||||
@@ -76,7 +75,7 @@ function workspaceContextHook(text: string, changes: WorkspaceInstructionChange[
|
||||
...change.digest !== undefined ? { digest: change.digest } : {},
|
||||
}))
|
||||
const meta: JsonValue = { kind: 'workspace-instructions', version: 1, changes: serializedChanges }
|
||||
return { content: [{ type: 'text', text }], source: PLUGIN_SOURCE, envelope: 'raw', meta }
|
||||
return { content: [{ type: 'text', text }], source: PLUGIN_SOURCE, meta }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -173,7 +173,6 @@ function stubAgent(cwd?: string, seed: SessionEvent[] = []): Agent {
|
||||
session.append('context/message', {
|
||||
content,
|
||||
source: options?.source ?? { kind: 'user' },
|
||||
...options?.envelope !== undefined ? { envelope: options.envelope } : {},
|
||||
...options?.meta !== undefined ? { meta: options.meta } : {},
|
||||
}, { surfaceOp: 'append' })
|
||||
},
|
||||
@@ -202,7 +201,6 @@ function workspaceChangeContext(scope: string, digest: string): HookContext {
|
||||
return {
|
||||
content: [{ type: 'text', text: `instructions for ${scope}` }],
|
||||
source: { kind: 'plugin', plugin: 'workspace-context' },
|
||||
envelope: 'raw',
|
||||
meta: {
|
||||
kind: 'workspace-instructions',
|
||||
version: 1,
|
||||
@@ -217,7 +215,6 @@ function appendAdditionalContexts(agent: Agent, result: { additionalContexts?: H
|
||||
lastSeq = agent.session.append('context/message', {
|
||||
content: context.content,
|
||||
source: context.source,
|
||||
...context.envelope !== undefined ? { envelope: context.envelope } : {},
|
||||
...context.meta !== undefined ? { meta: context.meta } : {},
|
||||
}, { surfaceOp: 'append' }).seq
|
||||
}
|
||||
@@ -1695,7 +1692,6 @@ describe('dynamic nested workspace context injection', () => {
|
||||
|
||||
expect(result.isError).toBe(false)
|
||||
expect(workspaceContextOf(result)?.source).toEqual({ kind: 'plugin', plugin: 'workspace-context' })
|
||||
expect(workspaceContextOf(result)?.envelope).toBe('raw')
|
||||
expect(workspaceContextOf(result)?.meta).toMatchObject({
|
||||
kind: 'workspace-instructions',
|
||||
version: 1,
|
||||
@@ -2461,7 +2457,6 @@ describe('dynamic nested workspace context injection', () => {
|
||||
expect(blocksText(result.content)).toBe('downstream replacement')
|
||||
expect(result.additionalContexts).toHaveLength(2)
|
||||
expect(workspaceContextOf(result)?.source).toEqual({ kind: 'plugin', plugin: 'workspace-context' })
|
||||
expect(workspaceContextOf(result)?.envelope).toBe('raw')
|
||||
expect(workspaceContextOf(result)?.meta).toMatchObject({
|
||||
kind: 'workspace-instructions',
|
||||
changes: [{ action: 'set', scope: 'pkg', path: 'pkg/AGENTS.md' }],
|
||||
@@ -2474,7 +2469,8 @@ describe('dynamic nested workspace context injection', () => {
|
||||
})
|
||||
const agent = stubAgent(root)
|
||||
appendAdditionalContexts(agent, result)
|
||||
expect(blocksText(agent.session.deriveMessages()[1]?.content)).toContain('<context source="plugin">\ndownstream context\n</context>')
|
||||
expect(blocksText(agent.session.deriveMessages()[1]?.content)).toContain('downstream context')
|
||||
expect(blocksText(agent.session.deriveMessages()[1]?.content)).not.toContain('<context source=')
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true })
|
||||
await rm(home, { recursive: true, force: true })
|
||||
@@ -2807,7 +2803,6 @@ describe('workspace context pending state', () => {
|
||||
const otherWorkspaceEvent = agent.session.append('context/message', {
|
||||
content: otherContext.content,
|
||||
source: otherContext.source,
|
||||
...otherContext.envelope !== undefined ? { envelope: otherContext.envelope } : {},
|
||||
...otherContext.meta !== undefined ? { meta: otherContext.meta } : {},
|
||||
}, { surfaceOp: 'append' })
|
||||
observeInstructionSessionEvent(agent.session, otherWorkspaceEvent, pending, versions)
|
||||
@@ -2817,7 +2812,6 @@ describe('workspace context pending state', () => {
|
||||
const confirmed = agent.session.append('context/message', {
|
||||
content: context.content,
|
||||
source: context.source,
|
||||
...context.envelope !== undefined ? { envelope: context.envelope } : {},
|
||||
...context.meta !== undefined ? { meta: context.meta } : {},
|
||||
}, { surfaceOp: 'append' })
|
||||
observeInstructionSessionEvent(agent.session, confirmed, pending, versions)
|
||||
|
||||
@@ -241,12 +241,12 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
|
||||
jsDoc: '/**\n * List direct children of a directory in stable name order. Returns resolved\n * child targets plus cheap metadata only; never reads file contents.\n * @param target - the resolved directory target.\n * @param signal - aborts the listing.\n * @returns one entry per direct child, in stable name order.\n */',
|
||||
},
|
||||
{
|
||||
signature: 'abstract writeText(target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal): Promise<FsWriteOutcome>',
|
||||
jsDoc: '/**\n * Atomically create or replace UTF-8 text. `expected` guards intent and\n * staleness; omission allows unconditional overwrite.\n * @param target - the resolved target to write.\n * @param content - the full new file content.\n * @param expected - the write intent guarding the write; omit for unconditional.\n * @param signal - aborts before the atomic rename takes effect.\n * @returns the outcome, including the version the write produced.\n */',
|
||||
signature: 'abstract writeText( target: FsTarget, content: string, expected?: FsWriteIntent, signal?: AbortSignal, sandboxMode?: SandboxMode, ): Promise<FsWriteOutcome>',
|
||||
jsDoc: '/**\n * Atomically create or replace UTF-8 text. `expected` guards intent and\n * staleness; omission allows unconditional overwrite.\n * @param target - the resolved target to write.\n * @param content - the full new file content.\n * @param expected - the write intent guarding the write; omit for unconditional.\n * @param signal - aborts before the atomic rename takes effect.\n * @param sandboxMode - the per-call sandbox mode this write runs under; a\n * sandboxing backend fences the write by it, the bare backend ignores it.\n * Omit to leave the backend its own default.\n * @returns the outcome, including the version the write produced.\n */',
|
||||
},
|
||||
{
|
||||
signature: 'abstract editText(target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal): Promise<FsEditOutcome>',
|
||||
jsDoc: '/**\n * Atomically edit literal text. When supplied, the version guard is checked\n * before matching so stale content reports `FS_STALE_VERSION`; omission edits\n * the current content without a freshness precondition.\n * @param target - the resolved target to edit.\n * @param edit - the literal search/replace request.\n * @param expected - the version guard; omit for an unconditional edit.\n * @param signal - aborts before the atomic rename takes effect.\n * @returns the outcome, including the version the edit produced.\n */',
|
||||
signature: 'abstract editText( target: FsTarget, edit: FsEditRequest, expected?: { version: FsVersion }, signal?: AbortSignal, sandboxMode?: SandboxMode, ): Promise<FsEditOutcome>',
|
||||
jsDoc: '/**\n * Atomically edit literal text. When supplied, the version guard is checked\n * before matching so stale content reports `FS_STALE_VERSION`; omission edits\n * the current content without a freshness precondition.\n * @param target - the resolved target to edit.\n * @param edit - the literal search/replace request.\n * @param expected - the version guard; omit for an unconditional edit.\n * @param signal - aborts before the atomic rename takes effect.\n * @param sandboxMode - the per-call sandbox mode this edit runs under; a\n * sandboxing backend fences the edit by it, the bare backend ignores it.\n * Omit to leave the backend its own default.\n * @returns the outcome, including the version the edit produced.\n */',
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -304,6 +304,11 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'sandboxPolicy',
|
||||
summary: 'The sandbox-policy service (`ctx.sandboxPolicy`).',
|
||||
methods: [],
|
||||
},
|
||||
{
|
||||
key: 'sessionPersistence',
|
||||
summary: 'Durable append-only session storage.',
|
||||
@@ -1068,10 +1073,6 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
name: 'ContentBlockType',
|
||||
declaration: 'export type ContentBlockType = keyof ContentBlockMap;',
|
||||
},
|
||||
{
|
||||
name: 'ContextEnvelope',
|
||||
declaration: 'export type ContextEnvelope = \'context\' | \'raw\';',
|
||||
},
|
||||
{
|
||||
name: 'CreateAgentOptions',
|
||||
declaration: 'export interface CreateAgentOptions {\n readonly sessionId: SessionId;\n readonly meta?: {\n readonly cwd?: string;\n readonly parentSession?: SessionId;\n readonly seedLength?: number;\n };\n readonly seed?: readonly SessionEvent[];\n readonly agentOptions?: AgentOptions;\n readonly signal?: AbortSignal;\n readonly setup?: (agentCtx: Context) => Promise<void> | void;\n}',
|
||||
@@ -1170,11 +1171,11 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'HookContext',
|
||||
declaration: 'export interface HookContext {\n content: ContentBlock[];\n source: MessageSource;\n envelope?: ContextEnvelope;\n meta?: JsonValue;\n}',
|
||||
declaration: 'export interface HookContext {\n content: ContentBlock[];\n source: MessageSource;\n meta?: JsonValue;\n}',
|
||||
},
|
||||
{
|
||||
name: 'InjectOptions',
|
||||
declaration: 'export interface InjectOptions extends SendOptions {\n envelope?: ContextEnvelope;\n meta?: JsonValue;\n}',
|
||||
declaration: 'export interface InjectOptions extends SendOptions {\n meta?: JsonValue;\n}',
|
||||
},
|
||||
{
|
||||
name: 'JsonValue',
|
||||
@@ -1258,7 +1259,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'SessionEventMap',
|
||||
declaration: 'export interface SessionEventMap {\n \'turn/start\': {\n turn: number;\n trigger: TurnTrigger;\n };\n \'turn/end\': {\n turn: number;\n reason: TurnEndReason;\n };\n \'step/start\': {\n turn: number;\n step: number;\n };\n \'step/end\': {\n turn: number;\n step: number;\n };\n \'user/message\': {\n content: ContentBlock[];\n source: MessageSource;\n };\n \'prompt/blocked\': {\n content: ContentBlock[];\n source: MessageSource;\n reason: string;\n };\n \'context/message\': {\n content: ContentBlock[];\n source: MessageSource;\n envelope?: ContextEnvelope;\n meta?: JsonValue;\n };\n \'assistant/chunk\': {\n turn: number;\n step: number;\n chunk: StreamChunk;\n };\n \'assistant/message\': {\n turn: number;\n step: number;\n content: ContentBlock[];\n provenance: AssistantProvenance;\n usage?: TokenUsage;\n };\n \'tool/call\': {\n turn: number;\n step: number;\n callId: CallId;\n name: string;\n arguments: string;\n };\n \'tool/result\': {\n turn: number;\n step: number;\n callId: CallId;\n content: ContentBlock[];\n isError: boolean;\n error?: {\n name: string;\n code: string;\n };\n meta?: unknown;\n };\n \'steering/message\': {\n turn: number;\n content: ContentBlock[];\n source: MessageSource; /* …truncated — full shape in source */',
|
||||
declaration: 'export interface SessionEventMap {\n \'turn/start\': {\n turn: number;\n trigger: TurnTrigger;\n };\n \'turn/end\': {\n turn: number;\n reason: TurnEndReason;\n };\n \'step/start\': {\n turn: number;\n step: number;\n };\n \'step/end\': {\n turn: number;\n step: number;\n };\n \'user/message\': {\n content: ContentBlock[];\n source: MessageSource;\n };\n \'prompt/blocked\': {\n content: ContentBlock[];\n source: MessageSource;\n reason: string;\n };\n \'context/message\': {\n content: ContentBlock[];\n source: MessageSource;\n meta?: JsonValue;\n };\n \'assistant/chunk\': {\n turn: number;\n step: number;\n chunk: StreamChunk;\n };\n \'assistant/message\': {\n turn: number;\n step: number;\n content: ContentBlock[];\n provenance: AssistantProvenance;\n usage?: TokenUsage;\n };\n \'tool/call\': {\n turn: number;\n step: number;\n callId: CallId;\n name: string;\n arguments: string;\n };\n \'tool/result\': {\n turn: number;\n step: number;\n callId: CallId;\n content: ContentBlock[];\n isError: boolean;\n error?: {\n name: string;\n code: string;\n };\n meta?: unknown;\n };\n \'steering/message\': {\n turn: number;\n content: ContentBlock[];\n source: MessageSource;\n };\n \'todo/write\': {\n /* …truncated — full shape in source */',
|
||||
},
|
||||
{
|
||||
name: 'SessionEventReadRequest',
|
||||
|
||||
@@ -252,7 +252,6 @@ export class ReactLoopAgent implements Agent {
|
||||
const context = {
|
||||
content,
|
||||
source,
|
||||
...options?.envelope !== undefined ? { envelope: options.envelope } : {},
|
||||
...options?.meta !== undefined ? { meta: options.meta } : {},
|
||||
}
|
||||
if (isTurnOpen(this.session)) {
|
||||
|
||||
@@ -260,11 +260,10 @@ async function runTurn(
|
||||
session.append('user/message', { content, source: message.source }, { surfaceOp: 'append' })
|
||||
// Every `allow.additionalContexts` entry is a separate context/message the
|
||||
// next request also sees. The turn is open, so inject() appends each one
|
||||
// into THIS turn without flattening provenance, framing, or metadata.
|
||||
// into THIS turn without flattening provenance or metadata.
|
||||
for (const context of decision.additionalContexts ?? []) {
|
||||
agent.inject(context.content, {
|
||||
source: context.source,
|
||||
...context.envelope !== undefined ? { envelope: context.envelope } : {},
|
||||
...context.meta !== undefined ? { meta: context.meta } : {},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -99,7 +99,6 @@ describe('agent/prompt-submit', () => {
|
||||
additionalContexts: [{
|
||||
content: [{ type: 'text', text: '<system-reminder>extra ctx</system-reminder>' }],
|
||||
source: { kind: 'plugin', plugin: 'test' },
|
||||
envelope: 'raw',
|
||||
meta,
|
||||
}],
|
||||
}))
|
||||
@@ -113,7 +112,6 @@ describe('agent/prompt-submit', () => {
|
||||
expect(userMsg).toBeDefined()
|
||||
expect(ctxMsg?.type === 'context/message' && ctxMsg.data.content).toEqual([{ type: 'text', text: '<system-reminder>extra ctx</system-reminder>' }])
|
||||
expect(ctxMsg?.type === 'context/message' && ctxMsg.data.source).toEqual({ kind: 'plugin', plugin: 'test' })
|
||||
expect(ctxMsg?.type === 'context/message' && ctxMsg.data.envelope).toBe('raw')
|
||||
expect(ctxMsg?.type === 'context/message' && ctxMsg.data.meta).toEqual(meta)
|
||||
const sent = JSON.stringify(adapter.requests[0]!.messages)
|
||||
expect(sent).toContain('extra ctx')
|
||||
@@ -556,7 +554,6 @@ describe('tool additionalContexts buffering across a step', () => {
|
||||
additionalContexts: [{
|
||||
content: [{ type: 'text', text: `ctx-${exec.callId}` }],
|
||||
source: { kind: 'plugin', plugin: 'p' },
|
||||
envelope: 'raw',
|
||||
meta: { callId: exec.callId },
|
||||
}],
|
||||
}))
|
||||
@@ -580,7 +577,6 @@ describe('tool additionalContexts buffering across a step', () => {
|
||||
.map(b => (b.type === 'text' ? b.text : ''))
|
||||
expect(ctxTexts).toEqual(['ctx-c1', 'ctx-c2'])
|
||||
const contextEvents = events(agent).filter(e => e.type === 'context/message')
|
||||
expect(contextEvents.map(e => e.type === 'context/message' && e.data.envelope)).toEqual(['raw', 'raw'])
|
||||
expect(contextEvents.map(e => e.type === 'context/message' && e.data.meta)).toEqual([{ callId: 'c1' }, { callId: 'c2' }])
|
||||
})
|
||||
|
||||
@@ -591,7 +587,7 @@ describe('tool additionalContexts buffering across a step', () => {
|
||||
name: 'composite', description: 'composite', parameters: {},
|
||||
async execute(_args, exec) {
|
||||
exec.deferContext({ content: [{ type: 'text', text: 'nested-a' }], source: { kind: 'plugin', plugin: 'a' }, meta: { order: 1 } })
|
||||
exec.deferContext({ content: [{ type: 'text', text: 'nested-b' }], source: { kind: 'plugin', plugin: 'b' }, envelope: 'raw', meta: { order: 2 } })
|
||||
exec.deferContext({ content: [{ type: 'text', text: 'nested-b' }], source: { kind: 'plugin', plugin: 'b' }, meta: { order: 2 } })
|
||||
return [{ type: 'text', text: 'outer result' }]
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -385,10 +385,10 @@ describe('agent loop', () => {
|
||||
await waitForIdle(ctx, agent)
|
||||
const flat = JSON.stringify(adapter.requests[0]!.messages)
|
||||
expect(flat).toContain('file changed: a.ts')
|
||||
expect(flat).toContain('<context source=\\"plugin\\">')
|
||||
expect(flat).not.toContain('<context source=')
|
||||
})
|
||||
|
||||
it('inject() can persist raw structured context without the generic context envelope', async () => {
|
||||
it('inject() persists structured context content verbatim with durable hidden meta', async () => {
|
||||
const adapter = new MockAdapter([textResponse('ok')])
|
||||
const ctx = await harness(adapter)
|
||||
const agent = ctx.agentLoop.create(SessionId('raw-context'), { provider: 'mock', model: 'mock' })
|
||||
@@ -401,14 +401,13 @@ describe('agent loop', () => {
|
||||
|
||||
agent.inject([{ type: 'text', text }], {
|
||||
source: { kind: 'plugin', plugin: 'workspace-context' },
|
||||
envelope: 'raw',
|
||||
meta,
|
||||
})
|
||||
send(agent, 'go')
|
||||
await waitForIdle(ctx, agent)
|
||||
|
||||
const contextEvent = agent.session.events.find(event => event.type === 'context/message')
|
||||
expect(contextEvent?.type === 'context/message' && contextEvent.data).toMatchObject({ envelope: 'raw', meta })
|
||||
expect(contextEvent?.type === 'context/message' && contextEvent.data).toMatchObject({ meta })
|
||||
const requestText = JSON.stringify(adapter.requests[0]!.messages)
|
||||
expect(requestText).toContain('Additional instructions from: pkg/AGENTS.md')
|
||||
expect(requestText).not.toContain('<context source=')
|
||||
@@ -432,7 +431,6 @@ describe('agent loop', () => {
|
||||
const first = { type: 'text' as const, text: 'mid-turn notice' }
|
||||
agent.inject([first], {
|
||||
source: { kind: 'plugin', plugin: 'x' },
|
||||
envelope: 'raw',
|
||||
meta,
|
||||
})
|
||||
first.text = 'mutated after inject'
|
||||
@@ -458,7 +456,6 @@ describe('agent loop', () => {
|
||||
expect(contexts).toHaveLength(2)
|
||||
expect(result.seq).toBeLessThan(contexts[0]!.seq)
|
||||
expect(contexts[0]?.type === 'context/message' && contexts[0].data).toMatchObject({
|
||||
envelope: 'raw',
|
||||
meta,
|
||||
})
|
||||
expect(contexts.flatMap(event => event.type === 'context/message' ? event.data.content : []))
|
||||
|
||||
@@ -46,7 +46,7 @@ The lifecycle edges have two important local caveats. `agent/created` runs after
|
||||
|
||||
Most interception points are cooperative waterfalls returning seam-specific decisions. `agent/pre-step` and `agent/post-step` are serial checkpoints around a step's durable work, while `agent/request-error` is the failed-model-request recovery waterfall: a retry opens a new numbered step after the failed step closes. `agent/turn-stop` is the terminal serial fold: it runs after ordinary continuation and steering folding, and a returned stop remains in force through turn close and flush so later steering cannot create an extra step or turn. Ordinary queued prompts remain intact. The full rationale for scoped dispatch and terminal settlement is in the [agent-scope runtime-design Agent Note](../../../.agents/notes/implemented/architecture/2026-07-12-agent-scope-runtime-design.md#three-execution-boundaries-are-deliberately-one-way).
|
||||
|
||||
`PromptDecision.additionalContexts` is an array so every injected context keeps its own source, envelope, and metadata. A `ContinuationDecision` reason is narrower: it becomes a `steering/message`, not a `context/message`, and therefore carries only content and source.
|
||||
`PromptDecision.additionalContexts` is an array so every injected context keeps its own source and metadata. A `ContinuationDecision` reason is narrower: it becomes a `steering/message`, not a `context/message`, and therefore carries only content and source.
|
||||
|
||||
Turn and step boundaries and the model token stream are durable `session/event` facts rather than mirrored `agent/*` notifications. Consumers read `turn/*`, `step/*`, and `assistant/chunk` from the session feed; tool policy and outcome observation belong to the complete pipeline documented by [`dsh-tools`](../tools/README.md).
|
||||
|
||||
@@ -56,7 +56,7 @@ The handle every plugin programs against:
|
||||
|
||||
- `agent.send(content, options?)` — queue a message; starts a turn when idle. Content and resolved source become one detached, deeply frozen lossless-JSON record before `agent/queued` and enqueue; invalid data throws synchronously, and caller or notification-listener in-place mutation cannot change the log or model input (`agent/prompt-submit` still rewrites by returning replacement content).
|
||||
- `agent.steer(content, options?)` — steer a running turn (inject between steps); uses the same owned acceptance boundary and behaves like `send` when idle
|
||||
- `agent.inject(content, options?)` — accept detached in-session context without running the model; the next request sees its `context/message`. `options.envelope` defaults to the canonical `<context>` framing and may be `'raw'` when the caller owns a complete familiar frame; `options.meta` persists opaque JSON state without rendering it. While a turn is open it joins that turn, deferring FIFO while the current tool batch executes and draining before turn close if execution is interrupted; while idle it is wrapped in a one-shot `injection` turn and durability checkpoint ([the turn-enclosure invariant](../../../.agents/notes/implemented/architecture/2026-06-15-turn-enclosure-invariant.md)).
|
||||
- `agent.inject(content, options?)` — accept detached in-session context without running the model; the next request sees its `context/message` with `content` rendered verbatim as a user-role message. `options.meta` persists opaque JSON state without rendering it. While a turn is open it joins that turn, deferring FIFO while the current tool batch executes and draining before turn close if execution is interrupted; while idle it is wrapped in a one-shot `injection` turn and durability checkpoint ([the turn-enclosure invariant](../../../.agents/notes/implemented/architecture/2026-06-15-turn-enclosure-invariant.md)).
|
||||
- `agent.cancel(reason?)` — cancel ALL pending work: clears the queued + steering FIFOs, aborts the in-flight step, and drops a turn about to start (the pre-step window) so a queued-but-not-started prompt never runs. A UI/ACP `session/cancel` maps to this. The single public stop primitive. Idle with nothing pending → a safe no-op.
|
||||
- `agent.whenIdle()` — resolve once the agent reaches quiescence after settling out of `running` (idle → immediately; disposed → awaits the loop exit). A non-owner's quiescence-observation hook: it observes the work settling WITHOUT tearing the agent down. Teardown is separate — a lifecycle owner stops and unregisters via `AgentHandle.dispose()`, which awaits the loop exit directly.
|
||||
- `agent.session`, `agent.status`, `agent.options`, `agent.id`
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import type { Context } from 'cordis'
|
||||
import type { Scoped } from '@deepseek-ai/dsh-scope'
|
||||
import type { ContentBlock, LlmCallConfig, Message, MessageSource } from '@deepseek-ai/dsh-llm'
|
||||
import type { ContextEnvelope, JsonValue, Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { JsonValue, Session, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type {} from '@deepseek-ai/dsh-system-prompt'
|
||||
declare module '@deepseek-ai/dsh-system-prompt' {
|
||||
interface AssembleContext {
|
||||
@@ -32,8 +32,6 @@ export interface SendOptions {
|
||||
|
||||
/** Options specific to durable synthetic context injection. */
|
||||
export interface InjectOptions extends SendOptions {
|
||||
/** Keep the canonical context tag, or send caller-owned framing verbatim. */
|
||||
envelope?: ContextEnvelope
|
||||
/** Opaque JSON state retained in the session event but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
@@ -50,8 +48,6 @@ export type AgentStatus = 'idle' | 'running' | 'disposed'
|
||||
export interface HookContext {
|
||||
content: ContentBlock[]
|
||||
source: MessageSource
|
||||
/** Keep the canonical context tag, or use caller-owned framing verbatim. */
|
||||
envelope?: ContextEnvelope
|
||||
/** Opaque JSON state retained in the session event but hidden from the model. */
|
||||
meta?: JsonValue
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ Durable values need one accepted representation, not a check followed by a secon
|
||||
|
||||
`request/header` records a full canonical snapshot of the non-history request envelope with reason `initial`, `resume`, or `change`. `foldRequestHeader()` selects the latest snapshot; legacy delta events and the removed `fallback` reason are rejected. `messagePrefix` remains separate from derived history. See the [reconstructable-requests Agent Note](../../../.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md).
|
||||
|
||||
`context/message` defaults to the canonical tagged context projection. A producer may set `envelope: 'raw'` when its `content` already contains the complete model-facing frame, and may attach JSON `meta` for replayable plugin state; metadata remains durable but is excluded from `deriveMessages()`.
|
||||
`context/message` renders its `content` verbatim as a user-role message, and may attach JSON `meta` for replayable plugin state; metadata remains durable but is excluded from `deriveMessages()`.
|
||||
|
||||
### Session event vocabulary (`types.ts`)
|
||||
|
||||
@@ -87,7 +87,7 @@ Every `SessionEvent` carries two optional top-level fields (structural metadata)
|
||||
|
||||
#### What the model sees
|
||||
|
||||
The model receives projections of `user/message`, `assistant/message`, and `tool/result` surface entries verbatim. A `context/message` is a user-role message containing exactly `<context source="<source-kind>">`, its content blocks, and `</context>`; `steering/message` uses the identical `<steering source="<source-kind>">` / `</steering>` wrapper. Tool calls live inside assistant messages. Chunks, boundaries, usage, hook records, todo records, and other log-only events add no message.
|
||||
The model receives projections of `user/message`, `assistant/message`, `tool/result`, `context/message`, and `steering/message` surface entries verbatim: each is a user- or assistant-role message carrying its content blocks unchanged. Tool calls live inside assistant messages. Chunks, boundaries, usage, hook records, todo records, and other log-only events add no message.
|
||||
|
||||
#### Token effect
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ import { isAbsolute } from 'node:path'
|
||||
import { deepFreeze } from '@deepseek-ai/dsh-llm'
|
||||
import { scopeOf, scopeTarget } from '@deepseek-ai/dsh-scope'
|
||||
import type { Scoped } from '@deepseek-ai/dsh-scope'
|
||||
import type { ContentBlock, Message, MessageSource } from '@deepseek-ai/dsh-llm'
|
||||
import type { Message } from '@deepseek-ai/dsh-llm'
|
||||
import { SESSION_FORMAT_VERSION, SessionId } from './types.ts'
|
||||
import type { ContextEnvelope, CreateSessionOptions, EpochHeader, SessionEvent, SessionEventMap, SessionEventType, SessionHeader, SurfaceIntent, SurfaceEventType } from './types.ts'
|
||||
import type { CreateSessionOptions, EpochHeader, SessionEvent, SessionEventMap, SessionEventType, SessionHeader, SurfaceIntent, SurfaceEventType } from './types.ts'
|
||||
import { snapshotJsonValue } from './json.ts'
|
||||
import { SurfaceManager } from './surface.ts'
|
||||
import type { SessionSurface } from './surface.ts'
|
||||
@@ -80,21 +80,6 @@ declare module 'cordis' {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render injected context as tagged synthetic user-role content, keeping the
|
||||
* canonical session vocabulary provider-neutral. Adapter-specific exceptions
|
||||
* belong in the adapter.
|
||||
*/
|
||||
function renderTagged(tag: string, content: ContentBlock[], source: MessageSource): ContentBlock[] {
|
||||
const open = `<${tag} source=${JSON.stringify(source.kind)}>`
|
||||
const close = `</${tag}>`
|
||||
return [
|
||||
{ type: 'text', text: open },
|
||||
...content,
|
||||
{ type: 'text', text: close },
|
||||
]
|
||||
}
|
||||
|
||||
/** Detach, validate, and freeze the creation metadata published by a session. */
|
||||
function snapshotSessionHeader(id: SessionId, source?: SessionHeader): SessionHeader {
|
||||
const input: unknown = source === undefined
|
||||
@@ -228,22 +213,6 @@ interface SessionEntry {
|
||||
/** Store attachment for the append path; module-private to keep Session store-agnostic publicly. */
|
||||
const attachments = new WeakMap<Session, SessionEntry>()
|
||||
|
||||
/**
|
||||
* Render one context contribution exactly as it will appear in model history.
|
||||
* @param content - content blocks supplied by the context producer.
|
||||
* @param source - attribution used by the canonical context envelope.
|
||||
* @param envelope - canonical tagged framing or caller-owned raw framing.
|
||||
* @returns a detached block list ready for the derived model transcript.
|
||||
*/
|
||||
export function renderContextContent(
|
||||
content: ContentBlock[],
|
||||
source: MessageSource,
|
||||
envelope: ContextEnvelope = 'context',
|
||||
): ContentBlock[] {
|
||||
const cloned = structuredClone(content)
|
||||
return envelope === 'raw' ? cloned : renderTagged('context', cloned, source)
|
||||
}
|
||||
|
||||
/**
|
||||
* An event-sourced session: an append-only log of {@link SessionEvent}s.
|
||||
*
|
||||
@@ -509,7 +478,18 @@ export class Session {
|
||||
// trace/replay data.
|
||||
|
||||
switch (event.type) {
|
||||
case 'user/message': {
|
||||
// Injected context and mid-turn steering project identically to a user
|
||||
// prompt: content verbatim, in user role. context's `source`/`meta` and
|
||||
// steering's `turn` are log-only and do not reach the model. Do NOT
|
||||
// re-add per-type framing (e.g. `<context>`/`<steering>`) here: framing is
|
||||
// caller-owned — a producer bakes it into `content`, as workspace-context
|
||||
// does with `<system-reminder>` — or, if reintroduced, must be driven by
|
||||
// the event `meta` map and a dedicated renderer, keeping this projection a
|
||||
// verbatim pass-through. See the deferred design note in
|
||||
// ../../../../.agents/notes/implemented/simplification/2026-07-20-unwrap-injected-content-envelopes.md
|
||||
case 'user/message':
|
||||
case 'context/message':
|
||||
case 'steering/message': {
|
||||
return { role: 'user', content: event.data.content }
|
||||
}
|
||||
case 'assistant/message': {
|
||||
@@ -526,14 +506,6 @@ export class Session {
|
||||
content: [{ type: 'tool-result', toolCallId: callId, content, isError }],
|
||||
}
|
||||
}
|
||||
case 'context/message': {
|
||||
const { content, source, envelope } = event.data
|
||||
return { role: 'user', content: renderContextContent(content, source, envelope) }
|
||||
}
|
||||
case 'steering/message': {
|
||||
const { content, source } = event.data
|
||||
return { role: 'user', content: renderTagged('steering', content, source) }
|
||||
}
|
||||
default:
|
||||
// A non-surface event (boundary, chunk, log-only record) projects to
|
||||
// no message. Merge-extensible union: no assertNever here.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user