feat: add the worker-thread code runtime (dsh-code-runtime-worker)
The shipped backend of the code-execution seam, per the Code Mode RFC's
worker-thread section: one fresh Node worker per run, executing the
model's TypeScript after a host-side type-strip (wrapped in an
async-function shell so top-level return/await parse, sliced back out
position-preserved), bindings bridged over the message port under
hostile-peer rules (own-property name lookup, at-most-once replies,
post-settlement drops, null-prototype namespaces), logs streamed eagerly
with an in-band truncation marker, and two independent budgets — measured
event-loop busy time (computeMs) plus a never-pausing wall ceiling
(maxWallMs) — funneling into worker.terminate(). env: {} and execArgv: []
keep the isolate hermetic; disposal aborts in-flight runs and awaits
worker exits.
The worker entry loads unbuilt via Node's native type stripping
(src/worker.ts, erasable-only) and ships built as a sibling tsdown bundle
(lib/worker.js); tests/built-lib.e2e.ts pins the built load path under
plain node and joins the built-artifact smoke gate. Unit suites cover the
bootstrap in-process (fake port) and the runtime over real workers,
per-file 100%.
2026-07-08 11:00:06 +08:00
|
|
|
import { spawn } from 'node:child_process'
|
|
|
|
|
import { existsSync } from 'node:fs'
|
|
|
|
|
import { join } from 'node:path'
|
|
|
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
|
|
|
|
|
|
/**
|
2026-07-14 00:40:36 +08:00
|
|
|
* Keyless built-artifact smoke: plain Node imports the package by name through its exports map,
|
|
|
|
|
* then exercises type stripping, sibling `worker.cjs` loading, bindings, and logs. Unit tests use
|
|
|
|
|
* `src/worker.ts`; this pins the downstream `lib/index.js` path. It skips when `lib/` is absent,
|
|
|
|
|
* and CI runs it after the build.
|
feat: add the worker-thread code runtime (dsh-code-runtime-worker)
The shipped backend of the code-execution seam, per the Code Mode RFC's
worker-thread section: one fresh Node worker per run, executing the
model's TypeScript after a host-side type-strip (wrapped in an
async-function shell so top-level return/await parse, sliced back out
position-preserved), bindings bridged over the message port under
hostile-peer rules (own-property name lookup, at-most-once replies,
post-settlement drops, null-prototype namespaces), logs streamed eagerly
with an in-band truncation marker, and two independent budgets — measured
event-loop busy time (computeMs) plus a never-pausing wall ceiling
(maxWallMs) — funneling into worker.terminate(). env: {} and execArgv: []
keep the isolate hermetic; disposal aborts in-flight runs and awaits
worker exits.
The worker entry loads unbuilt via Node's native type stripping
(src/worker.ts, erasable-only) and ships built as a sibling tsdown bundle
(lib/worker.js); tests/built-lib.e2e.ts pins the built load path under
plain node and joins the built-artifact smoke gate. Unit suites cover the
bootstrap in-process (fake port) and the runtime over real workers,
per-file 100%.
2026-07-08 11:00:06 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
const pkgDir = fileURLToPath(new URL('..', import.meta.url))
|
2026-07-13 20:55:47 +08:00
|
|
|
const built = ['lib/index.js', 'lib/worker.cjs'].every(file => existsSync(join(pkgDir, file)))
|
feat: add the worker-thread code runtime (dsh-code-runtime-worker)
The shipped backend of the code-execution seam, per the Code Mode RFC's
worker-thread section: one fresh Node worker per run, executing the
model's TypeScript after a host-side type-strip (wrapped in an
async-function shell so top-level return/await parse, sliced back out
position-preserved), bindings bridged over the message port under
hostile-peer rules (own-property name lookup, at-most-once replies,
post-settlement drops, null-prototype namespaces), logs streamed eagerly
with an in-band truncation marker, and two independent budgets — measured
event-loop busy time (computeMs) plus a never-pausing wall ceiling
(maxWallMs) — funneling into worker.terminate(). env: {} and execArgv: []
keep the isolate hermetic; disposal aborts in-flight runs and awaits
worker exits.
The worker entry loads unbuilt via Node's native type stripping
(src/worker.ts, erasable-only) and ships built as a sibling tsdown bundle
(lib/worker.js); tests/built-lib.e2e.ts pins the built load path under
plain node and joins the built-artifact smoke gate. Unit suites cover the
bootstrap in-process (fake port) and the runtime over real workers,
per-file 100%.
2026-07-08 11:00:06 +08:00
|
|
|
&& existsSync(join(pkgDir, '../code-runtime/lib/index.js'))
|
|
|
|
|
|
|
|
|
|
describe.skipIf(!built)('built lib real load path (plain node)', () => {
|
2026-07-13 20:55:47 +08:00
|
|
|
it('runs a TypeScript program with a binding through lib/index.js and its lib/worker.cjs entry', async () => {
|
feat: add the worker-thread code runtime (dsh-code-runtime-worker)
The shipped backend of the code-execution seam, per the Code Mode RFC's
worker-thread section: one fresh Node worker per run, executing the
model's TypeScript after a host-side type-strip (wrapped in an
async-function shell so top-level return/await parse, sliced back out
position-preserved), bindings bridged over the message port under
hostile-peer rules (own-property name lookup, at-most-once replies,
post-settlement drops, null-prototype namespaces), logs streamed eagerly
with an in-band truncation marker, and two independent budgets — measured
event-loop busy time (computeMs) plus a never-pausing wall ceiling
(maxWallMs) — funneling into worker.terminate(). env: {} and execArgv: []
keep the isolate hermetic; disposal aborts in-flight runs and awaits
worker exits.
The worker entry loads unbuilt via Node's native type stripping
(src/worker.ts, erasable-only) and ships built as a sibling tsdown bundle
(lib/worker.js); tests/built-lib.e2e.ts pins the built load path under
plain node and joins the built-artifact smoke gate. Unit suites cover the
bootstrap in-process (fake port) and the runtime over real workers,
per-file 100%.
2026-07-08 11:00:06 +08:00
|
|
|
const script = `
|
|
|
|
|
const { Context } = await import('cordis')
|
|
|
|
|
const { WorkerCodeRuntime } = await import('@deepseek-ai/dsh-code-runtime-worker')
|
|
|
|
|
const ctx = new Context()
|
|
|
|
|
await ctx.plugin(WorkerCodeRuntime, {})
|
|
|
|
|
const result = await ctx.codeRuntime.run({
|
2026-07-23 01:32:17 +08:00
|
|
|
program: 'const doubled: number = await tools.double({ n: 21 }); console.log("halfway", doubled); let failure; try { await tools.fail({}) } catch (error) { failure = { typed: error instanceof ToolCallError, name: error.name, toolName: error.toolName, message: error.message } } return { doubled, failure };',
|
|
|
|
|
bindings: [{
|
|
|
|
|
global: 'tools',
|
|
|
|
|
functions: {
|
|
|
|
|
double: async args => args.n * 2,
|
|
|
|
|
fail: async () => { throw new Error('denied') },
|
|
|
|
|
},
|
|
|
|
|
errorClass: { name: 'ToolCallError', memberNameProperty: 'toolName' },
|
|
|
|
|
}],
|
feat: add the worker-thread code runtime (dsh-code-runtime-worker)
The shipped backend of the code-execution seam, per the Code Mode RFC's
worker-thread section: one fresh Node worker per run, executing the
model's TypeScript after a host-side type-strip (wrapped in an
async-function shell so top-level return/await parse, sliced back out
position-preserved), bindings bridged over the message port under
hostile-peer rules (own-property name lookup, at-most-once replies,
post-settlement drops, null-prototype namespaces), logs streamed eagerly
with an in-band truncation marker, and two independent budgets — measured
event-loop busy time (computeMs) plus a never-pausing wall ceiling
(maxWallMs) — funneling into worker.terminate(). env: {} and execArgv: []
keep the isolate hermetic; disposal aborts in-flight runs and awaits
worker exits.
The worker entry loads unbuilt via Node's native type stripping
(src/worker.ts, erasable-only) and ships built as a sibling tsdown bundle
(lib/worker.js); tests/built-lib.e2e.ts pins the built load path under
plain node and joins the built-artifact smoke gate. Unit suites cover the
bootstrap in-process (fake port) and the runtime over real workers,
per-file 100%.
2026-07-08 11:00:06 +08:00
|
|
|
})
|
|
|
|
|
console.log(JSON.stringify(result))
|
|
|
|
|
process.exit(0)
|
|
|
|
|
`
|
|
|
|
|
const child = spawn(process.execPath, ['--input-type=module', '-e', script], { cwd: pkgDir, stdio: ['ignore', 'pipe', 'pipe'] })
|
|
|
|
|
let stdout = ''
|
|
|
|
|
let stderr = ''
|
|
|
|
|
child.stdout.on('data', (chunk: Buffer) => { stdout += chunk.toString('utf8') })
|
|
|
|
|
child.stderr.on('data', (chunk: Buffer) => { stderr += chunk.toString('utf8') })
|
|
|
|
|
const exitCode = await new Promise<number | null>(resolve => child.on('close', resolve))
|
|
|
|
|
|
|
|
|
|
expect(exitCode, `stderr:\n${stderr}`).toBe(0)
|
|
|
|
|
const lastLine = stdout.trim().split('\n').at(-1) ?? ''
|
2026-07-14 03:07:41 +08:00
|
|
|
const result = JSON.parse(lastLine) as { value?: unknown; logs: string[]; error?: unknown }
|
feat: add the worker-thread code runtime (dsh-code-runtime-worker)
The shipped backend of the code-execution seam, per the Code Mode RFC's
worker-thread section: one fresh Node worker per run, executing the
model's TypeScript after a host-side type-strip (wrapped in an
async-function shell so top-level return/await parse, sliced back out
position-preserved), bindings bridged over the message port under
hostile-peer rules (own-property name lookup, at-most-once replies,
post-settlement drops, null-prototype namespaces), logs streamed eagerly
with an in-band truncation marker, and two independent budgets — measured
event-loop busy time (computeMs) plus a never-pausing wall ceiling
(maxWallMs) — funneling into worker.terminate(). env: {} and execArgv: []
keep the isolate hermetic; disposal aborts in-flight runs and awaits
worker exits.
The worker entry loads unbuilt via Node's native type stripping
(src/worker.ts, erasable-only) and ships built as a sibling tsdown bundle
(lib/worker.js); tests/built-lib.e2e.ts pins the built load path under
plain node and joins the built-artifact smoke gate. Unit suites cover the
bootstrap in-process (fake port) and the runtime over real workers,
per-file 100%.
2026-07-08 11:00:06 +08:00
|
|
|
expect(result.error).toBeUndefined()
|
2026-07-23 01:32:17 +08:00
|
|
|
expect(result.value).toEqual({
|
|
|
|
|
doubled: 42,
|
|
|
|
|
failure: { typed: true, name: 'ToolCallError', toolName: 'fail', message: 'denied' },
|
|
|
|
|
})
|
2026-07-14 03:07:41 +08:00
|
|
|
expect(result.logs).toContain('halfway 42')
|
feat: add the worker-thread code runtime (dsh-code-runtime-worker)
The shipped backend of the code-execution seam, per the Code Mode RFC's
worker-thread section: one fresh Node worker per run, executing the
model's TypeScript after a host-side type-strip (wrapped in an
async-function shell so top-level return/await parse, sliced back out
position-preserved), bindings bridged over the message port under
hostile-peer rules (own-property name lookup, at-most-once replies,
post-settlement drops, null-prototype namespaces), logs streamed eagerly
with an in-band truncation marker, and two independent budgets — measured
event-loop busy time (computeMs) plus a never-pausing wall ceiling
(maxWallMs) — funneling into worker.terminate(). env: {} and execArgv: []
keep the isolate hermetic; disposal aborts in-flight runs and awaits
worker exits.
The worker entry loads unbuilt via Node's native type stripping
(src/worker.ts, erasable-only) and ships built as a sibling tsdown bundle
(lib/worker.js); tests/built-lib.e2e.ts pins the built load path under
plain node and joins the built-artifact smoke gate. Unit suites cover the
bootstrap in-process (fake port) and the runtime over real workers,
per-file 100%.
2026-07-08 11:00:06 +08:00
|
|
|
})
|
|
|
|
|
})
|