Merge branch 'codex/invariant-service-seam' into codex/invariant-package-registration-gate

This commit is contained in:
Tianyi Cui
2026-07-20 19:48:51 +08:00
391 changed files with 15415 additions and 8532 deletions

View File

@@ -0,0 +1,84 @@
/**
* The sandbox POLICY home (`ctx.sandboxPolicy`): the single owner of the
* deployment's sandbox default — the file-effect {@link SandboxMode} a session
* starts from and the `workspace-write` boundary root — plus the per-session
* override kit (the `sandbox/mode` event, its fold, and its write path, from
* `./session-mode.ts`).
*
* Both enforcing capability families read the SAME policy here: the sandboxed
* bash executor (`@deepseek-ai/dsh-bash-sandbox`) and the sandboxed filesystem
* provider (`@deepseek-ai/dsh-fs-sandbox`) inject `ctx.sandboxPolicy` for the
* default mode and workspace root, so bash and fs can never confine to
* different roots — the split world the sandbox RFC warns about. The default
* lives here rather than on either executor's config precisely because it is
* one fact two families share.
*
* This service holds only the DEFAULT; the per-session fold
* ({@link effectiveSandboxMode}) is a pure function the tool layers apply to
* stamp each call, so neither the executor nor the provider depends on session
* events.
*
* @module @deepseek-ai/dsh-sandbox-policy
*/
import { resolve } from 'node:path'
import { Context, Service } from 'cordis'
import z from 'schemastery'
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox'
export { SANDBOX_MODES, effectiveSandboxMode, setSandboxMode } from './session-mode.ts'
declare module 'cordis' {
interface Context {
sandboxPolicy: SandboxPolicyService
}
}
/**
* 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
}
/**
* The sandbox-policy service (`ctx.sandboxPolicy`). Owns the deployment
* default mode and workspace root; enforcing implementations read
* {@link defaultMode} and {@link workspaceRoot}, and the tool layers fold each
* session's `sandbox/mode` override with {@link effectiveSandboxMode} on top.
*/
export class SandboxPolicyService extends Service {
// Inline schema call: the config catalog walks `static Config` statically.
static Config: z<Config> = z.object({
mode: z.union(['read-only', 'workspace-write', 'danger-full-access'] as const).default('read-only'),
// No schema default: process.cwd() is resolved in the constructor so the
// stored root is always absolute regardless of how it was supplied.
workspaceRoot: z.string(),
})
/** The deployment default mode — the fallback beneath a session override. */
readonly defaultMode: SandboxMode
/** The absolute `workspace-write` boundary root both families fence against. */
readonly workspaceRoot: string
constructor(ctx: Context, config: Config) {
super(ctx, 'sandboxPolicy')
// schemastery (static Config) already filled `mode`; the cast records that
// runtime fact. `workspaceRoot` has NO schema default, so its fallback to
// the process cwd is real branching, resolved absolute either way.
this.defaultMode = config.mode as SandboxMode
this.workspaceRoot = resolve(config.workspaceRoot ?? process.cwd())
}
}
export default SandboxPolicyService

View File

@@ -0,0 +1,27 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-sandbox-policy`.
* @module @deepseek-ai/dsh-sandbox-policy/invariant
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-sandbox-policy'
/** Cordis companion plugin name. */
export const name = 'sandbox-policy-invariant'
/** Services required before the companion can register. */
export const inject = ['invariants']
/** No runtime invariant: the follow-up checks package-owned sandbox-mode events once this topology gate lands. */
const install: InvariantInstaller = () => {}
/**
* Register this package's invariant companion.
* @param ctx - Cordis context carrying the invariant service.
* @returns the installed registration's disposer after setup succeeds.
*/
export const apply = (ctx: Context): Promise<() => void> =>
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
/* jscpd:ignore-end */

View File

@@ -0,0 +1,68 @@
/**
* 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 `sandbox/mode` event on the session it applies to;
* `effective = fold(events) ?? the deployment 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 boundary
* markers in the enforcing tools, never from the event itself. EXECUTION
* honors the fold in each tool layer — it stamps the effective mode onto the
* per-call policy carrier (a bash request's `sandboxMode`, an fs mutation's
* `sandboxMode`), weakest-precedence beneath an escalation grant.
*
* The override is policy state shared by every enforcing family (bash and
* filesystem alike), so it lives here in the policy package rather than in any
* one capability's seam.
*
* @module dsh-sandbox-policy/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).
*/
'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 `sandbox/mode` event in the
* log, or undefined when the session never switched (callers apply the
* deployment 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 === 'sandbox/mode') return event.data.mode
}
return undefined
}
/**
* THE write path for a session's sandbox-mode override: appends exactly one
* `sandbox/mode` event — the switch IS its event; nothing mutates mode state
* out of band. Takes effect on the session's next confined call (bash or fs)
* — the consumers fold on every read.
* @param session - the session the override belongs to.
* @param mode - the mode every subsequent confined call in this session runs
* under (until the next switch).
*/
export function setSandboxMode(session: Session, mode: SandboxMode): void {
session.append('sandbox/mode', { mode })
}