Files
deepseek-harness/packages/workflow/workflow-vm/tests/meta.spec.ts
T
Tianyi Cui 2accf85714 workflow: simplify to the trust premise; settle result on cancellation
Two review responses that belong together — the same review argued the
engine was defending the wrong threat while a benign-input bug wedged
the product.

1) Drop hostile-value containment; state the trust premise.

Scripts are model-written — the same trust level as the model's bash
access — yet successive pre-push review rounds had ratcheted in defenses
that only matter against an adversarial author: trap-free proxy
rejection, accessor-never-invoked descriptor walks, realm-side
pre-rendering of thrown values, realm-built promises/arrays/error clones
with structural fatal recognition. That same author keeps a documented,
accepted, unkillable event-loop spin, so containing its error VALUES is
cost without a threat model — and the planned hardened engine
(worker/isolated-vm) gets value isolation by serialization and deletes
all of this machinery anyway.

What stays, because benign scripts hit it constantly: result never
rejects; dropped hook promises cannot become unhandled rejections; the
value boundary rejects LOUD everything JSON cannot carry (now a plain
recursive walk — getters are read ordinarily and their result is what
crosses; a throwing read fails loud); a "__proto__" key still copies as
a data property; the fatal-vs-null combinator discipline (now host
instanceof — unforgeable from the realm and simpler than clone-shape
recognition). What changes for scripts (documented in the engine
README): hooks hand back host values and host errors — in-script
`instanceof Error` on a hook failure is false (branch on e.name/e.code)
— and args are host-cloned once so a script cannot mutate the caller's
object. realm.ts drops 289 → 173 lines; the hostile-value test tables go
with it. The premise now leads the engine module doc, the README, and
the RFC's engine section, with the removed machinery recorded under
What was rejected.

2) result settles within the dispose grace of a cancellation.

Review finding (verified through the real registry + tool + engine): a
script parked on a promise no hook owns — `await new Promise(() => {})`,
`await Promise.race([])`, a returned never-settling thenable — could not
be settled by cancel(): hooks reject and children abort, but nothing
touches a promise the engine does not own, so `result` stayed pending
FOREVER (the previous cut even pinned that as intended). The tool awaits
run.result BEFORE its disposing finally, the registry awaits the tool,
the loop awaits the registry — one such script wedged the whole agent
turn past any abort, unrecoverable in-process; the mock engine in the
tool's abort test settles result on cancel, which is exactly the
behavior the real engine lacked, so no existing test could see it.

The seam contract now says it out loud: once a run is cancelled, result
SETTLES within the implementation's bounded grace even if the script
never does. The vm engine arms an abandon channel in cancel(); drive()
races the script against it, force-settling 'cancelled' at the grace
(the abandoned settlement stays contained; a post-slice synchronous spin
remains the documented limitation). dispose()'s outer race now exists
for child quiescence only, and `workflow/end` again fires exactly once
per started run. The old 'result stays pending' pin is FLIPPED to the
new contract (the pinned behavior was the bug); new regressions cover
cancel-then-settle on a parked script, a never-settling returned
thenable, and the full composition through the REAL registry + tool +
vm engine (tool-workflow gains workflow-vm/subagent devDeps for it).
agentsStarted JSDoc clarified while touching the vocabulary (accepted
calls, including ones still queued at cancellation).
2026-07-06 00:48:49 +08:00

