description, kind
| description | kind |
|---|---|
| Abstract code-execution seam (`ctx.codeRuntime`) for users and maintainers composing, consuming, or building a backend that runs one model-written program against host-provided bindings. | package-reference |
@deepseek-ai/dsh-code-runtime
English | 中文
Summary
dsh-code-runtime defines what a code runtime does: run one model-written program against a set of host-provided async functions and report { value, logs, error? } — without dictating how any backend implements it. Load it in a composition with a backend and the service is available as ctx.codeRuntime; Code Mode in dsh-tools then runs model-written programs that compose tools. Every request runs once with no state carried between runs, and every program outcome — including failures — resolves as a result field rather than a rejection. The runtime knows nothing about tools or sessions: it is handed a program and named bindings, and everything tool-shaped stays with the consumer.
Table of Contents
- Use this package
- Understand the implementation
- Further Exploration
- Model Experience
- Known Limitations and Deferred Work
- Dev Note
Use this package
Choose this package when you compose a deployment that executes model-written programs, consume ctx.codeRuntime directly, or build a backend that runs programs. In the shipped composition, Code Mode in dsh-tools is the consumer: only what the program printed and returned re-enters the conversation.
Run a program
Give the runtime a program source and one or more binding namespaces. Each namespace becomes one global object of async functions inside the program — Code Mode passes one under tools. The program runs as the body of an async function, so top-level await and return work; a lossless-JSON completion becomes result.value, emitted text arrives in order as result.logs, and any failure is reported in result.error with a kind you can branch on. The runtime never rejects for a program failure — rejection means you misused the seam, for example by submitting a run after disposal.
const result = await ctx.codeRuntime.run({
program: 'return await tools.add({ a: 1, b: 2 })',
bindings: [{ global: 'tools', functions: { add: async (args) => args.a + args.b } }],
})
// result.value === 3
Choose a backend
Backends declare two descriptors you can rely on: language — what the program must be written in, with 'typescript' and 'python' as the well-known values and only TypeScript shipped — and isolation — the execution substrate ('worker-thread', 'process', 'container'), a label for deployments and diagnostics, not a security claim. The shipped backend is dsh-code-runtime-worker-thread, which executes TypeScript in a fresh Node worker thread; dsh-code-runtime-python owns the wire protocol for the CPython backend.
Name your bindings portably
Binding-global and error-class names are language-portable: they must match [A-Za-z_][A-Za-z0-9_]*, avoid every portable target language's reserved words, and avoid backend-owned slots, so one namespace list is valid against every backend. A name like $tools, lambda, or console fails the run before it starts; the exact exclusion sets are part of the seam contract.
What can go wrong
Failures arrive as result.error with an orthogonal kind: the program threw or failed to parse (exception), a budget expired (timeout), the run was aborted (abort), the execution substrate died (worker-exit), the completion value was not lossless JSON (invalid-output), or the serialized output exceeded the cap (output-limit). Each kind carries a model-feedable message. run() rejects only for seam misuse, such as a run submitted after disposal or a binding name that fails the portable-identifier rules.
Understand the implementation
Implementation internals — click to expand
This section explains the design behind the seam; observable behavior is fully covered in Use this package.
Design concept
The package is the Service Definition role of the code-execution capability seam (capability seams): an abstract CodeRuntime extends Service registered as ctx.codeRuntime, plus the vocabulary both backends and the consumer share. Providers subclass CodeRuntime, implement run, and register the service; the consumer (Code Mode in dsh-tools) generates the model-facing SDK and bridges tool dispatch. The runtime stays ignorant of tools and sessions by contract: it receives a program and named async bindings and returns { value, logs, error? }.
Service API
The contract is three members a backend implements: run(request) executes one program against the request's bindings and resolves every program outcome — parse/transform failure, thrown exception, invalid completion, output overflow, budget expiry, abort, or substrate death — as a result error field, with rejection reserved for caller misuse such as a run submitted after disposal; language and isolation are read-only descriptors labeling the source language and execution substrate for deployments and diagnostics.
The exhaustive semantics live in the code runtime subsystem reference; the exact signatures are in src/index.ts.
Vocabulary
CodeRunRequest (program, bindings, signal?) carries everything the runtime acts on; defaulting (time budgets, output caps) is each provider's validated config, never a hidden ?? inside run(). bindings is a list of CodeBindingNamespaces (global + functions + optional errorClass), each exposed to the program as one global object of async callables returning CodeJsonValue — the seam's structural lossless-JSON type. An errorClass descriptor names a real program-global constructor and the own property that receives the rejected member name, so backends never learn consumer terms such as ToolCallError. CodeRunResult reports the lossless-JSON completion value?, ordered logs: string[], and error? (CodeRunFailure: orthogonal kind + model-feedable message). See src/types.ts for the full contracts.
Portable identifiers
Binding-global and error-class names are language-portable: they must match the identifier subset [A-Za-z_][A-Za-z0-9_]* (no JS-only $) and clear the seam-exported exclusion sets, so one bindings list is valid against every backend. The package exports the contract every backend enforces — PORTABLE_RESERVED_WORDS (ECMAScript ∪ Python reserved words), RESERVED_BINDING_GLOBALS (backend-owned globals such as console and __dsh_main__), RESERVED_ERROR_MEMBERS and DUNDER_MEMBER (error-member exclusions) — so a name like $tools, lambda, or __dsh_main__ makes run() reject as seam misuse on any backend. See src/index.ts for the exact sets.
Source map
| File | Role |
|---|---|
src/index.ts |
Plugin entry: abstract CodeRuntime service and the portable-identifier exclusion sets |
src/types.ts |
Vocabulary: CodeRunRequest, CodeBindingNamespace, CodeJsonValue, CodeRunResult, CodeRunFailure |
src/invariant.ts |
Invariant companion (no runtime invariant; the seam registers no mutable data relation) |
Further Exploration
Read these when the package-level contract is not enough. They move from the Code Mode consumer to the shipped backends and the capability-seam model.
- Code Mode Agent Note — how the tool registry consumes
ctx.codeRuntimeand presentsrun_codeto the model. - Worker-thread backend — the shipped TypeScript execution backend.
- Python protocol package — the wire protocol for the CPython backend.
- Code runtime subsystem reference — request/result vocabulary, bindings, and the
ctx.codeRuntimecordis surface. - Capability seams — the Service Definition / Service Provider / Consumer split.
Model Experience
Indirectly, through Code Mode in dsh-tools, which exposes run_code and returns program logs, values, or failures as retained tool-result tokens.
KV Cache effect
No direct invalidation; the named consumer owns any request-prefix changes.
Known Limitations and Deferred Work
These limits define what the seam cannot do; they are current package constraints, not a task backlog.
run()is one-shot —logsarrive only on the resolvedCodeRunResult; the seam exposes no streaming-log or progress API for a live program's output.- No state survives between runs — every request runs against a fresh world; a persistent REPL-style kernel is deferred until a backend brings its own logging story.
- Only the worker-thread backend ships —
'process'and'container'are declared well-knownisolationvalues with no implementation; a hard security boundary awaits a container backend. - Intermediate binding values have no byte cap — implementations remain subject to structured-clone cost and process memory, while a provider may already impose its own acquisition bound.
Dev Note
Working context for maintainers — click to expand
This Dev Note is working context for maintainers: undecided directions and open questions. It is explicitly non-authoritative — shipped behavior and limits live in the sections above and the package code.
Future: persistent kernel backend
A REPL-style kernel that keeps state across run_code calls remains undecided; it would need its own logging story, because the no-state-between-runs contract is what keeps every request reconstructable from the session log alone.
Future: container backend
A container-class backend would provide a hard multi-tenant boundary for both code and shell execution; nothing is decided beyond the well-known isolation value.