diff --git a/docs/architecture.md b/docs/architecture.md
index 371b5df579..02dac4a2dd 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -26,6 +26,7 @@ The default distribution is a composition, not a hierarchy. `packages/core/` is
|---|---|---|
| `ctx.llm` | [`llm/`](../packages/llm/README.md) | adapter registry and streaming model calls |
| `ctx.bash` | [`bash/`](../packages/bash/README.md) | foreground/background command execution |
+| `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.web` | [`web/`](../packages/web/README.md) | search/fetch provider registries |
| `ctx.compact` | [`compact/`](../packages/compact/README.md) | session-surface compaction |
diff --git a/docs/capability-seams.md b/docs/capability-seams.md
index 414ee898d1..339954c00f 100644
--- a/docs/capability-seams.md
+++ b/docs/capability-seams.md
@@ -42,6 +42,8 @@ flowchart LR
pkg_bash_local["bash-local"]
pkg_hooks_claude["hooks-claude"]
pkg_hooks_codex["hooks-codex"]
+ pkg_code_runtime["code-runtime"]
+ svc_codeRuntime["ctx.codeRuntime
Code-execution seam"]
pkg_fs["fs"]
svc_fs["ctx.fs
Filesystem provider seam"]
pkg_fs_local["fs-local"]
@@ -64,6 +66,7 @@ flowchart LR
pkg_agent_loop --> svc_agentLoop
pkg_bash --> svc_bash
pkg_bash_local --> svc_bash
+ pkg_code_runtime --> svc_codeRuntime
pkg_compact --> svc_compact
pkg_compact_basic --> svc_compact
pkg_fs --> svc_fs
@@ -134,6 +137,7 @@ flowchart LR
| `ctx.agents` | `core` | [`agent`](../packages/core/agent) | - | [`agent-loop`](../packages/core/agent-loop), [`acp`](../packages/ui/acp), [`subagent-inprocess`](../packages/subagent/subagent-inprocess), [`stdio-agent`](../packages/ui/stdio-agent), [`invariants`](../packages/support/invariants) | - | Owns live Agent handles and the create/resume factory seam. |
| `ctx.agentLoop` | `bundle` | [`agent-loop`](../packages/core/agent-loop) | - | [`agent-core`](../packages/core/agent-core) | - | The one concrete loop plugin; extension packages depend on dsh-agent events and services, not on this package. |
| `ctx.bash` | `seam` | [`bash`](../packages/bash/bash) | [`bash-local`](../packages/bash/bash-local) | [`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 can replace bash-local. |
+| `ctx.codeRuntime` | `seam` | [`code-runtime`](../packages/code-runtime/code-runtime) | - | - | - | Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the Code Mode RFC specifies the worker-thread backend and the tool-registry consumer). |
| `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.compact` | `seam` | [`compact`](../packages/compact/compact) | [`compact-basic`](../packages/compact/compact-basic) | [`compact-basic`](../packages/compact/compact-basic) | - | The basic backend currently consumes the pre-step event directly; 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), [`subagent-mock`](../packages/support/subagent-mock) | [`tool-subagent`](../packages/subagent/tool-subagent) | - | Providers implement transports; tool-subagent exposes one configured provider as a model-facing tool name. |
diff --git a/docs/config-catalog.md b/docs/config-catalog.md
index e268f63da6..e55066101f 100644
--- a/docs/config-catalog.md
+++ b/docs/config-catalog.md
@@ -802,6 +802,7 @@ These load from a `cordis.yml` entry with no `config:` block; they declare no co
Abstract service classes — a deployment loads a concrete implementation package instead ([capability seams](rfc/implemented/architecture/2026-06-13-capability-seams.md)).
- `@deepseek-ai/dsh-bash` — abstract `BashExecutor` ([`packages/bash/bash/src/index.ts`](../packages/bash/bash/src/index.ts))
+- `@deepseek-ai/dsh-code-runtime` — abstract `CodeRuntime` ([`packages/code-runtime/code-runtime/src/index.ts`](../packages/code-runtime/code-runtime/src/index.ts))
- `@deepseek-ai/dsh-compact` — abstract `CompactService` ([`packages/compact/compact/src/index.ts`](../packages/compact/compact/src/index.ts))
- `@deepseek-ai/dsh-fs` — abstract `FileSystem` ([`packages/fs/fs/src/index.ts`](../packages/fs/fs/src/index.ts))
- `@deepseek-ai/dsh-session-persistence` — abstract `SessionPersistence` ([`packages/session-persistence/session-persistence/src/index.ts`](../packages/session-persistence/session-persistence/src/index.ts))
diff --git a/docs/cordis-catalog/services.md b/docs/cordis-catalog/services.md
index f7f2734a9f..26126c4481 100644
--- a/docs/cordis-catalog/services.md
+++ b/docs/cordis-catalog/services.md
@@ -67,6 +67,25 @@ Types: [BashExecRequest](../core-data-structures/bash.md) · [BashExecSpec](../c
Source: [`packages/bash/bash/src/index.ts:59`](../../packages/bash/bash/src/index.ts)
+## `ctx.codeRuntime` — `CodeRuntime` (abstract seam)
+
+Abstract code-execution service. Subclass, implement run and the two descriptors, and load the subclass as a plugin — it registers as `ctx.codeRuntime` (one implementation per context; loading a second throws, cordis' standard duplicate-service behavior).
+
+Semantics every implementation must honor:
+
+- run resolves with an error FIELD for every program outcome — parse/transform failures, thrown exceptions, budget expiry, abort, substrate death (CodeRunFailure's taxonomy). It REJECTS only for caller misuse of the seam itself (e.g. a run submitted after disposal).
+- Binding calls bridge to the caller's CodeBindingFunctions verbatim; arguments and resolutions must be structured-cloneable, and the runtime treats the program as a hostile peer (arbitrary binding names are own properties, malformed traffic is rejected or ignored, never crashes the host).
+- Runs are isolated from each other: no state survives from one run to the next through the runtime.
+- Disposal reaches quiescence: in-flight runs are terminated AND awaited before the service's own teardown completes (no orphan substrate survives `fiber.dispose()`).
+
+```ts cordis-catalog
+abstract run(request: CodeRunRequest): Promise
+```
+
+Types: [CodeRunRequest](../core-data-structures/code-runtime.md) · [CodeRunResult](../core-data-structures/code-runtime.md)
+
+Source: [`packages/code-runtime/code-runtime/src/index.ts:59`](../../packages/code-runtime/code-runtime/src/index.ts)
+
## `ctx.compact` — `CompactService` (abstract seam)
Abstract compaction service. Subclass implement the two abstract methods, and load the subclass as a plugin — it registers as `ctx.compact` (one implementation per context; loading a second throws, which is cordis' standard duplicate-service behavior).
diff --git a/docs/core-data-structures/code-runtime.md b/docs/core-data-structures/code-runtime.md
new file mode 100644
index 0000000000..1f87e8e8a4
--- /dev/null
+++ b/docs/core-data-structures/code-runtime.md
@@ -0,0 +1,94 @@
+# Code Runtime
+
+The code-execution seam — a [capability seam](../rfc/implemented/architecture/2026-06-13-capability-seams.md) whose interface ([dsh-code-runtime](../../packages/code-runtime/code-runtime), `ctx.codeRuntime`) runs one model-written program against host-provided async bindings and reports what it printed and returned. Code execution is **one optional capability**, not part of the agent-loop spine — so its vocabulary lives here, not in [core.md](core.md). Backends differ by execution substrate and source language, both readonly descriptors on the service; the worker-thread backend and the tool-registry consumer (Code Mode) are specified in the [Code Mode RFC](../rfc/proposed/feature/2026-06-15-code-mode.md).
+
+Source: [`packages/code-runtime/code-runtime/src/types.ts`](../../packages/code-runtime/code-runtime/src/types.ts)
+
+## The run: request in, result out
+
+A `CodeRunRequest` carries **everything the runtime acts on** — per the "explicit > implicit at package seams" rule, defaulting (time budgets, output caps) is the implementation's validated config, never a hidden `??` inside `run()`:
+
+```ts type-equiv
+interface CodeRunRequest {
+ /**
+ * The program source, in the runtime's {@link ../index.ts | language}. It
+ * runs as the body of an async function: top-level `await` and `return`
+ * are available, and the completion value becomes
+ * {@link CodeRunResult.value}.
+ */
+ program: string
+ /** Host functions exposed to the program, one global object per namespace. */
+ bindings: CodeBindingNamespace[]
+ /**
+ * Abort the run: the runtime stops the program (hard, even mid-loop) and
+ * resolves with a {@link CodeRunFailure} of kind `'abort'`. In-flight
+ * binding calls are the CALLER's to settle — the runtime only stops asking.
+ */
+ signal?: AbortSignal
+}
+```
+
+The result reports an error as a **field**, never a rejection of `run()` — reporting a failed program is the caller's job, not an exception path (mirroring `BashExecutor.run`'s resolve-on-failure contract):
+
+```ts type-equiv
+interface CodeRunResult {
+ /**
+ * The program's completion value (its top-level `return`), when it ran to
+ * completion and the value survived the runtime's serialization boundary;
+ * a non-transferable value is replaced by a string rendering, and a failed
+ * or value-less run leaves this absent.
+ */
+ value?: unknown
+ /** Everything the program emitted, in order (capped by the implementation). */
+ logs: CodeLogEntry[]
+ /** Present iff the run failed; see {@link CodeRunFailure} for the taxonomy. */
+ error?: CodeRunFailure
+}
+```
+
+## Bindings: host functions as program globals
+
+Each `CodeBindingNamespace` becomes one global object of async callables inside the program (the Code Mode consumer passes one: `tools`). Arguments and resolutions must be structured-cloneable — a runtime may bridge calls across a serialization boundary — and a runtime treats binding names as hostile input (`__proto__` is an ordinary own property, never a prototype collision):
+
+```ts type-equiv
+interface CodeBindingNamespace {
+ /** The global identifier the program sees (must be a valid JS identifier). */
+ global: string
+ /** The callable members, keyed by the exact name the program calls. */
+ functions: Record
+}
+```
+
+```ts type-equiv
+type CodeBindingFunction = (args: unknown) => Promise
+```
+
+## Captured output and the failure taxonomy
+
+Logs arrive in emission order, attributed to their channel (the runtime's `console` shim, or stray writes to the underlying streams):
+
+```ts type-equiv
+interface CodeLogEntry {
+ /** Which channel produced the text. */
+ source: 'console' | 'stdout' | 'stderr'
+ /** The console method used; present only when `source` is `'console'`. */
+ level?: 'log' | 'info' | 'warn' | 'error' | 'debug'
+ /** The captured text (possibly truncated by the implementation's caps, marked in-band). */
+ text: string
+}
+```
+
+Failure kinds are **orthogonal outcomes reported independently** (per [defensive-patterns](../defensive-patterns.md)): a budget expiry is not an exception, an abort is not a timeout, and a substrate death (e.g. OOM) is neither:
+
+```ts type-equiv
+interface CodeRunFailure {
+ /** The failure class (see the interface doc for each kind's meaning). */
+ kind: 'exception' | 'timeout' | 'abort' | 'worker-exit'
+ /** Human-readable detail, suitable for feeding back to a model to self-correct. */
+ message: string
+}
+```
+
+## The service
+
+`CodeRuntime` (`ctx.codeRuntime`, abstract — defined in [`packages/code-runtime/code-runtime/src/index.ts`](../../packages/code-runtime/code-runtime/src/index.ts)) is `run(request)` plus two readonly descriptors: `language` (what the program must be written in — `'typescript'` is the well-known value; a consumer generating language-specific presentation switches on it and fails loud on one it cannot present) and `isolation` (the execution substrate — `'worker-thread'`, `'process'`, `'container'`; a diagnostic label, **not a security claim**). Implementations must keep runs isolated from each other (no cross-run state) and dispose to quiescence: in-flight runs are terminated and awaited before teardown completes.
diff --git a/docs/core-data-structures/core.md b/docs/core-data-structures/core.md
index 7d1110d7a8..615c222d94 100644
--- a/docs/core-data-structures/core.md
+++ b/docs/core-data-structures/core.md
@@ -20,6 +20,7 @@ Everything else is documented on a **sub-page**, not here. The rule that draws t
| [persistence.md](persistence.md) | the durability seam: `SessionPersistence`, JSONL + SQLite backends, `session/flush`, crash recovery, `SessionHeader` |
| [tools.md](tools.md) | `ToolDefinition` full fields, the schema DSL, `ToolExecution`/`ToolResult`, tool-presentation UI types, the `tools/pre-execute`/`tools/post-execute` pipeline |
| [bash.md](bash.md) | the bash executor seam: `BashExecRequest`/`Spec`, `BashRunResult`, background `BashTask`s |
+| [code-runtime.md](code-runtime.md) | the code-execution seam: `CodeRunRequest`/`Result`, binding namespaces, captured logs, the `CodeRunFailure` taxonomy |
| [filesystem.md](filesystem.md) | the filesystem seam: `FsTarget`, read/write/edit outcomes, observed-file state, `FsErrorCode` |
| [compaction.md](compaction.md) | the compaction seam: the `compact/*` session events, `CompactionResult`, the `CompactService` interface |
| [subagent.md](subagent.md) | the subagent seam: the named-provider registry, `SubagentStartRequest`/`Result`/`Run`, the start-time-vs-runtime capability split |
diff --git a/docs/module-graph.md b/docs/module-graph.md
index c08b1fd171..5e043d22d4 100644
--- a/docs/module-graph.md
+++ b/docs/module-graph.md
@@ -79,6 +79,9 @@ flowchart TD
pkg_app_boot["app-boot"]
pkg_stdio_agent["stdio-agent"]
end
+ subgraph group_code_runtime["packages/code-runtime"]
+ pkg_code_runtime["code-runtime"]
+ end
pkg_llm --> pkg_brand
pkg_bash --> pkg_brand
pkg_llm_deepseek --> pkg_llm
@@ -210,6 +213,7 @@ flowchart TD
| [`brand`](../packages/util/brand) | `util` | — |
| [`acp-snapshot`](../packages/support/acp-snapshot) | `support` | — |
| [`app-boot`](../packages/ui/app-boot) | `ui` | — |
+| [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | — |
| [`llm`](../packages/llm/llm) | `llm` | [`brand`](../packages/util/brand) |
| [`bash`](../packages/bash/bash) | `bash` | [`brand`](../packages/util/brand) |
| [`llm-deepseek`](../packages/llm/llm-deepseek) | `llm` | [`llm`](../packages/llm/llm) |
diff --git a/docs/rfc/proposed/feature/2026-06-15-code-mode.md b/docs/rfc/proposed/feature/2026-06-15-code-mode.md
index e9015ce740..b9a408241e 100644
--- a/docs/rfc/proposed/feature/2026-06-15-code-mode.md
+++ b/docs/rfc/proposed/feature/2026-06-15-code-mode.md
@@ -65,7 +65,7 @@ Per explicit-over-implicit at seams, the request spells out everything the runti
### The worker-thread runtime
-`packages/code-runtime/code-runtime-worker/` — `@deepseek-ai/dsh-code-runtime-worker`. Per `run()`:
+`@deepseek-ai/dsh-code-runtime-worker`, the second package of the `packages/code-runtime/` group. Per `run()`:
1. **Type-strip host-side** with Node's built-in `stripTypeScriptTypes` (`node:module`; present across the repo's whole engines range, `^22.19.0 || >=24.0.0`, and position-preserving, so runtime error line numbers match the model's source). Strip-only mode rejects non-erasable syntax (`enum`, namespaces) — that rejection returns as `error.kind: 'exception'` with Node's message, the SDK instructions say "erasable TypeScript only", and the model self-corrects like any other program error. A syntax-level failure never spawns a worker.
2. **Spawn one fresh `Worker` per run** from the package's own bootstrap module: `env: {}` (truly empty — stronger than the scrubbed-env rule for spawned commands), `resourceLimits` from config, `stdout`/`stderr` captured into `logs` rather than inherited. No pooling and no cross-run state: a program's world dies with its worker, which keeps runs reconstructable from the log alone and makes state bleed unrepresentable.
diff --git a/packages/README.md b/packages/README.md
index f07e0d5a36..da75f740e8 100644
--- a/packages/README.md
+++ b/packages/README.md
@@ -11,6 +11,7 @@ Packages are grouped by modular role at `packages///`. The group dir
| [`core/`](core/README.md) | Product API spine: session, system-prompt, tools, agent, and the concrete loop | Product — stable surface |
| [`llm/`](llm/README.md) | LLM capability family: the abstract service + provider adapters | Product — stable surface |
| [`bash/`](bash/README.md) | Bash capability family: the executor seam, a local impl, and the model-facing tool | Product — stable surface |
+| [`code-runtime/`](code-runtime/README.md) | Code-execution capability family: the abstract runtime seam for model-written programs | Product — stable surface |
| [`fs/`](fs/README.md) | Filesystem capability family: the abstract seam, a local impl, and the model-facing file tools | Product — stable surface |
| [`compact/`](compact/README.md) | Compaction capability family: the abstract seam + a basic backend (tool deferred) | Product — stable surface |
| [`subagent/`](subagent/README.md) | Subagent capability family: the provider-registry seam and the model-facing delegation tool | Product — stable surface |
diff --git a/packages/code-runtime/README.md b/packages/code-runtime/README.md
new file mode 100644
index 0000000000..578f3179c1
--- /dev/null
+++ b/packages/code-runtime/README.md
@@ -0,0 +1,9 @@
+# code-runtime/ — code-execution capability family
+
+The code-execution capability seam (see [capability seams](../../docs/rfc/implemented/architecture/2026-06-13-capability-seams.md)): an abstract runtime interface for executing one model-written program against host-provided async bindings, capturing what it printed and returned. The consumer is the tool registry's Code Mode, and the first implementation (a Node worker-thread backend) is specified alongside it in the [Code Mode RFC](../../docs/rfc/proposed/feature/2026-06-15-code-mode.md). **Product** packages.
+
+| Package | Role | ctx key |
+|---|---|---|
+| `code-runtime/` | Abstract code-execution seam (interface + vocabulary) | `ctx.codeRuntime` |
+
+The interface lives at `code-runtime/code-runtime/`. Backends differ by execution substrate (worker thread, process, container) and by source language — both readonly descriptors on the service — and register `ctx.codeRuntime` without touching the interface or its consumer; that split is what makes a hardened backend a drop-in later.
diff --git a/packages/code-runtime/code-runtime/README.md b/packages/code-runtime/code-runtime/README.md
new file mode 100644
index 0000000000..2d7b12add1
--- /dev/null
+++ b/packages/code-runtime/code-runtime/README.md
@@ -0,0 +1,19 @@
+# @deepseek-ai/dsh-code-runtime
+
+The **code-execution seam**: an abstract `CodeRuntime` service (`ctx.codeRuntime`) defining WHAT a code runtime does — run one model-written program against a set of host-provided async bindings and report `{ value, logs, error? }` — without saying HOW.
+
+This package is the interface third of the capability (the bash trio is the template — see [capability seams](../../../docs/rfc/implemented/architecture/2026-06-13-capability-seams.md)): implementations subclass `CodeRuntime` and register the service; the consumer is the tool registry's Code Mode, which generates the model-facing SDK and bridges tool dispatch — both specified in the [Code Mode RFC](../../../docs/rfc/proposed/feature/2026-06-15-code-mode.md), whose first implementation is a Node worker-thread backend. The runtime knows nothing about tools or sessions: it is handed named async functions and a program string, and everything tool-shaped stays with the consumer.
+
+## Service API (`ctx.codeRuntime`)
+
+| Member | Semantics |
+|---|---|
+| `run(request)` | Execute one program against the request's bindings. **Resolves with an error FIELD for every program outcome** — parse/transform failure, thrown exception, budget expiry, abort, substrate death (`CodeRunFailure`'s orthogonal `kind` taxonomy); it rejects only for caller misuse of the seam itself (e.g. a run submitted after disposal). The program runs as the body of an async function: top-level `await`/`return` work, and the completion value becomes `result.value` when it survives the serialization boundary. |
+| `language` | Readonly descriptor: the source language `run` expects (`'typescript'` is the well-known value). Informational, not gating — a consumer that generates language-specific presentation switches on it and fails loud on a language it cannot present. |
+| `isolation` | Readonly descriptor: the execution substrate (`'worker-thread'`, `'process'`, `'container'`). A label for deployments and diagnostics, **not a security claim**. |
+
+Semantics every implementation must honor (contract details in the class JSDoc): binding calls bridge to the caller's functions verbatim with structured-cloneable arguments/resolutions; the program is treated as a hostile peer (arbitrary binding names are own properties, malformed traffic never crashes the host); no state survives between runs; disposal terminates in-flight runs AND awaits their exit before completing.
+
+## Vocabulary
+
+`CodeRunRequest` (`program`, `bindings`, `signal?`) carries everything the runtime acts on — defaulting (time budgets, output caps) is the implementation's validated config, never a hidden `??` inside `run()`. `bindings` is a list of `CodeBindingNamespace`s (`global` + `functions`), each exposed to the program as one global object of async callables. `CodeRunResult` reports the completion `value?`, the ordered `logs` (`CodeLogEntry`: `console`/`stdout`/`stderr` source, console `level`, capped text), and the `error?` (`CodeRunFailure`: `kind` + model-feedable `message`). See `src/types.ts` for the full contracts.
diff --git a/packages/code-runtime/code-runtime/package.json b/packages/code-runtime/code-runtime/package.json
new file mode 100644
index 0000000000..0fe24bb15c
--- /dev/null
+++ b/packages/code-runtime/code-runtime/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "@deepseek-ai/dsh-code-runtime",
+ "description": "Abstract code-execution seam (ctx.codeRuntime) for the DeepSeek Harness",
+ "version": "0.0.1",
+ "private": true,
+ "type": "module",
+ "main": "lib/index.js",
+ "types": "lib/types/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./lib/types/index.d.ts",
+ "default": "./lib/index.js"
+ },
+ "./src/*": "./src/*",
+ "./package.json": "./package.json"
+ },
+ "files": [
+ "lib/index.js",
+ "lib/types/**/*.d.ts",
+ "lib/types/**/*.d.ts.map",
+ "src"
+ ],
+ "license": "BSD-3-Clause",
+ "peerDependencies": {
+ "cordis": "^4.0.0-rc.6"
+ },
+ "devDependencies": {
+ "cordis": "^4.0.0-rc.6"
+ }
+}
diff --git a/packages/code-runtime/code-runtime/src/index.ts b/packages/code-runtime/code-runtime/src/index.ts
new file mode 100644
index 0000000000..af967da61d
--- /dev/null
+++ b/packages/code-runtime/code-runtime/src/index.ts
@@ -0,0 +1,93 @@
+/**
+ * The code-execution seam (`ctx.codeRuntime`): an abstract service defining
+ * WHAT a code runtime does — run one model-written program against a set of
+ * host-provided async bindings and report `{ value, logs, error? }` — without
+ * saying HOW. Implementations subclass {@link CodeRuntime} and register
+ * themselves as the `codeRuntime` service; backends may differ by execution
+ * substrate (worker thread, separate process, container) and by source
+ * language, both declared as readonly descriptors. The design and its
+ * consumer (the tool registry's Code Mode) are specified in the Code Mode RFC
+ * (docs/rfc/proposed/feature/2026-06-15-code-mode.md).
+ *
+ * The split mirrors the bash seam (`BashExecutor`): the runtime knows nothing
+ * about tools or sessions — it is handed named async functions and a program,
+ * and everything tool-shaped stays with the consumer.
+ *
+ * @module @deepseek-ai/dsh-code-runtime
+ */
+
+import { Context, Service } from 'cordis'
+import type { CodeRunRequest, CodeRunResult } from './types.ts'
+
+export type {
+ CodeBindingFunction,
+ CodeBindingNamespace,
+ CodeLogEntry,
+ CodeRunFailure,
+ CodeRunRequest,
+ CodeRunResult,
+} from './types.ts'
+
+declare module 'cordis' {
+ interface Context {
+ codeRuntime: CodeRuntime
+ }
+}
+
+/**
+ * Abstract code-execution service. Subclass, implement {@link run} and the
+ * two descriptors, and load the subclass as a plugin — it registers as
+ * `ctx.codeRuntime` (one implementation per context; loading a second throws,
+ * cordis' standard duplicate-service behavior).
+ *
+ * Semantics every implementation must honor:
+ * - {@link run} resolves with an error FIELD for every program outcome —
+ * parse/transform failures, thrown exceptions, budget expiry, abort,
+ * substrate death ({@link CodeRunFailure}'s taxonomy). It REJECTS only for
+ * caller misuse of the seam itself (e.g. a run submitted after disposal).
+ * - Binding calls bridge to the caller's {@link CodeBindingFunction}s
+ * verbatim; arguments and resolutions must be structured-cloneable, and the
+ * runtime treats the program as a hostile peer (arbitrary binding names are
+ * own properties, malformed traffic is rejected or ignored, never crashes
+ * the host).
+ * - Runs are isolated from each other: no state survives from one run to the
+ * next through the runtime.
+ * - Disposal reaches quiescence: in-flight runs are terminated AND awaited
+ * before the service's own teardown completes (no orphan substrate survives
+ * `fiber.dispose()`).
+ */
+export abstract class CodeRuntime extends Service {
+ /**
+ * The source language {@link run} expects `program` to be written in, as a
+ * lowercase identifier. Informational, not gating — a consumer that
+ * generates language-specific presentation (typed SDK stubs, usage
+ * instructions) switches on it and fails loud on a language it cannot
+ * present. Well-known value: `'typescript'`.
+ */
+ abstract readonly language: string
+
+ /**
+ * The execution substrate, as a lowercase identifier. Informational, not
+ * gating — a descriptor so deployments and diagnostics can tell backends
+ * apart, not a security claim. Well-known values: `'worker-thread'`,
+ * `'process'`, `'container'`.
+ */
+ abstract readonly isolation: string
+
+ constructor(ctx: Context) {
+ super(ctx, 'codeRuntime')
+ }
+
+ /**
+ * Execute one program against the request's bindings and capture what it
+ * emitted. See the class doc for the resolution contract (error is a result
+ * field; rejection means seam misuse only).
+ * @param request - the program, its bindings, and the abort signal; the
+ * request carries everything the runtime acts on, with no hidden defaults.
+ * @returns the run's outcome: completion value (when transferable), the
+ * ordered log capture, and the failure (if any).
+ */
+ abstract run(request: CodeRunRequest): Promise
+}
+
+export default CodeRuntime
diff --git a/packages/code-runtime/code-runtime/src/types.ts b/packages/code-runtime/code-runtime/src/types.ts
new file mode 100644
index 0000000000..8278f33a39
--- /dev/null
+++ b/packages/code-runtime/code-runtime/src/types.ts
@@ -0,0 +1,105 @@
+/**
+ * Vocabulary types for the code-execution seam: what a caller hands a
+ * {@link ../index.ts | CodeRuntime} and what it gets back. Pure types — no
+ * runtime code lives here.
+ *
+ * @module @deepseek-ai/dsh-code-runtime/src/types
+ */
+
+/**
+ * One host-side function exposed to the program as an async callable. The
+ * runtime bridges calls to it (possibly across a serialization boundary), so
+ * `args` and the resolution value MUST be structured-cloneable; a runtime
+ * rejects a non-cloneable value with a descriptive error rather than
+ * corrupting the run. A rejection of this function surfaces inside the
+ * program as a rejection of the corresponding call.
+ */
+export type CodeBindingFunction = (args: unknown) => Promise
+
+/**
+ * A named group of {@link CodeBindingFunction}s the runtime exposes to the
+ * program as one global object (e.g. `tools`). Function names are arbitrary
+ * strings — a runtime must treat names like `__proto__` or `constructor` as
+ * ordinary own properties (null-prototype construction), never as prototype
+ * collisions.
+ */
+export interface CodeBindingNamespace {
+ /** The global identifier the program sees (must be a valid JS identifier). */
+ global: string
+ /** The callable members, keyed by the exact name the program calls. */
+ functions: Record
+}
+
+/**
+ * One run: the program source plus everything the runtime acts on. Per the
+ * explicit-over-implicit convention, defaulting (time budgets, output caps)
+ * is the implementation's validated config — a request carries no optional
+ * tuning knobs for a hidden `??` to fill in.
+ */
+export interface CodeRunRequest {
+ /**
+ * The program source, in the runtime's {@link ../index.ts | language}. It
+ * runs as the body of an async function: top-level `await` and `return`
+ * are available, and the completion value becomes
+ * {@link CodeRunResult.value}.
+ */
+ program: string
+ /** Host functions exposed to the program, one global object per namespace. */
+ bindings: CodeBindingNamespace[]
+ /**
+ * Abort the run: the runtime stops the program (hard, even mid-loop) and
+ * resolves with a {@link CodeRunFailure} of kind `'abort'`. In-flight
+ * binding calls are the CALLER's to settle — the runtime only stops asking.
+ */
+ signal?: AbortSignal
+}
+
+/**
+ * One captured output entry, in emission order. `source` says which channel
+ * produced it: the program's `console` (shimmed by the runtime), or a stray
+ * write to the underlying stdout/stderr streams.
+ */
+export interface CodeLogEntry {
+ /** Which channel produced the text. */
+ source: 'console' | 'stdout' | 'stderr'
+ /** The console method used; present only when `source` is `'console'`. */
+ level?: 'log' | 'info' | 'warn' | 'error' | 'debug'
+ /** The captured text (possibly truncated by the implementation's caps, marked in-band). */
+ text: string
+}
+
+/**
+ * Why a run failed. The kinds are orthogonal outcomes reported independently
+ * (per docs/defensive-patterns.md): a budget expiry is not an exception, an
+ * abort is not a timeout, and a substrate death is neither.
+ *
+ * - `'exception'` — the program threw or failed to parse/transform.
+ * - `'timeout'` — an implementation-owned budget expired; the message says which.
+ * - `'abort'` — {@link CodeRunRequest.signal} fired.
+ * - `'worker-exit'` — the execution substrate died without settling (e.g. OOM).
+ */
+export interface CodeRunFailure {
+ /** The failure class (see the interface doc for each kind's meaning). */
+ kind: 'exception' | 'timeout' | 'abort' | 'worker-exit'
+ /** Human-readable detail, suitable for feeding back to a model to self-correct. */
+ message: string
+}
+
+/**
+ * The outcome of one run. An error is a FIELD on a resolved result, never a
+ * rejection of `run()` — reporting a failed program is the caller's job, not
+ * an exception path.
+ */
+export interface CodeRunResult {
+ /**
+ * The program's completion value (its top-level `return`), when it ran to
+ * completion and the value survived the runtime's serialization boundary;
+ * a non-transferable value is replaced by a string rendering, and a failed
+ * or value-less run leaves this absent.
+ */
+ value?: unknown
+ /** Everything the program emitted, in order (capped by the implementation). */
+ logs: CodeLogEntry[]
+ /** Present iff the run failed; see {@link CodeRunFailure} for the taxonomy. */
+ error?: CodeRunFailure
+}
diff --git a/packages/code-runtime/code-runtime/tests/service.spec.ts b/packages/code-runtime/code-runtime/tests/service.spec.ts
new file mode 100644
index 0000000000..4ff6d8f313
--- /dev/null
+++ b/packages/code-runtime/code-runtime/tests/service.spec.ts
@@ -0,0 +1,87 @@
+import { describe, expect, it } from 'vitest'
+import { Context } from 'cordis'
+import { CodeRuntime } from '@deepseek-ai/dsh-code-runtime'
+import type { CodeRunRequest, CodeRunResult } from '@deepseek-ai/dsh-code-runtime'
+
+/**
+ * Minimal concrete runtime: records requests, "executes" by invoking every
+ * binding once in declaration order, and lets tests script the outcome. The
+ * seam package ships no implementation, so the contract is exercised through
+ * the smallest subclass that honors it.
+ */
+class StubRuntime extends CodeRuntime {
+ readonly language = 'typescript'
+ readonly isolation = 'in-process-stub'
+ requests: CodeRunRequest[] = []
+ nextResult: CodeRunResult = { logs: [] }
+
+ async run(request: CodeRunRequest): Promise {
+ this.requests.push(request)
+ if (request.signal?.aborted) {
+ return { logs: [], error: { kind: 'abort', message: String(request.signal.reason) } }
+ }
+ for (const namespace of request.bindings) {
+ for (const fn of Object.values(namespace.functions)) {
+ await fn({ from: 'stub' })
+ }
+ }
+ return this.nextResult
+ }
+}
+
+async function setup() {
+ const ctx = new Context()
+ await ctx.plugin(StubRuntime)
+ const runtime = ctx.codeRuntime as StubRuntime
+ return { ctx, runtime }
+}
+
+describe('CodeRuntime service seam', () => {
+ it('registers as ctx.codeRuntime and serves the abstract API', async () => {
+ const { runtime } = await setup()
+ expect(runtime.language).toBe('typescript')
+ expect(runtime.isolation).toBe('in-process-stub')
+
+ const calls: unknown[] = []
+ const result = await runtime.run({
+ program: 'return 1',
+ bindings: [{ global: 'tools', functions: { probe: async args => void calls.push(args) } }],
+ })
+ expect(result).toEqual({ logs: [] })
+ expect(calls).toEqual([{ from: 'stub' }])
+ expect(runtime.requests).toHaveLength(1)
+ })
+
+ it('reports a failed run as an error field on a resolved result, never a rejection', async () => {
+ const { runtime } = await setup()
+ runtime.nextResult = {
+ logs: [{ source: 'console', level: 'error', text: 'boom' }],
+ error: { kind: 'exception', message: 'boom' },
+ }
+ const result = await runtime.run({ program: 'throw new Error("boom")', bindings: [] })
+ expect(result.error).toEqual({ kind: 'exception', message: 'boom' })
+ expect(result.value).toBeUndefined()
+ })
+
+ it('reports a pre-aborted signal as an abort failure', async () => {
+ const { runtime } = await setup()
+ const controller = new AbortController()
+ controller.abort('cancelled')
+ const result = await runtime.run({ program: 'return 1', bindings: [], signal: controller.signal })
+ expect(result.error).toEqual({ kind: 'abort', message: 'cancelled' })
+ })
+
+ it('is removed from the context when the providing fiber disposes (HMR safety)', async () => {
+ const ctx = new Context()
+ const fiber = await ctx.plugin(StubRuntime)
+ expect(ctx.get('codeRuntime')).toBeInstanceOf(StubRuntime)
+
+ await fiber.dispose()
+ expect(ctx.get('codeRuntime')).toBeUndefined()
+ })
+
+ it('rejects a second implementation in the same context (duplicate service)', async () => {
+ const { ctx } = await setup()
+ await expect(ctx.plugin(StubRuntime)).rejects.toThrow(/registered/)
+ })
+})
diff --git a/packages/code-runtime/code-runtime/tsconfig.json b/packages/code-runtime/code-runtime/tsconfig.json
new file mode 100644
index 0000000000..754725418e
--- /dev/null
+++ b/packages/code-runtime/code-runtime/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "extends": "../../../tsconfig.base.json",
+ "compilerOptions": {
+ "rootDir": "src",
+ "outDir": "lib/types"
+ },
+ "include": [
+ "src"
+ ],
+ "references": [
+ {
+ "path": "../../../vendor/cosmokit"
+ },
+ {
+ "path": "../../../vendor/cordis"
+ }
+ ]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9856e99e95..32ffa0d389 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -127,6 +127,12 @@ importers:
specifier: ^4.0.0-rc.6
version: 4.0.0-rc.6(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.4)
+ packages/code-runtime/code-runtime:
+ devDependencies:
+ cordis:
+ specifier: ^4.0.0-rc.6
+ version: 4.0.0-rc.6(@cordisjs/plugin-include@1.0.4)(@cordisjs/plugin-loader@1.0.0-rc.4)
+
packages/compact/compact:
devDependencies:
'@deepseek-ai/dsh-llm':
diff --git a/scripts/doc-budgets.manifest.json b/scripts/doc-budgets.manifest.json
index b23811e3d0..fc2b9d12c2 100644
--- a/scripts/doc-budgets.manifest.json
+++ b/scripts/doc-budgets.manifest.json
@@ -1,7 +1,7 @@
{
"AGENTS.md": 1691,
"docs/AGENTS.md": 1315,
- "docs/architecture.md": 1630,
+ "docs/architecture.md": 1640,
"docs/cordis-primer.md": 550,
"docs/defensive-patterns.md": 550,
"docs/testing.md": 800,
diff --git a/scripts/gen-cordis-catalog.ts b/scripts/gen-cordis-catalog.ts
index 451c4bec9e..6220006ede 100644
--- a/scripts/gen-cordis-catalog.ts
+++ b/scripts/gen-cordis-catalog.ts
@@ -97,6 +97,8 @@ export const LINK_MAP: Record = {
BashRunResult: 'bash.md',
BashTask: 'bash.md',
BashTaskRead: 'bash.md',
+ CodeRunRequest: 'code-runtime.md',
+ CodeRunResult: 'code-runtime.md',
FsEditOutcome: 'filesystem.md',
FsEditRequest: 'filesystem.md',
FsInfo: 'filesystem.md',
diff --git a/scripts/gen-doc-graphs.ts b/scripts/gen-doc-graphs.ts
index 66cb102b00..6440a672be 100644
--- a/scripts/gen-doc-graphs.ts
+++ b/scripts/gen-doc-graphs.ts
@@ -149,6 +149,15 @@ const SERVICE_ROLES: ServiceRole[] = [
consumers: ['tool-bash', 'hooks-claude', 'hooks-codex'],
note: 'The model-facing bash tools and hook bridges consume this seam; sandboxed or remote executors can replace bash-local.',
},
+ {
+ key: 'codeRuntime',
+ pkg: 'code-runtime',
+ title: 'Code-execution seam',
+ mode: 'seam',
+ implementations: [],
+ consumers: [],
+ note: 'Runs one model-written program against host-provided async bindings; backends differ by substrate and language (the Code Mode RFC specifies the worker-thread backend and the tool-registry consumer).',
+ },
{
key: 'fs',
pkg: 'fs',
diff --git a/scripts/type-equiv.manifest.json b/scripts/type-equiv.manifest.json
index b66882d8c9..613280ee1b 100644
--- a/scripts/type-equiv.manifest.json
+++ b/scripts/type-equiv.manifest.json
@@ -55,6 +55,13 @@
{ "doc": "docs/core-data-structures/bash.md", "symbol": "BashTask", "source": "packages/bash/bash/src/types.ts" },
{ "doc": "docs/core-data-structures/bash.md", "symbol": "BashTaskRead", "source": "packages/bash/bash/src/types.ts" },
+ { "doc": "docs/core-data-structures/code-runtime.md", "symbol": "CodeRunRequest", "source": "packages/code-runtime/code-runtime/src/types.ts" },
+ { "doc": "docs/core-data-structures/code-runtime.md", "symbol": "CodeRunResult", "source": "packages/code-runtime/code-runtime/src/types.ts" },
+ { "doc": "docs/core-data-structures/code-runtime.md", "symbol": "CodeBindingNamespace", "source": "packages/code-runtime/code-runtime/src/types.ts" },
+ { "doc": "docs/core-data-structures/code-runtime.md", "symbol": "CodeBindingFunction", "source": "packages/code-runtime/code-runtime/src/types.ts" },
+ { "doc": "docs/core-data-structures/code-runtime.md", "symbol": "CodeLogEntry", "source": "packages/code-runtime/code-runtime/src/types.ts" },
+ { "doc": "docs/core-data-structures/code-runtime.md", "symbol": "CodeRunFailure", "source": "packages/code-runtime/code-runtime/src/types.ts" },
+
{ "doc": "docs/core-data-structures/filesystem.md", "symbol": "FsTarget", "source": "packages/fs/fs/src/types.ts" },
{ "doc": "docs/core-data-structures/filesystem.md", "symbol": "FsTargetKey", "source": "packages/fs/fs/src/types.ts" },
{ "doc": "docs/core-data-structures/filesystem.md", "symbol": "FsVersion", "source": "packages/fs/fs/src/types.ts" },
diff --git a/tsconfig.base.json b/tsconfig.base.json
index 40e4dbe728..ea1d804ba5 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -43,6 +43,7 @@
"./packages/core/*/src",
"./packages/llm/*/src",
"./packages/bash/*/src",
+ "./packages/code-runtime/*/src",
"./packages/fs/*/src",
"./packages/compact/*/src",
"./packages/subagent/*/src",
diff --git a/tsconfig.build.json b/tsconfig.build.json
index 96d87c01e9..3d99ad4e28 100644
--- a/tsconfig.build.json
+++ b/tsconfig.build.json
@@ -22,6 +22,7 @@
{ "path": "./packages/core/agent-loop" },
{ "path": "./packages/core/agent-core" },
{ "path": "./packages/bash/bash" },
+ { "path": "./packages/code-runtime/code-runtime" },
{ "path": "./packages/compact/compact" },
{ "path": "./packages/compact/compact-basic" },
{ "path": "./packages/llm/llm-deepseek" },
diff --git a/tsconfig.json b/tsconfig.json
index ff737baf04..2091283c93 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -33,6 +33,7 @@
{ "path": "./packages/core/agent-loop" },
{ "path": "./packages/core/agent-core" },
{ "path": "./packages/bash/bash" },
+ { "path": "./packages/code-runtime/code-runtime" },
{ "path": "./packages/llm/llm-deepseek" },
{ "path": "./packages/llm/llm-pi-ai" },
{ "path": "./packages/bash/bash-local" },