Files
deepseek-harness/packages/cordis/tool-cordis
imccyu 1b1ba96d4f fix(tool-cordis): replace the pass-through ctx proxy with a whitelist façade
Review finding (#220): the guarded proxy only special-cased ctx.tools, so
mount code could reach an UNGUARDED context through ctx.root, ctx.extend(), or
a service instance's .ctx, then ctx.root.tools.register({…}) to bypass the
marker check and host-realm normalization — a raw vm-realm result would later
error a real agent turn at the session-log plainness check.

The sandbox ctx is now a whitelist façade, not a pass-through proxy: it exposes
only what a mount needs — tools.register (marker-guarded), on/once, provide, the
timer helpers, and injected services resolved through a guarded get — and denies
every framework-plumbing member (root, parent, fiber, reflect, registry, extend,
isolate, intercept, plugin, set, mixin, …) with a teaching error. Injected
services are wrapped so a method returning a Context is rejected on the way back
(the .ctx escape), closing the one indirect leak. There is no context-valued
member left to reach; cross-mount provide/inject is untouched (the plugin's own
inject and the fiber's pending/active gating are unchanged). ctx.plugin (child
plugins) and ctx.set are denied by design; ctx.effect is deferred (FIXME).

Adds tests/sandbox-context.spec.ts covering the escape class (root/extend/fiber/
plugin/set/… denied, the classic root.tools.register bypass, the .ctx escape,
read-only writes) plus the async-service and symbol/in-operator paths for 100%
coverage. RFC/README/tool-catalog/config-catalog updated; api-catalog.ts
regenerated (also picks up the codeRuntime service that entered on the master
merge and was left stale).
2026-07-09 13:57:03 +08:00
..

@deepseek-ai/dsh-tool-cordis

The self-referential cordis toolset: three model-facing tools over the live runtime the agent runs inside. Design home — sandbox semantics, mount lifecycle, cross-mount composition, the generated API catalog, standing decisions: the toolset RFC.

What it does

  • cordis_inspect — read-only report over the runtime: services, the loaded-plugin list, registered tools, the dynamic-mount table, and the catalog-backed api / events references.
  • cordis_mount — evaluates model-written JavaScript (the body of an async function) in a node:vm sandbox; the code must return a cordis plugin, which is mounted under the cordis-dynamic group fiber and tracked as dyn-<n>.
  • cordis_unmount — disposes one mount by id, returning only after quiescence.

Exact model-facing schemas: the generated tool catalog.

Trust stance

The sandbox isolates the global context only — it is not a security boundary. No Node API is provided: require, the timers, and fetch are callable traps that throw a redirect to the cordis alternative (ctx.fs / ctx.web / ctx.bash / inject: ['timer'] + ctx.setTimeout); process and Buffer are undefined; globalThis writes stay inside. The ctx a mounted plugin's apply receives is a whitelist façade — register tools, observe events, provide/consume services, use timers; framework internals (ctx.root, ctx.fiber, ctx.extend, ctx.plugin, …) are withheld — but the capabilities it does expose reach the real runtime, so load this plugin as deliberately as you would grant a bash tool.

Config

Field Default Meaning
vmTimeoutMs 5000 Bound on the SYNCHRONOUS portion of mount-code evaluation; an async body escapes it

The generated API catalog

src/api-catalog.ts is generated by scripts/gen-cordis-api.ts from the same AST walk as docs/cordis-catalog and freshness-gated by pnpm run verify-cordis-api (in doc-sync) — never edit it by hand. cordis_inspect intersects it with the live service store at call time.

Rendering

All three tools render generic cards (read / execute / delete); cordis_mount carries the mount code as rawInput. Presenters are pure functions of the args; results keep the default text rendering.

Export shape

Namespace plugin: named exports name / inject / Config / apply, no default export (docs/postmortem/0001).