workflow, subagent: fix Codex code-review round-1 blockers

Six verified A-findings from the code-stage review, each with a regression test:

- parallel()/pipeline() resolved to HOST arrays inside the vm realm, exposing
  host Array.prototype to scripts; combinator results are now realm-built
  (in-realm Array.from bound at context setup).
- materializeFromRealm ran proxy traps (ownKeys/getOwnPropertyDescriptor/
  getPrototypeOf) during the descriptor walk — realm code on the host stack,
  outside the vm timeout, escaping as raw errors; proxies (root, nested, and
  in the prototype position) are now rejected trap-free via util.types.isProxy
  before any inspection.
- an already-aborted signal or an immediate cancel() no longer reports
  'completed' for a hook-free script: drive() checks cancellation before
  running the body and again when the script settles.
- dispose() now waits (bounded by disposeGraceMs) for stray agent() children
  to FINISH disposing, not just for the script to settle: every agent() call
  is tracked and quiesce() drains the in-flight set.
- workflow/* event payloads were live mutable aliases shared across emissions;
  emitWorkflowEvent now hands each listener its own structural clone.
- the structured-output turn-continuation veto is now prepend: true, so an
  earlier-registered force-continue listener cannot short-circuit it.

Docs updated in the same change (READMEs, core-data-structures/workflow.md,
the dynamic-workflows RFC, regenerated cordis catalogs).
This commit is contained in:
Tianyi Cui
2026-07-05 19:04:38 +08:00
parent 1d43ea3cd5
commit e264a106fd
17 changed files with 358 additions and 51 deletions

View File

@@ -5,6 +5,7 @@ import SessionStore from '@deepseek-ai/dsh-session'
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
import ToolRegistry from '@deepseek-ai/dsh-tools'
import AgentRegistry, { AgentId } from '@deepseek-ai/dsh-agent'
import type { Agent, ContinuationDecision } from '@deepseek-ai/dsh-agent'
import AgentLoop from '@deepseek-ai/dsh-agent-loop'
import * as Invariants from '@deepseek-ai/dsh-invariants'
import SubagentService, { type SubagentStartRequest } from '@deepseek-ai/dsh-subagent'
@@ -85,6 +86,34 @@ describe('in-process structured output', () => {
await run.dispose()
})
it('the captured-turn veto is prepend: an EARLIER force-continue listener cannot short-circuit it', async () => {
const ctx = new Context()
await ctx.plugin(SystemPrompt)
await ctx.plugin(ToolRegistry)
// Registered BEFORE the structured runtime exists — without prepend, this
// goal-style listener would decide the turn first (returning WITHOUT
// calling next()) and the veto would never run.
ctx.on('agent/turn-continuation', () => Promise.resolve<ContinuationDecision>({ action: 'continue' }))
const acquisition = acquireStructuredRuntime(ctx)
const agent = { id: AgentId('structured-child') } as unknown as Agent
acquisition.attach(agent, SCHEMA)
const captured = await ctx.tools.execute({
callId: 'call-1' as never,
name: STRUCTURED_OUTPUT_TOOL,
arguments: { answer: 1 },
agent,
})
expect(captured.isError).toBeFalsy()
const decision = await ctx.waterfall(
'agent/turn-continuation', agent, 1,
{ action: 'continue' },
() => Promise.resolve<ContinuationDecision>({ action: 'continue' }),
)
expect(decision).toEqual({ action: 'stop' })
acquisition.detach(agent)
acquisition.release()
})
it('an invalid call gets an INVALID_ARGS isError result and the model retries in-turn', async () => {
const { ctx, parent } = await setup([
toolCallResponse('c1', STRUCTURED_OUTPUT_TOOL, { answer: 'not-a-number' }),