feat(workflow): journal store + script sandbox - #95
Conversation
Create workflow_runs, workflow_events, and workflow_agent_calls with indexes. Effort rename remains v4; journal schema is v5. Co-Authored-By: Claude <noreply@anthropic.com>
Create/claim/cancel/complete runs, monotonic event append+drain, agent call lifecycle, and stale-worker reaping. Wire unit tests. Co-Authored-By: Claude <noreply@anthropic.com>
Parse export const meta (pure literal), compile scripts as vm.Script, and run them with banned Date.now/Math.random/bare new Date plus no process/require/fetch. Rehydrate results into the host realm. Co-Authored-By: Claude <noreply@anthropic.com>
agent/parallel/pipeline/phase/log/workflow primitives, semaphore, ALS phase, isolation worktree hook, nest depth 1, and executeWorkflow against injectable runProvider for unit tests. Co-Authored-By: Claude <noreply@anthropic.com>
Named/file script resolve, agent worktree factory, resume matcher (index+key then consume-once), and Ajv schema enforcement wired into agent(). Adds ajv dependency. Co-Authored-By: Claude <noreply@anthropic.com>
Detached __worker with heartbeat/cancel, real adapters via runLocalAgentProvider, worktree isolation, and resume wiring. setScriptPath persists script after run create. Co-Authored-By: Claude <noreply@anthropic.com>
Always include bundled skills root when subagents enabled so seeding subagent-delegation no longer hides later skills. Seed dynamic-workflows alongside subagent-delegation on init. Co-Authored-By: Claude <noreply@anthropic.com>
Load ordered enable-list from config/env, probe available providers on init and doctor, and filter workflow CLI providers by enabled∩live. Co-Authored-By: Claude <noreply@anthropic.com>
Gate on config.subagents. run_workflow spawns the same detached worker as CLI; status long-polls journal events; cancel requests cooperative stop. Co-Authored-By: Claude <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| throw new Error("Workflow script did not compile to a function"); | ||
| } | ||
|
|
||
| const result = await withTimeout( |
There was a problem hiding this comment.
[P0] This timeout does not stop synchronous workflow code. The vm.Script timeout only covers creation of the async factory, and this Promise timer cannot fire while factory() is blocking the event loop (for example, while (true) {}). The workflow body needs to run in an externally terminable worker/child process, with a regression test for a synchronous infinite loop.
There was a problem hiding this comment.
Addressed by follow-up PR #114 (f5e9252, 09a7fc4). Workflow JavaScript now runs in a disposable child process, and the parent enforces the host timeout by terminating that process. A regression test executes while (true) {} with a short timeout, verifies it is externally killed, and then runs another workflow to prove the host remains responsive.
| consoleProxy: Record<string, (...args: unknown[]) => void>, | ||
| ): Record<string, unknown> { | ||
| return { | ||
| Object, |
There was a problem hiding this comment.
[P0] Injecting host-realm constructors makes this sandbox escapable. I reproduced Object.constructor('return process.version')() successfully recovering the host process. Omitting process/require from globals is therefore insufficient. Please stop exposing host constructors and treat a disposable worker/child process as the actual capability boundary; add constructor-chain escape tests.
There was a problem hiding this comment.
Addressed by follow-up PR #114. Workflow APIs and determinism shims are now created inside the VM context and close over a hidden IPC bridge; host-realm constructors are no longer injected into the model-authored script. Constructor-chain regression tests cover both Object.constructor(...) and agent.constructor(...), and neither can recover the child or parent process object.
| const body = normalized.replace(META_EXPORT, " const meta ="); | ||
|
|
||
| // Reject further imports / exports after transform | ||
| if (/\bimport\s+/.test(body) || /\bexport\s+/.test(body)) { |
There was a problem hiding this comment.
[P1] This scans raw source, so valid strings/comments containing import or export are rejected; agent('Explain export syntax') reproduces it. Please use a parser/tokenizer, or a lexical scan that correctly ignores strings, comments, template literals, and regex literals.
There was a problem hiding this comment.
Addressed by follow-up PR #117 (81a0a2e). The raw import/export text scan was removed. Static module syntax is now rejected by the JavaScript parser when compiling the wrapped workflow, while strings, comments, template literals, and regex literals containing those words remain valid. Regression tests cover all of those lexical contexts.
| return { | ||
| events, | ||
| nextSeq, | ||
| terminal: TERMINAL_STATUSES.has(run.status), |
There was a problem hiding this comment.
[P1] terminal can become true while later event pages still exist. A completed run with more events than the page limit makes callers stop after the first page and silently lose the remainder. Please return hasMore/journal-complete separately and only tell a reader it is done when the run is terminal and no events remain after this page.
There was a problem hiding this comment.
Addressed by follow-up PR #115 (dfbf5a1). drainEvents() now reads one extra row, returns hasMore, and reports terminal: true only when the run is terminal and no unread events remain after the returned page. MCP status also exposes hasMore, so orchestrators do not stop at a terminal status while journal pages remain.
Wire LocalAgentRunInput.schema through CodexSdkLocalAgentRuntime to thread.run turn options, and surface parsed structured output.
Pass JSON Schema via outputFormat on query options and prefer structured_output from result messages. OpenCode stays prompt-path only.
Hardcode NATIVE_SCHEMA_PROVIDERS; attempt 0 uses adapter schema without prompt bloat, then prompt-repair retries with Ajv. Wire schema through runProvider / CLI worker. Document in skill.
refactor(agents): unify provider resolution and subagent guidance
feat(workflow): support agent profiles and project workflows
fix(workflow): replay deterministic call prefixes
fix(workflow): supervise cancellation and terminal lifecycle
feat(workflow): isolate workflow script execution
feat(ui): live workspace and workflow dashboards
feat(workflow): read-only project workflow TUI
feat(workflow): script iteration, replay provenance, and call inspection
refactor(workflow): model operational failures with better-result
refactor(workflow): strengthen contracts and zod boundaries
feat(agents): native schema for codex/claude agent({ schema })
feat(workflow): MCP tools, skill, agentProviders
feat(workflow): CLI worker, worktrees, resume, schema
feat(workflow): engine API with fakes
Summary
workflow_runs/workflow_events/workflow_agent_callsWorkflowStorejournal API (create/claim/events/agent calls/reap)vm.ScriptcompileStack
PR2 of 5 · base:
pr/dw-1-types-effortTest plan
tsx src/workflow-store.test.tstsx src/workflow-script.test.tstsx src/workflow-sandbox.test.tsnpm run typecheckSummary by CodeRabbit
New Features
Tests