feat(workflow): add dynamic workflows - #94
Conversation
Add workflow-types contract module (statuses, AgentOpts, journal shapes, cache key helper, budget stub, concurrency). Wire test into package.json. Fix primitives-spec §13: isolation worktree is v1 must-have. Co-Authored-By: Claude <noreply@anthropic.com>
Normalize LocalAgentRunInput, profiles, CLI, store, and adapters on effort. CLI accepts --thinking as a one-release alias; profile YAML thinking: still maps to effort. DB migration v4 renames the column. Co-Authored-By: Claude <noreply@anthropic.com>
Update profile schema, examples, and subagent skill for effort. Note legacy thinking alias and provider-native pi --thinking flag. Co-Authored-By: Claude <noreply@anthropic.com>
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 skippedDraft detected. 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 |
|
The primitive set is a strong read-oriented MVP, but I do not think the public contract should be called frozen yet. |
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.
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
feat(workflow): journal store + script sandbox
| } else if (scriptPath) { | ||
| const resolvedResult = await readWorkflowScriptFileResult(scriptPath); |
There was a problem hiding this comment.
Unchecked workflow script path
When an authenticated MCP client supplies a scriptPath outside the selected workspace, the handler reads, persists, and executes that file without validating it against the workspace or allowed roots, allowing external workflow logic to run agents with write access to the selected workspace.
How this was verified: The caller-controlled path flows directly through readWorkflowScriptFileResult, whose generic reader resolves and reads it without a root or canonical-path check.
| async ({ runId, sinceSeq, yieldTimeMs }) => { | ||
| const store = createWorkflowStore(config); | ||
| try { | ||
| const runResult = store.getRunResult(runId); | ||
| if (runResult.isErr()) throw runResult.error; | ||
| if (!runResult.value) throw new WorkflowNotFoundError(runId); | ||
| const page = await yieldEvents(store, runId, sinceSeq ?? 0, yieldTimeMs ?? 0); | ||
| return toolResult(page, "workflow_status"); | ||
| } catch (error) { | ||
| if (isWorkflowOperationError(error)) return workflowToolError(error); | ||
| throw error; | ||
| } finally { | ||
| store.close(); | ||
| } | ||
| }, |
There was a problem hiding this comment.
When a client supplies a run ID created for another workspace, workflow_status retrieves that global record and workflow_cancel can cancel it without verifying workspace ownership, exposing the other run's status, result, or error and allowing cross-workspace cancellation.
How this was verified: Both handlers address the shared workflow store solely by runId and perform no comparison with the run's workspaceId or workspaceRoot.
Greptile SummaryThis PR introduces the Dynamic Workflow contracts and runtime while renaming local-agent reasoning configuration from thinking to effort.
Confidence Score: 3/5The PR should not merge until workflow script loading and run operations are constrained to the caller's authorized workspace. The new MCP workflow surface permits unchecked filesystem paths to supply executable workflow source and uses globally addressable run identifiers for status and cancellation without workspace ownership validation. Files Needing Attention: src/workflow-tools.ts, src/workflow-files.ts, src/workflow-store.ts
|
| Filename | Overview |
|---|---|
| src/workflow-tools.ts | Adds the MCP workflow surface, but script paths and run-level operations are not consistently constrained to the selected workspace. |
| src/workflow-files.ts | Adds source persistence and named/project workflow resolution; specialized project readers are scoped, while the generic script reader accepts unrestricted paths. |
| src/workflow-api.ts | Implements workflow primitives, concurrency, replay journaling, profile resolution, schema enforcement, and worktree finalization with coherent lifecycle handling. |
| src/workflow-store.ts | Adds the global SQLite workflow journal and state transitions; consumers must enforce workspace ownership when accessing runs. |
| src/workflow-cli.ts | Adds detached workflow execution, lifecycle commands, provider invocation, replay, and follow-mode output. |
| src/db/migrations.ts | Renames the local-agent thinking column to effort and adds the workflow run, event, call, replay, and profile-provenance schema. |
| src/workflow-types.ts | Freezes workflow limits, records, options, event contracts, cache identity, and public runtime types. |
| src/server.ts | Gates and registers workflow capabilities, exposes active workflow summaries, and integrates the workflow reaper and UI. |
| src/local-agent-resolution.ts | Centralizes provider/profile selection and effort/model precedence for direct agents and workflows. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Client[MCP client] --> Tools[Workflow MCP tools]
Tools --> Files[Script resolution and persistence]
Files --> Worker[Detached workflow worker]
Worker --> Sandbox[Workflow sandbox]
Sandbox --> Agents[Local agent providers]
Agents --> Workspace[Workspace or isolated worktree]
Worker <--> Store[(Global workflow journal)]
Tools <--> Store
Reviews (1): Last reviewed commit: "Merge pull request #95 from pr/dw-2-stor..." | Re-trigger Greptile
DevSpace can invoke local agents one call at a time, but it has no durable orchestration layer for work that needs concurrency, nested calls, resumability, inspection, or cancellation. Long-running multi-agent tasks therefore depend on the chat session staying alive, and recovery after interruption or script edits is difficult to reason about.
This draft adds opt-in Dynamic Workflows: typed public contracts and limits; a SQLite journal for runs, events, and agent calls; restricted child-process script execution; CLI and MCP run, status, cancellation, and inspection surfaces; deterministic prefix replay with provenance; worktree isolation; lifecycle supervision; provider and profile selection; schema-aware outputs; and read-only TUI and MCP App dashboards. It also standardizes local-agent
effortacross profiles, adapters, storage, and CLI so workflows and direct subagent execution use the same provider model.Stack
This PR is the merge vehicle for Dynamic Workflows. Review and land the remaining refinements in order: #119 → #120 → #121. Each PR is based on the previous one and should be merged into this branch before this PR targets
main.Dynamic Workflows remain experimental and opt-in through the existing agent capability gates.