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:
@@ -81,6 +81,27 @@ describe('materializeFromRealm', () => {
|
||||
expect(materializeFromRealm(inRealm('Object.assign(Object.create(null), { a: 1 })'))).toEqual({ a: 1 })
|
||||
})
|
||||
|
||||
it('rejects proxies (root, nested, revoked, host-realm) WITHOUT running any trap', () => {
|
||||
const trapped = inRealm(`new Proxy({ a: 1 }, {
|
||||
ownKeys() { throw new Error('trap ran') },
|
||||
getOwnPropertyDescriptor() { throw new Error('trap ran') },
|
||||
getPrototypeOf() { throw new Error('trap ran') },
|
||||
})`)
|
||||
// A trap firing would surface 'trap ran' (a non-MaterializeError) instead.
|
||||
expect(rejection(trapped)).toContain('proxies cannot cross')
|
||||
expect(rejection(inRealm('{ nested: new Proxy([], {}) }'))).toContain('value.nested')
|
||||
const revoked = inRealm('(() => { const r = Proxy.revocable({}, {}); r.revoke(); return r.proxy })()')
|
||||
expect(rejection(revoked)).toContain('proxies cannot cross')
|
||||
expect(rejection(new Proxy({}, {}))).toContain('proxies cannot cross')
|
||||
})
|
||||
|
||||
it('rejects an object whose PROTOTYPE is a proxy without dereferencing through it', () => {
|
||||
const value = inRealm(`Object.create(new Proxy({}, {
|
||||
getPrototypeOf() { throw new Error('trap ran') },
|
||||
}))`)
|
||||
expect(rejection(value)).toContain('exotic prototype')
|
||||
})
|
||||
|
||||
it('rejects cycles and accepts the same object reused as a sibling (a DAG)', () => {
|
||||
expect(rejection(inRealm('(() => { const o = {}; o.self = o; return o })()'))).toContain('circular')
|
||||
const dag = inRealm('(() => { const leaf = { v: 1 }; return { a: leaf, b: leaf } })()')
|
||||
|
||||
Reference in New Issue
Block a user