Merge remote-tracking branch 'origin/master' into cross-family-fs-sandbox

# Conflicts:
#	docs/config-catalog.md
#	docs/cordis-catalog/events.md
#	docs/cordis-catalog/services.md
#	docs/event-producer-consumer.md
#	docs/persistence-catalog.md
#	examples/acp-agent/cordis.yml
#	examples/acp-agent/tests/snapshots/advanced-toolchain/session.1.jsonl
#	examples/acp-agent/tests/snapshots/advanced-toolchain/session.2.jsonl
#	examples/acp-agent/tests/snapshots/advanced-toolchain/session.jsonl
#	examples/acp-agent/tests/snapshots/both-mode-turn/session.jsonl
#	examples/acp-agent/tests/snapshots/permission-switching/session.jsonl
#	examples/acp-agent/tests/snapshots/skill-load/session.jsonl
#	examples/acp-agent/tests/snapshots/text-turn/session.jsonl
#	packages/bash/bash-sandbox/src/index.ts
#	packages/bash/bash/src/types.ts
#	packages/bash/tool-bash/src/index.ts
#	packages/fs/fs/src/index.ts
#	packages/sandbox/sandbox-policy/src/session-mode.ts
#	packages/ui/permission/src/index.ts
#	packages/ui/permission/tests/permission.spec.ts
#	scripts/doc-budgets.manifest.json
This commit is contained in:
kingwl
2026-07-14 21:25:36 +08:00
608 changed files with 7195 additions and 12508 deletions

View File

@@ -1,13 +1,7 @@
/**
* The model-facing `write` tool: create or fully replace a UTF-8 text file. The
* tool is the executor: it dispatches the `fs/write-intent` waterfall to
* obtain the optional version guard, calls `ctx.fs.writeText` directly, and
* emits `fs/observed`. The default thunk returns `undefined` (unconditional
* create-or-overwrite — the bare provider); a policy plugin
* (`@deepseek-ai/dsh-fs-policy`) occupies the single decision slot and
* returns `createIfAbsent`/`replaceIfVersion` instead. The tool stats ZERO
* times either way.
*
* Model-facing full-file write. It obtains an optional intent from the single policy slot, calls
* `ctx.fs.writeText` without a stat, then records the resulting version; no policy means an
* unconditional atomic create-or-overwrite.
* @module @deepseek-ai/dsh-tool-fs/src/write
*/
@@ -101,20 +95,17 @@ export function applyWriteTool(ctx: Context, sandbox: FsSandboxSurface): void {
}
// Record the observed version (a no-op when no policy plugin listens).
ctx.emit('fs/observed', target, outcome.version, exec)
// Attach a contextual hunk as `meta` ONLY for an overwrite (a before-version
// exists). A create has no "before" — `outcome.before` is null — so it
// carries no `meta`; `presentResult` then renders a whole-file diff from the
// args, so the completed card is still a diff (never the result text).
// Overwrites carry applied hunks. Creates have no prior text, so result presentation uses
// the args-derived whole-file diff instead.
const diffs = outcome.before !== null ? computeHunkDiffs(input.filePath, outcome.before, outcome.after) : []
return {
content: [{ type: 'text', text: formatWriteOutput(target.displayPath, outcome) }],
...diffs.length > 0 ? { meta: { diffs } } : {},
}
},
// Pure display: a diff card (an editor renders write as a new-file / full-
// replace diff). `oldText: null` — a call-time presenter has no access to the
// file's prior content, so even an overwrite renders new-file style, matching
// claude-agent-acp. A follow-along location points at the written file.
// Pure display: a diff card (an editor renders write as a new-file / full- replace diff).
// `oldText: null` — a call-time presenter has no access to the file's prior content, so
// even an overwrite renders new-file style, matching claude-agent-acp.
presentCall(args): DiffCallView {
return {
card: 'diff',
@@ -123,14 +114,10 @@ export function applyWriteTool(ctx: Context, sandbox: FsSandboxSurface): void {
locations: [{ path: args.file_path }],
}
},
// Result-time display: a `diff` card so the completed `tool_call_update`
// re-installs the diff rather than the model-facing result text (an ACP
// `tool_call_update.content` REPLACES the call's content, so a text result
// would clobber the pending diff card). An OVERWRITE uses the applied
// contextual hunks on `meta`; a CREATE has no `meta` (no prior content), so
// its whole-file new-file diff is derived from `args.content` (replay-safe,
// matching the call-time card). An error falls through to generic rendering
// so its message shows.
// Result-time display: a `diff` card so the completed `tool_call_update` re-installs the
// diff rather than the model-facing result text (an ACP `tool_call_update.content` REPLACES
// the call's content, so a text result would clobber the pending diff card). Overwrites use
// applied metadata; creates and identical overwrites use the replay-safe args fallback.
presentResult(args, result: ToolResult): DiffResultView | undefined {
if (result.isError) return undefined
const diffs = diffsFromMeta(result.meta)