deepseek-harness/packages/code-runtime/code-runtime-python
Chinesezjc be839a8e53 fix(code-runtime-python): count non-lossless bytes and bind the mirror gate to TS types
Two gaps from the previous round's fixes:

- checkDoneValue flagged a non-lossless number but skipped counting its encoded
  bytes, so a value over budget ONLY through that number classified as
  non-lossless instead of over-budget (e.g. [Infinity] at cap 3, whose encoding
  is 10 bytes). Count the scalar's bytes even when flagging, so the budget check
  wins as the JSDoc promises. Add cap-3 regression cases.

- The mirror e2e compared the Python TypedDict keys against a hand-written
  constant, so a field change on the TS side alone would not fail it, and the
  reply frames were not probed at all. Introduce WIRE_FRAME_FIELDS in
  protocol.ts, bound to each frame interface's key set via `satisfies` (a
  renamed/removed field breaks typecheck — verified), and drive the mirror test
  from it, now covering ReplyOk/ReplyErr too. The test therefore fails on
  one-sided drift from either language.
2026-08-07 13:27:54 +08:00
..
py fix(code-runtime-python): close coverage gap and tighten the wire mirror 2026-08-07 13:27:54 +08:00
src fix(code-runtime-python): count non-lossless bytes and bind the mirror gate to TS types 2026-08-07 13:27:54 +08:00
tests fix(code-runtime-python): count non-lossless bytes and bind the mirror gate to TS types 2026-08-07 13:27:54 +08:00
package.json fix(code-runtime-python): satisfy static gates for the protocol-only layer 2026-08-07 13:27:54 +08:00
README.i18n.yaml feat(code-runtime-python): make the TypedDict wire mirror an executable gate 2026-08-07 13:27:54 +08:00
README.md feat(code-runtime-python): make the TypedDict wire mirror an executable gate 2026-08-07 13:27:54 +08:00
README.zh.md feat(code-runtime-python): make the TypedDict wire mirror an executable gate 2026-08-07 13:27:54 +08:00
tsconfig.json feat(code-runtime-python): add the fd-3 frame protocol 2026-08-07 13:27:54 +08:00
tsdown.config.ts feat(code-runtime-python): add the fd-3 frame protocol 2026-08-07 13:27:54 +08:00

@deepseek-ai/dsh-code-runtime-python

English | 中文

CPython-subprocess implementation of the @deepseek-ai/dsh-code-runtime seam. Companion to @deepseek-ai/dsh-code-runtime-worker; trades the Node worker thread for a fresh python3 subprocess so model code is Python instead of TypeScript.

This package is built up across the code-runtime-python PR stack. This layer ships the wire protocol; the PythonCodeRuntime implementation that drives a python3 -I process over it lands on top of it.

Wire protocol

The host and the CPython subprocess exchange a versionless, JSON-lines protocol on the child's fd 3 — one JSON object per line, leaving stdout/stderr free for the program's own output. src/protocol.ts is the host side; py/protocol.py mirrors its message shapes and the shared truncation-marker text on the Python side.

  • fd 3, not stdout — Node pins the channel positionally with stdio: ['pipe','pipe','pipe','pipe']; the Python bootstrap reads the same PROTOCOL_FD constant. JSON-lines framing.
  • Host treats every inbound frame as hostile — model code has full access to fd 3 and can post anything through it, so validateChildFrame shape-validates and REBUILDS each frame before the host reads it: forged extra fields never ride along, a non-number call id can never be echoed into a reply, and junk drops to undefined rather than throwing in the host's message handler. The Python side trusts host replies (the host is not model-controlled).
  • Lossless-JSON crossing — completion values and binding arguments cross as exact JSON. encodeJsonPlain serializes a JSON.parse-produced value without recursion, so a deep value below the byte budget crosses intact instead of dying on JSON.stringify's stack limit; checkDoneValue meters a forged completion value's byte length AND number losslessness in one traversal that rejects an over-budget payload before the incremental work it would add (escaped-string copy, enqueued children, per-key JSON.stringify) — the frame's own width is already parsed and capped upstream by the host's fd-3 receive buffer, not re-bounded here; hasUnsafeIntegerToken reads the raw frame text to catch an integer token that JSON.parse would silently round; hasNonLosslessNumber rejects a non-finite or negative-zero number in unbounded call.args. Beyond-safe-range integral doubles serialize through BigInt digits so the exact integer crosses, not the rounded String() form.
  • Shared truncation marker — logTruncationMarker(maxBytes) produces byte-identical text on both sides, so a truncated log run reads the same however the cap was hit. The log frame's truncated flag distinguishes the child ledger's own marker from program output.

Model Experience

Indirectly, through Code Mode in dsh-tools, which renders this backend's exact completion value when it fits (or an explicit invalid-output / output-limit failure), plus the exact [dsh-code-runtime-python] log capture truncated at <maxLogBytes> bytes log marker, into a retained run_code result.

KV Cache effect

No direct invalidation; the named consumer owns any request-prefix changes.

Known Limitations and Deferred Work

  • The cross-language guard covers the runtime-executed surfaces and the frame field shapes — tests/protocol-mirror.e2e.ts spawns a real python3 and asserts, against src/protocol.ts, both PROTOCOL_FD / the log truncation marker text AND each TypedDict's required/optional wire field set in py/protocol.py. What it does not compare is the field types (e.g. that cpuSeconds is an int on both sides): comparing type declarations across TypeScript and Python has no mechanical equivalent here, so a type-level drift is still caught by review plus the backend's real-subprocess suite rather than this package's tests.
  • The PythonCodeRuntime implementation and its Python-side JSON codec are not in this layer — they ship in the backend-core PR on top of this branch; src/index.ts re-exports only the protocol vocabulary until then.