Skip to content

feat(workflow): journal store + script sandbox - #95

Merged
Waishnav merged 76 commits into
pr/dw-1-types-effortfrom
pr/dw-2-store-sandbox
Jul 27, 2026
Merged

feat(workflow): journal store + script sandbox#95
Waishnav merged 76 commits into
pr/dw-1-types-effortfrom
pr/dw-2-store-sandbox

Conversation

@Waishnav

@Waishnav Waishnav commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • DB v5: workflow_runs / workflow_events / workflow_agent_calls
  • WorkflowStore journal API (create/claim/events/agent calls/reap)
  • Script meta extract + vm.Script compile
  • Restricted sandbox (Date/Math bans, no process/require/fetch)

Stack

PR2 of 5 · base: pr/dw-1-types-effort

Test plan

  • tsx src/workflow-store.test.ts
  • tsx src/workflow-script.test.ts
  • tsx src/workflow-sandbox.test.ts
  • npm run typecheck

Summary by CodeRabbit

  • New Features

    • Added support for defining and validating workflow scripts with metadata, size limits, and syntax checks.
    • Added a secure, deterministic workflow execution environment with logging, budgets, timeouts, and restricted access.
    • Added persistent workflow run tracking, event history, cancellation, retries, agent-call state, and stale-run recovery.
    • Added database support for storing workflow execution data.
  • Tests

    • Expanded coverage for workflow parsing, sandbox behavior, persistence, lifecycle management, and database migrations.

Waishnav and others added 9 commits July 21, 2026 17:10
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>
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 94a1970d-e746-4e29-8a57-8bfce061476e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch pr/dw-2-store-sandbox

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread src/workflow-sandbox.ts Outdated
throw new Error("Workflow script did not compile to a function");
}

const result = await withTimeout(

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/workflow-sandbox.ts Outdated
consoleProxy: Record<string, (...args: unknown[]) => void>,
): Record<string, unknown> {
return {
Object,

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/workflow-script.ts Outdated
const body = normalized.replace(META_EXPORT, " const meta =");

// Reject further imports / exports after transform
if (/\bimport\s+/.test(body) || /\bexport\s+/.test(body)) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/workflow-store.ts Outdated
return {
events,
nextSeq,
terminal: TERMINAL_STATUSES.has(run.status),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Waishnav added 27 commits July 27, 2026 13:01
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
@Waishnav
Waishnav marked this pull request as ready for review July 27, 2026 15:05
@Waishnav
Waishnav merged commit 0e30254 into pr/dw-1-types-effort Jul 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant