Automation
Save repeatable agent and code work as durable workflows.
A workflow is a durable definition of what should happen. Manual, scheduled, and supported event-triggered runs execute today. Runs record code steps, waits, outputs, and access rules.
When to Make Work Repeatable
Start with a manual agent run, package action, or webhook setup. Save a workflow only after the useful task has a stable trigger, scoped capabilities, an approval point for risky writes, and evidence that someone can inspect after each run.
What a Workflow Stores
Create One From a Prompt
Open the Workflows page, describe the repeatable job in the authoring panel, and start the workflow-building agent. ExecWarden opens that agent with workflow admin capability and asks it to call workflow:validate before workflow:create or workflow:update. If the request is ready, the agent saves the workflow and links back to it; if credentials, package setup, or policy details are missing, it should draft the shape and explain what to review before the workflow is runnable. Open the saved workflow from the returned link or the Workflows page to inspect its definition, run it, and review the run evidence.
Advanced fallback: start a generic agent session, open Configure capabilities, enable Workflow authoring with Admin access, then ask for the workflow you want. That manual route uses the same workflow management functions, but the Workflows-page authoring panel is the primary entry point.
Create a manual workflow named Echo input. It should accept a JSON object, return it with an ok flag, and be enabled so I can run it from the Workflows page.
Create a manual workflow that starts an agent to inspect my repo status, summarize open risks, and ask before making any GitHub write.
Create a scheduled workflow named Daily readiness check. It should run every weekday at 09:00 Europe/Brussels time, inspect a JSON input trigger object, and return a concise ok/status result.
Create a graph workflow with a JS step that checks a condition from input. If it is not ready, return a durable wait for 60 seconds; otherwise finish with a ready result.
Create a workflow that subscribes to the existing webhook:incident-router receiver's incident.created event and starts an investigation agent with the event payload.For webhooks, set up the receiver first from the Webhooks page or a webhook-capable agent, then create a workflow that subscribes to the receiver's emitted events. Webhook handlers publish workflow events; workflows do not subscribe to raw HTTP requests. See Webhook Events for the receiver and handler shape.
Current Executable Slice
Agents with workflow capability can create, run, and schedule workflows today. Schedules use five-field minute cron with optional timezone and fire through the gateway scheduler with one run per scheduled slot. Workflows can also subscribe to supported events by source/type, including events emitted by GitHub-preset or custom webhook receivers. A workflow can be a single bounded awaited wasm-js entrypoint, or a persisted graph of committed wasm-js steps. Each run freezes the workflow definition and policy it started with. Graph steps are the durable path: each step commits before the next one starts, and JS steps can pause by returning a durable wait directive instead of sleeping inside JavaScript. Long-running agent work follows the same model: start a hosted agent run, store its run id in wait output, then poll it after the workflow resumes.
Durability Guarantees and Limits
Programming Model
Durable Waits
Do not keep a JavaScript loop alive while waiting for a remote condition. Run a short check, return a wait directive, and let the workflow engine persist the pause. If next is omitted, the workflow resumes the same step. If the returned value includes output, that output becomes the next input when the wait resumes.
export async function main(ctx, workflow, gateway) {
const ready = await remoteCondition(ctx.input);
if (ready) {
return workflow.next("finish", { ready: true });
}
return workflow.sleep("60s", {
output: { attempt: (ctx.input.attempt ?? 0) + 1 }
});
}The raw object form still works and is what the helpers compile to:
export async function main(ctx) {
const ready = await remoteCondition(ctx.input);
if (ready) {
return { output: { ready: true }, next: "finish" };
}
return {
wait: { delayMs: 60_000 },
output: { attempt: (ctx.input.attempt ?? 0) + 1 }
};
}Durable Agent Runs
Workflows should treat hosted agents as long-running jobs. Start the agent once with a stable idempotency key, return a durable wait, then poll the run after resume. This keeps the workflow recoverable and lets the next step branch from the agent result.
export async function main(ctx, workflow, gateway) {
const requestId = String(ctx.input.requestId);
if (!ctx.input.agentRunId) {
const started = await gateway.call("gateway:agent_runs.start", {
idempotencyKey: `workflow-agent-${requestId}`,
title: "Review report",
prompt: `Review ${ctx.input.reportName} and return launch risks.`
});
if (!started.ok) throw new Error(started.error);
return workflow.sleep("60s", {
output: {
...ctx.input,
agentRunId: started.data.run.id,
attempt: 1
}
});
}
const fetched = await gateway.call("gateway:agent_runs.get", {
runId: ctx.input.agentRunId
});
if (!fetched.ok) throw new Error(fetched.error);
const run = fetched.data.run;
if (run.status === "succeeded") {
return workflow.next("decide", {
agentRunId: run.id,
summary: run.result?.message
});
}
if (run.status === "failed" || run.status === "cancelled") {
return workflow.next("handle_failure", {
agentRunId: run.id,
status: run.status,
error: run.error
});
}
return workflow.sleep("60s", {
output: {
...ctx.input,
attempt: (ctx.input.attempt ?? 0) + 1
}
});
}Human Input Pauses
Workflows can also pause when a package-backed action asks for human input. Approval-shaped pauses appear in the existing approvals surfaces and can satisfy approval policy for the selected followup action. Generic requests use the broader user-input request API for values, choices, or explicit resume signals. In both cases, the run keeps the request, answer, and followup context with the workflow history instead of requiring the agent to invent its own out-of-band checkpoint.
Declared Branches
Branching should be visible in the saved graph, not hidden inside JavaScript. Add routes to a step, then return a route label from the JS result. When a step declares routes, it must return one of those route labels; otherwise the run fails instead of silently taking an invisible default path. The Workflows page can then render the real branch structure before a run starts. Prefer workflow.route(label, output) for new graph steps; the raw { route, output } shape remains supported.
{
"id": "review",
"kind": "wasm-js",
"source": "export function main(ctx, workflow, gateway) { return workflow.route(ctx.input.ok ? 'approved' : 'needs_changes', ctx.input); }",
"entrypoint": "main",
"routes": [
{ "label": "approved", "nextStepId": "publish" },
{ "label": "needs_changes", "nextStepId": "revise" }
]
}Example Workflow Shapes
Keep PRs current
A manual workflow run starts an agent that rebases branches, runs tests, and shows the branch update before pushing within the branch policy.
Fix red builds
A manual or GitHub/custom webhook event-triggered workflow starts an investigation with repo and CI context.
Poll remote readiness
A JS step checks an external condition, returns a durable wait when it is not ready, then resumes later without keeping a process alive.
Review comments loop
A manually run workflow reads review comments, proposes edits, and asks before writing.
Nightly report
A scheduled workflow produces a report artifact without write authority, with one run per scheduled slot.