Merge remote-tracking branch 'origin/master' into feat/plan-mode

# Conflicts:
#	docs/config-catalog.md
#	docs/cordis-catalog/services.md
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	packages/README.md
#	packages/bash/tool-bash/src/index.ts
#	packages/core/tools/tests/gen-tool-catalog.spec.ts
#	pnpm-lock.yaml
#	scripts/doc-budgets.manifest.json
This commit is contained in:
Tianyi Cui
2026-07-17 21:22:16 +08:00
357 files changed
+18449 -2870

No files matched your search

+2 -2
View File
@@ -28,11 +28,11 @@ Implementations subclass `BashExecutor` and implement the abstract methods. Disp
## Vocabulary
`BashExecRequest` (command, workdir?, timeoutMs?, signal?, stdin?, env?, sandboxMode?) resolves to `BashExecSpec` (command, workdir, timeoutMs, signal?, stdin?, env?, sandboxMode) before execution. `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.
`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, the `setSandboxMode(session, mode)` write path, and THE read path `resolveMode(session)` above, which folds the override and runs the `bash/resolve-mode` waterfall around it. `SANDBOX_MODES` is the narrowest→widest ladder; the ordering is part of the contract (the escalation widening check and a mode's access clamp compare by index). `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 `env` are set by in-process plugins (the hooks bridges, native plugins) to feed a hook command its JSON payload on stdin and its `CLAUDE_PROJECT_DIR`/`CLAUDE_PLUGIN_ROOT` env. The model-facing `dsh-tool-bash` tool does not expose them as parameters — a model already has equivalent power through shell syntax (`FOO=bar cmd`, a heredoc), so they would be redundant tool params. This is not a security boundary: the implementation's credential scrub (not these fields) is what keeps the harness's ambient secrets out of a spawned command. They are plain optionals on the resolved spec; a missing value means "none". See [the bash-stdin-env RFC](../../../docs/rfc/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.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 RFC](../../../docs/rfc/implemented/architecture/2026-06-30-bash-stdin-env-trusted-plugin-surface.md) and [the session environment RFC](../../../docs/rfc/implemented/feature/2026-07-10-agent-session-identity-and-log-location.md).
## Model Experience
+3
View File
@@ -11,6 +11,7 @@ import type { Session } from '@deepseek-ai/dsh-session'
import { effectiveSandboxMode } from './session-mode.ts'
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,
@@ -21,6 +22,8 @@ export type {
BashRunResult,
BashSandboxInfo,
CollectedOutput,
DshEnvironment,
DshEnvironmentKey,
} from './types.ts'
declare module 'cordis' {
+51 -11
View File
@@ -6,6 +6,15 @@
import type { SandboxEnforcement, SandboxMode } from '@deepseek-ai/dsh-sandbox'
/** Namespace prefix reserved for DeepSeek Harness-managed child environment facts. */
export const DSH_ENV_PREFIX = 'DSH_' as const
/** One environment key inside the managed {@link DSH_ENV_PREFIX} namespace. */
export type DshEnvironmentKey = `${typeof DSH_ENV_PREFIX}${string}`
/** Trusted DeepSeek Harness variables for one bash execution. */
export type DshEnvironment = Readonly<Record<DshEnvironmentKey, string>>
/**
* Sandbox facts for one run, present iff a sandboxing executor handled it.
* Facts are reported independently of process exit status so callers can
@@ -34,6 +43,13 @@ export interface BashExecRequest {
workdir?: string | undefined
/** Timeout override in milliseconds (implementations cap it). */
timeoutMs?: number | undefined
/**
* Foreground stdout capture budget in bytes. Absent uses the executor's
* default output cap. Trusted in-process consumers use this when they must
* parse complete stdout up to their own bounded limit; the model-facing bash
* tool does not expose it as a parameter.
*/
stdoutMaxBytes?: number | undefined
/** Abort signal — implementations kill the command when it fires. */
signal?: AbortSignal | undefined
/**
@@ -45,15 +61,20 @@ export interface BashExecRequest {
*/
stdin?: string | undefined
/**
* Extra environment entries for the command, merged AFTER the
* implementation's credential scrub (so an explicit entry here is honored even
* when its name matches the scrub pattern — the caller named a value it holds,
* not the harness's ambient secret). Set by in-process plugins (the hooks
* bridges set `CLAUDE_PROJECT_DIR`, `CLAUDE_PLUGIN_ROOT`, …); the model-facing
* bash tool does not expose it as a parameter (a model that needs an env var
* uses shell syntax like `FOO=bar cmd`).
* Ordinary environment entries for the command, merged after the credential
* scrub. `DSH_*` is reserved for {@link dshEnv} and implementations reject it
* here. Set by in-process plugins (the hooks bridges set
* `CLAUDE_PROJECT_DIR`, `CLAUDE_PLUGIN_ROOT`, …); the model-facing bash tool
* does not expose it as a parameter.
*/
env?: Record<string, string> | undefined
/**
* Harness-owned `DSH_*` variables for this execution. Executors discard
* ambient `DSH_*` entries before merging this snapshot, so an unavailable
* current fact cannot inherit a stale value from the harness process, and
* reject non-`DSH_*` names supplied through this managed channel.
*/
dshEnv?: DshEnvironment | undefined
/** Explicit per-call sandbox mode override. */
sandboxMode?: SandboxMode | undefined
}
@@ -67,15 +88,24 @@ export interface BashExecSpec {
command: string
workdir: string
timeoutMs: number
/**
* Resolved foreground stdout capture budget in bytes. `run()` uses it for
* stdout; background tasks and stderr keep the executor's own output cap.
*/
stdoutMaxBytes: number
/** Abort signal — implementations kill the command when it fires. */
signal?: AbortSignal | undefined
/** Bytes to write to stdin before closing it; absent means no stdin. */
stdin?: string | undefined
/**
* Extra environment entries, merged after credential scrubbing so explicit
* values win; absent means no extra entries.
* Ordinary environment entries carried through from
* {@link BashExecRequest.env}. `DSH_*` remains reserved for {@link dshEnv}.
* OPTIONAL on the spec for the same reason as `stdin`: absent means no
* ordinary extra environment.
*/
env?: Record<string, string> | undefined
/** Managed `DSH_*` snapshot; implementations reject ordinary names. */
dshEnv?: DshEnvironment | undefined
/** Resolved sandbox mode; ignored by executors that do not confine. */
sandboxMode: SandboxMode | undefined
}
@@ -96,9 +126,19 @@ export interface BashRunResult {
exitCode: number | null
/** Terminating signal (e.g. 'SIGTERM'); null on normal exit. */
signal: NodeJS.Signals | null
/** True when the executor's own timeout killed the command. */
/**
* True when the executor's own timeout was the FIRST cause to cut the command
* short. Mutually exclusive with {@link aborted}: one fused deadline drives
* both the timeout and the caller's cancellation, so a timeout and an abort
* racing before process close report the single first-abort cause, not both
* (see the [timeout-library RFC](../../../../docs/rfc/implemented/architecture/2026-07-06-timeout-deadline-library.md)).
*/
timedOut: boolean
/** True when the caller's AbortSignal killed the command. */
/**
* True when the caller's `AbortSignal` was the FIRST cause to kill the command
* (and it was not the executor's own timeout). Mutually exclusive with
* {@link timedOut} — see there for the first-cause classification.
*/
aborted: boolean
/** The effective timeout applied to this run (after defaulting/capping). */
timeoutMs: number
+2 -1
View File
@@ -17,6 +17,7 @@ class StubExecutor extends BashExecutor {
command: request.command,
workdir: request.workdir ?? '/stub',
timeoutMs: request.timeoutMs ?? 1000,
stdoutMaxBytes: request.stdoutMaxBytes ?? 64_000,
...request.signal ? { signal: request.signal } : {},
sandboxMode: request.sandboxMode,
}
@@ -56,7 +57,7 @@ describe('BashExecutor service seam', () => {
const ctx = new Context()
await ctx.plugin(StubExecutor)
const spec = ctx.bash.resolve({ command: 'echo hi' })
expect(spec).toEqual({ command: 'echo hi', workdir: '/stub', timeoutMs: 1000, sandboxMode: undefined })
expect(spec).toEqual({ command: 'echo hi', workdir: '/stub', timeoutMs: 1000, stdoutMaxBytes: 64_000, sandboxMode: undefined })
const result = await ctx.bash.run(spec)
expect(result.exitCode).toBe(0)