162 lines
7.2 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { WorkflowError } from '@deepseek-ai/dsh-workflow'
import { extractMeta } from '../src/meta.ts'
const TIMEOUT = 1000
/** Extract and expect success. */
function ok(script: string) {
return extractMeta(script, TIMEOUT)
}
/** The WorkflowError a bad script produces (throws if it extracts cleanly). */
function bad(script: string): WorkflowError {
try {
extractMeta(script, TIMEOUT)
} catch (error: unknown) {
if (error instanceof WorkflowError) return error
throw error
}
throw new Error('expected extraction to fail')
}
describe('extractMeta', () => {
it('extracts a full meta block and blanks the statement line-preservingly', () => {
const script = `export const meta = {
name: 'audit-routes',
description: 'Audit every route',
whenToUse: 'when auditing',
phases: [{ title: 'Scan', detail: 'find files' }, { title: 'Fix', model: 'deepseek-v4-pro' }],
}
const x = 1
return x`
const { meta, body } = ok(script)
expect(meta).toEqual({
name: 'audit-routes',
description: 'Audit every route',
whenToUse: 'when auditing',
phases: [{ title: 'Scan', detail: 'find files' }, { title: 'Fix', model: 'deepseek-v4-pro' }],
})
// Same line count; the statement's characters blanked; the body intact.
expect(body.split('\n').length).toBe(script.split('\n').length)
expect(body.split('\n')[6]).toBe('const x = 1')
expect(body).not.toContain('export')
})
it('allows leading line and block comments before the meta statement', () => {
const script = `// a workflow
/* multi
line */
export const meta = { name: 'x', description: 'y' }
return 1`
expect(ok(script).meta.name).toBe('x')
})
it('handles braces inside strings and comments while scanning', () => {
const script = `export const meta = {
name: 'tricky', // } not a close {
/* } also not } */
description: "has { braces } and 'quotes'",
}
return 2`
expect(ok(script).meta.description).toBe("has { braces } and 'quotes'")
})
it('tolerates template-quoted strings WITHOUT interpolation, escapes included', () => {
const script = 'export const meta = { name: `plain`, description: `esc \\` tick` }\nreturn 1'
expect(ok(script).meta.name).toBe('plain')
})
it('consumes a trailing semicolon after the literal, spaces included', () => {
const { body } = ok("export const meta = { name: 'x', description: 'y' };\nreturn 1")
expect(body).not.toContain(';')
expect(body.split('\n')[1]).toBe('return 1')
const spaced = ok("export const meta = { name: 'x', description: 'y' } ;\nreturn 1")
expect(spaced.body).not.toContain(';')
})
it('rejects a script that does not begin with the meta statement (SCRIPT_PARSE)', () => {
expect(bad('const a = 1').code).toBe('SCRIPT_PARSE')
expect(bad('').code).toBe('SCRIPT_PARSE')
expect(bad('export const meta = [1]').code).toBe('SCRIPT_PARSE')
})
it('rejects template interpolation in the meta block as impure (SCRIPT_PARSE)', () => {
const error = bad('export const meta = { name: `w-${1}`, description: "d" }\nreturn 1')
expect(error.code).toBe('SCRIPT_PARSE')
expect(error.message).toContain('pure literal')
})
it('rejects unbalanced literals, unterminated strings, and unterminated comments (SCRIPT_PARSE)', () => {
expect(bad('export const meta = { name: "x", description: "y"').code).toBe('SCRIPT_PARSE')
expect(bad('export const meta = { name: "x').code).toBe('SCRIPT_PARSE')
expect(bad('export const meta = { /* open').code).toBe('SCRIPT_PARSE')
// A line comment running to EOF (no newline) leaves the literal unbalanced.
expect(bad('export const meta = { name: "x" // eof comment').code).toBe('SCRIPT_PARSE')
})
it('rejects a literal referencing variables or calls (META_INVALID via the empty realm)', () => {
const error = bad('export const meta = { name: someVariable, description: "d" }\nreturn 1')
expect(error.code).toBe('META_INVALID')
expect(error.message).toContain('pure literal')
expect(bad('export const meta = { name: compute(), description: "d" }').code).toBe('META_INVALID')
})
it('rejects a literal evaluating to non-JSON data (META_INVALID via materialization)', () => {
const error = bad('export const meta = { name: "x", description: "d", whenToUse: () => 1 }')
expect(error.code).toBe('META_INVALID')
expect(error.message).toContain('JSON data')
})
it('a meta expression that THROWS maps to META_INVALID carrying the rendered value', () => {
const error = bad('export const meta = { name: (() => { throw "nope" })(), description: "d" }\nreturn 1')
expect(error.code).toBe('META_INVALID')
expect(error.message).toContain('pure literal')
expect(error.message).toContain('nope')
})
it('a spinning meta expression dies by the eval timeout', () => {
try {
extractMeta('export const meta = { name: (() => { while (true) {} })(), description: "d" }', 50)
throw new Error('expected the extraction to time out')
} catch (error: unknown) {
expect(error).toBeInstanceOf(WorkflowError)
expect((error as WorkflowError).code).toBe('META_INVALID')
expect((error as WorkflowError).message.toLowerCase()).toContain('timed out')
}
})
it('rejects shape violations with EVERY violation listed (META_INVALID)', () => {
const error = bad('export const meta = { description: 7, bogus: 1 }\nreturn 1')
expect(error.code).toBe('META_INVALID')
expect(error.message).toContain('meta.name must be a non-empty string')
expect(error.message).toContain('meta.description must be a non-empty string')
expect(error.message).toContain('meta.bogus is not a recognized field')
})
it('rejects malformed whenToUse and phases shapes precisely', () => {
expect(bad('export const meta = { name: "x", description: "d", whenToUse: 3 }').message)
.toContain('meta.whenToUse must be a string')
expect(bad('export const meta = { name: "x", description: "d", phases: "no" }').message)
.toContain('meta.phases must be an array')
expect(bad('export const meta = { name: "x", description: "d", phases: [3] }').message)
.toContain('meta.phases[0] must be an object')
expect(bad('export const meta = { name: "x", description: "d", phases: [{}] }').message)
.toContain('meta.phases[0].title must be a non-empty string')
expect(bad('export const meta = { name: "x", description: "d", phases: [{ title: "t", extra: 1 }] }').message)
.toContain('meta.phases[0].extra is not a recognized field')
expect(bad('export const meta = { name: "x", description: "d", phases: [{ title: "t", detail: 1 }] }').message)
.toContain('meta.phases[0].detail must be a string')
expect(bad('export const meta = { name: "x", description: "d", phases: [{ title: "t", model: 1 }] }').message)
.toContain('meta.phases[0].model must be a string')
})
it('stops scanning at the balanced literal — trailing expression text stays in the body', () => {
// The scanner extracts exactly `{ valueOf: null }`; the ` && 3` is body
// text (which would fail compilation later, but extraction sees only the
// literal and reports its unknown field).
expect(bad('export const meta = { valueOf: null } && 3').message)
.toContain('meta.valueOf is not a recognized field')
})
})