Merge remote-tracking branch 'origin/master' into codex/rfc-subagent-background-tasks
# Conflicts: # docs/config-catalog.md # docs/cordis-catalog/services.md # docs/core-data-structures/bash.md # docs/core-data-structures/core.md # docs/event-producer-consumer.md # docs/module-graph.md # docs/tool-catalog.md # examples/acp-agent/tests/snapshots/both-mode-turn/session.jsonl # examples/acp-agent/tests/snapshots/code-mode-turn/session.jsonl # examples/acp-agent/tests/snapshots/text-turn/session.jsonl # packages/README.md # packages/bash/README.md # packages/bash/bash-local/src/index.ts # packages/bash/bash/README.md # packages/bash/bash/package.json # packages/bash/bash/src/index.ts # packages/bash/bash/src/types.ts # packages/bash/bash/tests/service.spec.ts # packages/bash/bash/tsconfig.json # packages/bash/tool-bash/README.md # packages/bash/tool-bash/src/index.ts # packages/bash/tool-bash/tests/tools.spec.ts # packages/bash/tool-bash/tsconfig.json # packages/cordis/tool-cordis/src/api-catalog.ts # packages/core/agent-core/README.md # packages/core/agent-core/package.json # packages/core/agent-core/src/index.ts # packages/core/agent-core/tests/agent-core.spec.ts # packages/core/tools/tests/gen-tool-catalog.spec.ts # packages/hooks/hook-protocol/tests/runner.spec.ts # packages/ui/acp-agent/tests/acp-agent.spec.ts # packages/ui/stdio-agent/tests/stdio-agent.spec.ts # pnpm-lock.yaml # scripts/doc-budgets.manifest.json # scripts/gen-tool-catalog.ts
This commit is contained in:
193 files changed
+11980
-803
No files matched your search
@@ -22,8 +22,10 @@
|
||||
*/
|
||||
|
||||
import { Context, Service } from 'cordis'
|
||||
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
|
||||
import type { BashExecRequest, BashExecSpec, BashProcess, BashRunResult } from './types.ts'
|
||||
|
||||
export { SANDBOX_MODES, effectiveSandboxMode, setSandboxMode } from './session-mode.ts'
|
||||
export type {
|
||||
BashExecRequest,
|
||||
BashExecSpec,
|
||||
@@ -31,6 +33,7 @@ export type {
|
||||
BashProcessRead,
|
||||
BashProcessStatus,
|
||||
BashRunResult,
|
||||
BashSandboxInfo,
|
||||
CollectedOutput,
|
||||
} from './types.ts'
|
||||
|
||||
@@ -67,6 +70,15 @@ export abstract class BashExecutor extends Service {
|
||||
super(ctx, 'bash')
|
||||
}
|
||||
|
||||
/**
|
||||
* The sandbox mode this executor applies by default, or `undefined` when it
|
||||
* does not sandbox commands.
|
||||
* @returns the configured default sandbox mode, when supported.
|
||||
*/
|
||||
get sandboxMode(): SandboxMode | undefined {
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a caller's {@link BashExecRequest} into a fully-specified
|
||||
* {@link BashExecSpec}, applying this implementation's config defaults and
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Per-session sandbox-mode override: the session log as the store. A runtime
|
||||
* switch (an ACP `session/set_config_option`, a test scenario) is recorded as
|
||||
* one `bash/sandbox-mode` event on the session it applies to;
|
||||
* `effective = fold(events) ?? the executor's configured default`, so an
|
||||
* override survives restart by replay, two sessions can never see each
|
||||
* other's state, and there is no external config store. The event is
|
||||
* log-only (the `approval/*` precedent): the model learns the mode from the
|
||||
* prompt section and the boundary notices in `@deepseek-ai/dsh-tool-bash`,
|
||||
* never from the event itself. EXECUTION honors the fold in the tool layer —
|
||||
* it stamps the effective mode onto each call's `BashExecRequest.sandboxMode`
|
||||
* (weakest-precedence: an escalation grant for the call outranks it) — the
|
||||
* executor itself stays a config-fixed default plus per-call overrides.
|
||||
*
|
||||
* @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 {
|
||||
/**
|
||||
* 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).
|
||||
*/
|
||||
'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 (callers apply the
|
||||
* executor's configured default). The pure fold — resume needs no catch-up
|
||||
* machinery because replaying the log IS the 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
|
||||
}
|
||||
|
||||
/**
|
||||
* THE write path for a session's sandbox-mode override: appends exactly one
|
||||
* `bash/sandbox-mode` event — the switch IS its event; nothing mutates mode
|
||||
* state out of band. Takes effect on the session's next bash call and next
|
||||
* prompt assembly (the consumers fold on every read).
|
||||
* @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 })
|
||||
}
|
||||
@@ -12,6 +12,24 @@
|
||||
* @module dsh-bash/types
|
||||
*/
|
||||
|
||||
import type { SandboxEnforcement, SandboxMode } from '@deepseek-ai/dsh-sandbox'
|
||||
|
||||
/**
|
||||
* Sandbox facts for one run, present iff a sandboxing executor handled it.
|
||||
* Facts are reported independently of process exit status so callers can
|
||||
* distinguish command failures from policy denials and runner failures.
|
||||
*/
|
||||
export interface BashSandboxInfo {
|
||||
/** The mode the command actually ran under. */
|
||||
mode: SandboxMode
|
||||
/** Whether the sandbox denied a file operation. */
|
||||
denied: boolean
|
||||
/** How completely the selected runner enforced the requested mode. */
|
||||
enforcement?: SandboxEnforcement
|
||||
/** Whether the sandbox runner failed before the command could run. */
|
||||
runnerFailed?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* A caller's execution REQUEST: `workdir` and `timeoutMs` are optional and
|
||||
* filled by {@link BashExecutor.resolve} from the implementation's config.
|
||||
@@ -44,6 +62,8 @@ export interface BashExecRequest {
|
||||
* uses shell syntax like `FOO=bar cmd`).
|
||||
*/
|
||||
env?: Record<string, string> | undefined
|
||||
/** Explicit per-call sandbox mode override. */
|
||||
sandboxMode?: SandboxMode | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,6 +96,8 @@ export interface BashExecSpec {
|
||||
* config default, absent means "no extra env".
|
||||
*/
|
||||
env?: Record<string, string> | undefined
|
||||
/** Resolved sandbox mode; ignored by executors that do not confine. */
|
||||
sandboxMode: SandboxMode | undefined
|
||||
}
|
||||
|
||||
/** One captured stream: the (possibly truncated) text plus recovery info. */
|
||||
@@ -102,6 +124,8 @@ export interface BashRunResult {
|
||||
timeoutMs: number
|
||||
stdout: CollectedOutput
|
||||
stderr: CollectedOutput
|
||||
/** Sandbox execution facts, absent for an unsandboxed executor. */
|
||||
sandbox?: BashSandboxInfo
|
||||
}
|
||||
|
||||
/** Lifecycle of a background process. */
|
||||
@@ -138,6 +162,8 @@ export interface BashProcess {
|
||||
signal: NodeJS.Signals | null
|
||||
/** Resolves when the underlying process closes (never rejects — a spawn failure settles as `killed` with the error on stderr). */
|
||||
readonly done: Promise<void>
|
||||
/** Sandbox facts, stamped once a confined process settles. */
|
||||
sandbox?: BashSandboxInfo
|
||||
/**
|
||||
* Read output produced since the previous read (consuming — consecutive
|
||||
* reads never re-deliver). Reads that lost data flag `lossy` and point at
|
||||
|
||||
Reference in New Issue
Block a user