workflow: meta rides the seam as data — the engine never evaluates it
P1 review finding: extractMeta timed only the literal's vm evaluation;
materializing the RESULT then read properties ordinarily on the HOST
stack, so a meta literal smuggling a getter (get name() { while(true){} })
could wedge the host outside any timeout — defeating the exact spin
isolation the worker thread exists for.
Rather than harden the evaluator (descriptor walks, AST validation),
delete the mechanism: the workflow's identity now reaches the seam as a
plain JSON field (WorkflowStartRequest.meta), carried by the tool as a
schema-validated `meta` object parameter the model fills directly. The
engine only shape-validates data (validateMeta, every violation named)
and pre-parses the body; the scanner, the vm evaluation, and the
host-side materialization are gone, and with them the hole. A body
still opening with a Claude Code-style `export const meta` statement
gets a pointed SCRIPT_PARSE message (the likeliest authoring slip; a
CC script's body stays drop-in, only its meta header moves into the
parameter). syncTimeoutMs now governs exactly one thing: the initial
synchronous slice inside the worker.
The RFC's decision section is rewritten in place (implemented-RFC
rule); the embedded-meta format moves to alternatives-considered with
the hole as the reason. Tool description, presentation (title now reads
meta.name directly — the textual sniff is gone), seam vocabulary docs,
and catalogs follow.
This commit is contained in:
@@ -50,8 +50,8 @@ describe('dsh-workflow-workerthread over the real in-process stack', () => {
|
||||
const childIds: string[] = []
|
||||
ctx.on('workflow/agent-start', (_info, agent) => { childIds.push(agent.childId) })
|
||||
const run = ctx.workflows.start({
|
||||
script: `export const meta = { name: 'integration', description: 'plain + structured children' }
|
||||
phase('Read')
|
||||
meta: { name: 'integration', description: 'plain + structured children' },
|
||||
script: `phase('Read')
|
||||
const prose = await agent('read the repo')
|
||||
phase('Judge')
|
||||
const judged = await agent('judge: ' + prose, {
|
||||
@@ -78,8 +78,8 @@ return { prose, verdict: judged.verdict, confidence: judged.confidence }`,
|
||||
textResponse('still prose after the nudge'),
|
||||
])
|
||||
const run = ctx.workflows.start({
|
||||
script: `export const meta = { name: 'null-path', description: 'schema failure maps to null' }
|
||||
const judged = await agent('judge it', { schema: { type: 'object', properties: { v: { type: 'string' } } } })
|
||||
meta: { name: 'null-path', description: 'schema failure maps to null' },
|
||||
script: `const judged = await agent('judge it', { schema: { type: 'object', properties: { v: { type: 'string' } } } })
|
||||
return { got: judged === null ? 'null' : 'value' }`,
|
||||
parent,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user