@bastani/atomic 0.9.16-alpha.7 → 0.9.16-alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/dist/builtin/intercom/CHANGELOG.md +6 -0
- package/dist/builtin/intercom/README.md +4 -4
- package/dist/builtin/intercom/broker/broker.ts +617 -22
- package/dist/builtin/intercom/broker/client-message-validation.ts +9 -0
- package/dist/builtin/intercom/broker/client.ts +200 -10
- package/dist/builtin/intercom/broker/pending-send-registry.ts +4 -0
- package/dist/builtin/intercom/broker/send-handler.ts +90 -36
- package/dist/builtin/intercom/broker/send-signature.ts +20 -8
- package/dist/builtin/intercom/group.ts +85 -0
- package/dist/builtin/intercom/index.bundle.mjs +680 -53
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/intercom/session-target.ts +54 -0
- package/dist/builtin/intercom/skills/intercom/SKILL.md +18 -3
- package/dist/builtin/intercom/source-ownership.ts +31 -0
- package/dist/builtin/intercom/types.ts +115 -0
- package/dist/builtin/mcp/index.bundle.mjs +109 -35
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/src/extension/index.bundle.mjs +173 -51
- package/dist/builtin/web-access/index.bundle.mjs +109 -35
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +15 -0
- package/dist/builtin/workflows/README.md +40 -37
- package/dist/builtin/workflows/builtin/adversarial-verification.js +13 -0
- package/dist/builtin/workflows/builtin/{shared-prompts.ts → chunk-0x6e303p.js} +133 -154
- package/dist/builtin/workflows/builtin/chunk-29wrp38a.js +199 -0
- package/dist/builtin/workflows/builtin/chunk-2dqb5s2q.js +602 -0
- package/dist/builtin/workflows/builtin/chunk-4febxsv4.js +264 -0
- package/dist/builtin/workflows/builtin/chunk-5n10x7j2.js +69730 -0
- package/dist/builtin/workflows/builtin/chunk-5wgwscd0.js +4 -0
- package/dist/builtin/workflows/builtin/chunk-6fqs7c01.js +155 -0
- package/dist/builtin/workflows/builtin/chunk-7430zyas.js +126 -0
- package/dist/builtin/workflows/builtin/chunk-7at6dnkr.js +33 -0
- package/dist/builtin/workflows/builtin/chunk-82ha8p41.js +175 -0
- package/dist/builtin/workflows/builtin/chunk-bfkmzv9h.js +156 -0
- package/dist/builtin/workflows/builtin/chunk-c53y8bdh.js +345 -0
- package/dist/builtin/workflows/builtin/chunk-cdtd3m3w.js +529 -0
- package/dist/builtin/workflows/builtin/chunk-cg9tmks0.js +206 -0
- package/dist/builtin/workflows/builtin/chunk-hdpj1dkw.js +2447 -0
- package/dist/builtin/workflows/builtin/chunk-hzzn6adg.js +221 -0
- package/dist/builtin/workflows/builtin/chunk-nqr34qp3.js +1061 -0
- package/dist/builtin/workflows/builtin/chunk-qwzvgxnq.js +404 -0
- package/dist/builtin/workflows/builtin/chunk-qx2ptjs3.js +215 -0
- package/dist/builtin/workflows/builtin/chunk-v04c5qmd.js +1207 -0
- package/dist/builtin/workflows/builtin/chunk-wpckd35c.js +12 -0
- package/dist/builtin/workflows/builtin/chunk-x2ghn0je.js +4110 -0
- package/dist/builtin/workflows/builtin/chunk-zf28603f.js +1080 -0
- package/dist/builtin/workflows/builtin/classify-and-act.js +12 -0
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize.js +10 -0
- package/dist/builtin/workflows/builtin/generate-and-filter.js +12 -0
- package/dist/builtin/workflows/builtin/goal.js +16 -0
- package/dist/builtin/workflows/builtin/index.js +49 -0
- package/dist/builtin/workflows/builtin/loop-until-done.js +12 -0
- package/dist/builtin/workflows/builtin/open-claude-design.js +11 -0
- package/dist/builtin/workflows/builtin/ralph.js +16 -0
- package/dist/builtin/workflows/builtin/steering-context.d.ts +6 -0
- package/dist/builtin/workflows/builtin/steering-context.js +7 -0
- package/dist/builtin/workflows/builtin/tournament.js +13 -0
- package/dist/builtin/workflows/package.json +10 -7
- package/dist/builtin/workflows/src/authoring/typebox-defaults.d.ts +41 -0
- package/dist/builtin/workflows/src/extension/index.bundle.mjs +1228 -428
- package/dist/builtin/workflows/src/{index.bundle.mjs → index.js} +956 -427
- package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +403 -0
- package/dist/builtin/workflows/src/shared/authoring-contract-ui.d.ts +327 -0
- package/dist/builtin/workflows/src/shared/budget.d.ts +67 -0
- package/dist/builtin/workflows/src/shared/workflow-authoring-types.d.ts +60 -0
- package/dist/builtin/workflows/src/shared/workflow-heartbeat-contract.d.ts +21 -0
- package/dist/core/agent-session-custom-message-commit.d.ts.map +1 -1
- package/dist/core/agent-session-custom-message-commit.js +12 -5
- package/dist/core/agent-session-custom-message-commit.js.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.d.ts.map +1 -1
- package/dist/core/agent-session-persistent-custom-messages.js +2 -0
- package/dist/core/agent-session-persistent-custom-messages.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +2 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +2 -0
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -0
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/builtin-install-layout.d.ts +1 -1
- package/dist/core/builtin-install-layout.d.ts.map +1 -1
- package/dist/core/builtin-install-layout.js +1 -1
- package/dist/core/builtin-install-layout.js.map +1 -1
- package/dist/core/codex-fast-mode.d.ts +10 -7
- package/dist/core/codex-fast-mode.d.ts.map +1 -1
- package/dist/core/codex-fast-mode.js +31 -13
- package/dist/core/codex-fast-mode.js.map +1 -1
- package/dist/core/extensions/context-types.d.ts +35 -0
- package/dist/core/extensions/context-types.d.ts.map +1 -1
- package/dist/core/extensions/context-types.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
- package/dist/core/extensions/loader-virtual-modules.js +6 -1
- package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
- package/dist/core/model-runtime.d.ts +2 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +4 -0
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/runtime-credentials.d.ts +1 -0
- package/dist/core/runtime-credentials.d.ts.map +1 -1
- package/dist/core/runtime-credentials.js +8 -0
- package/dist/core/runtime-credentials.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +5 -5
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager-storage.d.ts +2 -0
- package/dist/core/session-manager-storage.d.ts.map +1 -1
- package/dist/core/session-manager-storage.js +4 -2
- package/dist/core/session-manager-storage.js.map +1 -1
- package/dist/core/slash-commands.js +1 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/workflow-stage-admission.d.ts +6 -1
- package/dist/core/workflow-stage-admission.d.ts.map +1 -1
- package/dist/core/workflow-stage-admission.js +8 -9
- package/dist/core/workflow-stage-admission.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-autocomplete.js +1 -1
- package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
- package/dist/modes/interactive/interactive-startup.js +1 -1
- package/dist/modes/interactive/interactive-startup.js.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.d.ts +4 -0
- package/dist/modes/interactive-engine/isolated-runtime.d.ts.map +1 -1
- package/dist/modes/interactive-engine/isolated-runtime.js +69 -10
- package/dist/modes/interactive-engine/isolated-runtime.js.map +1 -1
- package/dist/modes/rpc/rpc-client-api.d.ts +18 -2
- package/dist/modes/rpc/rpc-client-api.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client-api.js +33 -5
- package/dist/modes/rpc/rpc-client-api.js.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-command-handler.js +8 -4
- package/dist/modes/rpc/rpc-command-handler.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +13 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/intercom.md +15 -6
- package/docs/packages.md +2 -2
- package/docs/providers.md +8 -3
- package/docs/quickstart.md +2 -2
- package/docs/rpc.md +21 -4
- package/docs/settings.md +6 -4
- package/docs/subagents.md +1 -1
- package/docs/usage.md +1 -1
- package/docs/workflows.md +108 -239
- package/npm-shrinkwrap.json +32 -32
- package/package.json +9 -13
- package/dist/builtin/workflows/ambient.d.ts +0 -61
- package/dist/builtin/workflows/builtin/adversarial-verification-prompts.ts +0 -26
- package/dist/builtin/workflows/builtin/adversarial-verification-runner.ts +0 -391
- package/dist/builtin/workflows/builtin/adversarial-verification.ts +0 -34
- package/dist/builtin/workflows/builtin/classify-and-act-prompts.ts +0 -14
- package/dist/builtin/workflows/builtin/classify-and-act-runner.ts +0 -108
- package/dist/builtin/workflows/builtin/classify-and-act.ts +0 -40
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize-prompts.ts +0 -14
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize-runner.ts +0 -96
- package/dist/builtin/workflows/builtin/fan-out-and-synthesize.ts +0 -37
- package/dist/builtin/workflows/builtin/generate-and-filter-prompts.ts +0 -41
- package/dist/builtin/workflows/builtin/generate-and-filter-runner.ts +0 -90
- package/dist/builtin/workflows/builtin/generate-and-filter.ts +0 -31
- package/dist/builtin/workflows/builtin/goal-artifacts.ts +0 -64
- package/dist/builtin/workflows/builtin/goal-convergence.ts +0 -87
- package/dist/builtin/workflows/builtin/goal-ledger.ts +0 -139
- package/dist/builtin/workflows/builtin/goal-models.ts +0 -72
- package/dist/builtin/workflows/builtin/goal-orchestrator-prompts.ts +0 -94
- package/dist/builtin/workflows/builtin/goal-prompts.ts +0 -217
- package/dist/builtin/workflows/builtin/goal-reducer.ts +0 -176
- package/dist/builtin/workflows/builtin/goal-reports.ts +0 -76
- package/dist/builtin/workflows/builtin/goal-reverify.ts +0 -305
- package/dist/builtin/workflows/builtin/goal-review.ts +0 -139
- package/dist/builtin/workflows/builtin/goal-runner.ts +0 -504
- package/dist/builtin/workflows/builtin/goal-schemas.ts +0 -89
- package/dist/builtin/workflows/builtin/goal-types.ts +0 -164
- package/dist/builtin/workflows/builtin/goal.ts +0 -78
- package/dist/builtin/workflows/builtin/index.ts +0 -16
- package/dist/builtin/workflows/builtin/loop-until-done-prompts.ts +0 -87
- package/dist/builtin/workflows/builtin/loop-until-done-runner.ts +0 -282
- package/dist/builtin/workflows/builtin/loop-until-done.ts +0 -51
- package/dist/builtin/workflows/builtin/open-claude-design-live-protocol.ts +0 -305
- package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +0 -337
- package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +0 -347
- package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +0 -313
- package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +0 -311
- package/dist/builtin/workflows/builtin/open-claude-design.ts +0 -57
- package/dist/builtin/workflows/builtin/pattern-artifact-root.ts +0 -28
- package/dist/builtin/workflows/builtin/progress-scoring.ts +0 -230
- package/dist/builtin/workflows/builtin/ralph-core.ts +0 -449
- package/dist/builtin/workflows/builtin/ralph-forked-prompts.ts +0 -100
- package/dist/builtin/workflows/builtin/ralph-models.ts +0 -183
- package/dist/builtin/workflows/builtin/ralph-review-gate.ts +0 -103
- package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +0 -104
- package/dist/builtin/workflows/builtin/ralph-runner.ts +0 -459
- package/dist/builtin/workflows/builtin/ralph.ts +0 -82
- package/dist/builtin/workflows/builtin/review-convergence.ts +0 -229
- package/dist/builtin/workflows/builtin/selection-math.ts +0 -156
- package/dist/builtin/workflows/builtin/steering-context.ts +0 -51
- package/dist/builtin/workflows/builtin/tournament-prompts.ts +0 -70
- package/dist/builtin/workflows/builtin/tournament-runner.ts +0 -400
- package/dist/builtin/workflows/builtin/tournament.ts +0 -73
- package/dist/builtin/workflows/builtin/verification-criteria.ts +0 -330
- package/dist/builtin/workflows/builtin/verification-prompts.ts +0 -206
- package/dist/builtin/workflows/builtin/verification-usage.ts +0 -44
package/docs/workflows.md
CHANGED
|
@@ -36,7 +36,7 @@ Default to a workflow for non-trivial work with a verifiable objective — see [
|
|
|
36
36
|
- [Built-in Workflows](#built-in-workflows)
|
|
37
37
|
- [Writing a Workflow](#writing-a-workflow)
|
|
38
38
|
- [Scope-Guard Starter Pattern](#scope-guard-starter-pattern)
|
|
39
|
-
- [The `workflow()`
|
|
39
|
+
- [The `workflow()` definition](#the-workflow-definition)
|
|
40
40
|
- [WorkflowContext](#workflowcontext)
|
|
41
41
|
- [Task and Stage Options](#task-and-stage-options)
|
|
42
42
|
- [StageContext](#stagecontext)
|
|
@@ -51,10 +51,9 @@ Default to a workflow for non-trivial work with a verifiable objective — see [
|
|
|
51
51
|
- [Workflow Configuration](#workflow-configuration)
|
|
52
52
|
- [Settings](#settings)
|
|
53
53
|
- [Package Setup](#package-setup)
|
|
54
|
-
- [Programmatic
|
|
55
|
-
- [Fast
|
|
54
|
+
- [Programmatic usage](#programmatic-usage)
|
|
55
|
+
- [Fast inference for workflow stages](#fast-inference-for-workflow-stages)
|
|
56
56
|
- [Context Engineering](#context-engineering)
|
|
57
|
-
- [Migrating from the `defineWorkflow()` Builder API](#migrating-from-the-defineworkflow-builder-api)
|
|
58
57
|
- [Design Checklist](#design-checklist)
|
|
59
58
|
- [Common Mistakes](#common-mistakes)
|
|
60
59
|
- [Workflow Best Practices](#workflow-best-practices)
|
|
@@ -125,6 +124,26 @@ Workflow run identifiers are shown in full everywhere they are presented to user
|
|
|
125
124
|
|
|
126
125
|
Stage targeting is exact but not UUID-bound, because stage identifiers are not all bare UUIDs. A `stageId` resolves by exact stage id — a bare UUID at the root, the full `runId:stageId` composite for a stage inside a nested workflow, or `tool:<argsHash>` for a `ctx.tool` node — or by exact stage or tool name. Partial names no longer match, so `build` will not select `build-check`. Two stages that share an exact name are still reported as ambiguous, listing the full matching identifiers.
|
|
127
126
|
|
|
127
|
+
#### Intercom delivery to pending workflow stages
|
|
128
|
+
|
|
129
|
+
A known workflow stage whose session has not initialized is still addressable by the workflow run's full UUID and its exact authored stage key. From a sibling session in the same workflow Intercom group, use ordinary Intercom delivery:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
intercom({
|
|
133
|
+
action: "send",
|
|
134
|
+
to: "<runId>:reviewer",
|
|
135
|
+
message: "Scope changed: raw amendment text is now part of the oracle."
|
|
136
|
+
})
|
|
137
|
+
// queued — distinct from live-session delivered
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Send material updates through Intercom to every affected workflow stage, including stages that have not started. Atomic queues messages only for known pending stages and delivers them when their sessions initialize, before their first model turn. Live stage delivery is immediate. Use `ask` once the stage session is live and can reply. Unknown run/stage identities retain the ordinary unknown-target failure.
|
|
141
|
+
|
|
142
|
+
The workflows extension persists pending messages with run state across resume/replay and broker restart. Each exact run/stage key accepts 50 queued messages; the next send is refused without eviction. Only sessions in the run's Intercom group may queue them. When the stage session initializes, Atomic delivers its messages FIFO through the ordinary inbound Intercom path **before the first model turn**. The transcript labels them **Messages received before you started**, preserves sender identity and `Sent:` timestamps, and keeps them separate from the stage task prompt. Duplicate logical message IDs and stage-attempt restarts do not redeliver a message.
|
|
143
|
+
|
|
144
|
+
If the destination is skipped, the run is cancelled, or the stage becomes terminal before its session initializes, Atomic marks queued messages undeliverable rather than dropping them. Senders whose messages requested acknowledgment receive a correlated failure notification. Running and completed stages continue through their existing live, late, and post-mortem routes.
|
|
145
|
+
|
|
146
|
+
|
|
128
147
|
At 80 columns and wider, each `BACKGROUND` card uses two rows so the id is not squeezed beside the workflow name: the first row contains the status glyph and full UUID, and the second contains the workflow name followed by its mode, progress, live-tool total when more than one is active, pending/running `ctx.tool` node names and statuses as space permits, and elapsed/status metadata. Tool nodes are read-only durable graph nodes, not attachable stage chats. The panel renders every qualifying top-level run, so each card is two rows high (plus the existing spacing between cards). Below 80 columns, the panel keeps its collapsed form, omits ids and tool names, and includes a live-tool count when one or more tool nodes are pending or running.
|
|
129
148
|
|
|
130
149
|
For chat surfaces such as workflow status, run detail, dispatch confirmation, and the run picker, a full id wraps onto continuation rows when the card is narrower than the id. The renderer never ellipsizes the id and keeps the card border closed at its minimum layout width, while terminals below that floor — including sub-30-column terminals — can hard-clip the box. An awaiting-input attribution banner is titled `AWAITING INPUT` and contains the same two identity rows — `?` plus the full run id, then the workflow name and optional metadata — while the existing prompt question and options remain below it in the normal prompt UI.
|
|
@@ -148,7 +167,7 @@ Below the breakpoint the same run set is represented by the collapsed count line
|
|
|
148
167
|
Workflow files are plain TypeScript modules. Create `.atomic/workflows/explain-file.ts`:
|
|
149
168
|
|
|
150
169
|
```ts
|
|
151
|
-
import { workflow } from "@bastani/workflows";
|
|
170
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
152
171
|
import { Type } from "typebox";
|
|
153
172
|
|
|
154
173
|
export default workflow({
|
|
@@ -270,7 +289,7 @@ Ask these questions in order and stop at the cheapest shape that satisfies every
|
|
|
270
289
|
8. **Is it only specialist evidence-gathering?** If the parent keeps control, no completion gate is needed, and the work is bounded (a debug pass, a parallel research fanout, one noisy investigation), inline subagents are enough—and cheaper than a workflow.
|
|
271
290
|
9. **Is it truly tiny?** Deterministic, low-risk, single-file/no-test/no-review—answer or edit inline and stop.
|
|
272
291
|
|
|
273
|
-
A first named workflow launch commits the selected execution shape for the turn. For one task, end the turn after that launch. For an independent queue, the selected shape is a bounded launch wave: issue every planned per-item top-level launch up to the concurrency bound before ending the turn. Do not casually chain unplanned unrelated top-level workflow launches. When one task needs multiple workflow capabilities or dependent items need ordered handoffs, design composition **before** launch: author one custom parent, import project/package definitions or builtins from `@bastani/workflows/builtin`, and call `ctx.workflow(...)`. Nested children preserve their stages and guarantees within the expanded graph up to `maxDepth`, but they remain under the parent's root lifecycle and failure boundary.
|
|
292
|
+
A first named workflow launch commits the selected execution shape for the turn. For one task, end the turn after that launch. For an independent queue, the selected shape is a bounded launch wave: issue every planned per-item top-level launch up to the concurrency bound before ending the turn. Do not casually chain unplanned unrelated top-level workflow launches. When one task needs multiple workflow capabilities or dependent items need ordered handoffs, design composition **before** launch: author one custom parent, import project/package definitions or builtins from `@bastani/atomic/workflows/builtin`, and call `ctx.workflow(...)`. Nested children preserve their stages and guarantees within the expanded graph up to `maxDepth`, but they remain under the parent's root lifecycle and failure boundary.
|
|
274
293
|
|
|
275
294
|
Choose the cheapest complete graph. Routing cues are not a reason to add decorative stages: avoid duplicated research and review loops. Before launch, state the selected graph, why one broad builtin is sufficient or insufficient, the evidence each major stage produces, and the stop/repair conditions. A simple direct match can be one sentence; a composed graph should briefly name its children and task-specific gates.
|
|
276
295
|
|
|
@@ -388,7 +407,7 @@ A natural-language request for a worktree does not configure runner isolation. I
|
|
|
388
407
|
```ts
|
|
389
408
|
// .atomic/workflows/issue-to-pr.ts
|
|
390
409
|
import { spawnSync } from "node:child_process";
|
|
391
|
-
import { workflow } from "@bastani/workflows";
|
|
410
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
392
411
|
import { Type, type Static } from "typebox";
|
|
393
412
|
|
|
394
413
|
const reviewDecision = Type.Object(
|
|
@@ -636,7 +655,7 @@ So every builtin stage prompt carries a **steering propagation contract**:
|
|
|
636
655
|
Every bundled workflow wraps its run context once at the definition entry point, so each `ctx.task`, `ctx.chain`, and `ctx.parallel` prompt carries the contract automatically. Do the same in a custom workflow:
|
|
637
656
|
|
|
638
657
|
```ts
|
|
639
|
-
import { withSteeringPropagationContext } from "@bastani/workflows/builtin/steering-context";
|
|
658
|
+
import { withSteeringPropagationContext } from "@bastani/atomic/workflows/builtin/steering-context";
|
|
640
659
|
|
|
641
660
|
export default workflow({
|
|
642
661
|
name: "my-workflow",
|
|
@@ -671,7 +690,7 @@ A long-running stage gets compacted, and compaction ranks lines individually rat
|
|
|
671
690
|
Wrap contract text in `keepContext` so it survives verbatim regardless of the compression ratio:
|
|
672
691
|
|
|
673
692
|
```ts
|
|
674
|
-
import { keepContext, workflow } from "@bastani/workflows";
|
|
693
|
+
import { keepContext, workflow } from "@bastani/atomic/workflows";
|
|
675
694
|
|
|
676
695
|
const prompt = [
|
|
677
696
|
keepContext("Research only. Do not implement code changes."),
|
|
@@ -712,7 +731,7 @@ Note what is tagged and what is not: the constraint and the criteria are protect
|
|
|
712
731
|
**Steering.** A `send` amendment is authoritative and stages must carry it forward, but it is one short message arriving late into an already-long session, competing against the entire transcript for retention. Tagging it keeps it alive until the stage acts on it:
|
|
713
732
|
|
|
714
733
|
```
|
|
715
|
-
|
|
734
|
+
intercom({ action: "send", to: `${runId}:<stageKey>`, message:
|
|
716
735
|
"<keepContext>\nNew requirement: the fix must not change the public API.\n</keepContext>" })
|
|
717
736
|
```
|
|
718
737
|
|
|
@@ -745,7 +764,7 @@ Across these builtins, model-facing stages use compact, outcome-first contracts
|
|
|
745
764
|
|
|
746
765
|
### Six composable pattern builtins
|
|
747
766
|
|
|
748
|
-
The six common patterns are full definitions exported from `@bastani/workflows/builtin`:
|
|
767
|
+
The six common patterns are full definitions exported from `@bastani/atomic/workflows/builtin`:
|
|
749
768
|
|
|
750
769
|
| Workflow | Required input | Bounded/defaulted knobs | Principal declared outputs |
|
|
751
770
|
|---|---|---|---|
|
|
@@ -766,7 +785,7 @@ import {
|
|
|
766
785
|
loopUntilDone,
|
|
767
786
|
ralph,
|
|
768
787
|
tournament,
|
|
769
|
-
} from "@bastani/workflows/builtin";
|
|
788
|
+
} from "@bastani/atomic/workflows/builtin";
|
|
770
789
|
|
|
771
790
|
const research = await ctx.workflow(fanOutAndSynthesize, {
|
|
772
791
|
inputs: {
|
|
@@ -883,7 +902,7 @@ If required inputs are missing or ambiguous, Atomic asks for them or opens the i
|
|
|
883
902
|
Workflow files are TypeScript modules that export a workflow definition:
|
|
884
903
|
|
|
885
904
|
```ts
|
|
886
|
-
import { workflow } from "@bastani/workflows";
|
|
905
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
887
906
|
import { Type } from "typebox";
|
|
888
907
|
|
|
889
908
|
export default workflow({
|
|
@@ -958,7 +977,6 @@ Authoring basics:
|
|
|
958
977
|
- `outputs` declares typed outputs that parent workflows receive from `ctx.workflow(childWorkflow, ...)`.
|
|
959
978
|
- `run: async (ctx) => { ... }` defines the workflow body.
|
|
960
979
|
|
|
961
|
-
To migrate an existing file from the removed `defineWorkflow(...).compile()` builder, see [Migrating from the `defineWorkflow()` Builder API](#migrating-from-the-defineworkflow-builder-api) for the full method-to-key mapping, a before/after walkthrough, and a conversion checklist.
|
|
962
980
|
|
|
963
981
|
`prompt` and `task` are aliases for task text inside authored workflow primitives. Prefer `prompt` because it mirrors lower-level `stage.prompt(...)`; `task` remains useful in `ctx.chain(...)` examples.
|
|
964
982
|
|
|
@@ -1061,7 +1079,7 @@ Protect a stage's role constraints, acceptance criteria, and prohibitions with `
|
|
|
1061
1079
|
|
|
1062
1080
|
### Inputs
|
|
1063
1081
|
|
|
1064
|
-
Inputs are declared with TypeBox `Type.*` schemas in the `inputs` object. Import `Type` from `typebox` directly in workflow files. Workflow packages still declare `typebox` as a peer dependency so TypeBox schemas resolve under `tsc` — see [Programmatic
|
|
1082
|
+
Inputs are declared with TypeBox `Type.*` schemas in the `inputs` object. Import `Type` from `typebox` directly in workflow files. Workflow packages still declare `typebox` as a peer dependency so TypeBox schemas resolve under `tsc` — see [Programmatic usage](#programmatic-usage). Common input schemas map to picker kinds and accepted runtime values:
|
|
1065
1083
|
|
|
1066
1084
|
| TypeBox schema | Picker kind | Accepted runtime value |
|
|
1067
1085
|
|---|---|---|
|
|
@@ -1172,7 +1190,7 @@ The same rule applies to inputs: `inputs: { counts: Type.Array(Type.Number()) }`
|
|
|
1172
1190
|
When you already have a precise TypeScript type for a deeply-nested serializable value and don't want to hand-write the equivalent TypeBox schema, wrap a permissive runtime schema with `Type.Unsafe<MyType>(...)`. The **static** type becomes exactly `MyType` (so `ctx.inputs`, the `run` return, and `child.outputs` stay precise), while the **runtime** check stays as lenient as the wrapped schema. Use a `type` alias rather than an `interface` for the wrapped type — an `interface` has no implicit index signature, so it does not satisfy the serializable-output constraint:
|
|
1173
1191
|
|
|
1174
1192
|
```ts
|
|
1175
|
-
import { workflow } from "@bastani/workflows";
|
|
1193
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
1176
1194
|
import { Type } from "typebox";
|
|
1177
1195
|
|
|
1178
1196
|
type ResearchPacket = {
|
|
@@ -1209,7 +1227,7 @@ Tradeoff: `Type.Unsafe<T>()` does not deeply validate at runtime — it trusts t
|
|
|
1209
1227
|
- TypeScript checks the `run` return against your declared outputs at **compile time** (a missing required output or wrong value type is a TypeScript error), and TypeBox `Value` checks it at **runtime** (rejecting undeclared keys and enforcing the declared shape recursively).
|
|
1210
1228
|
- `ctx.workflow(child)` returns a discriminated child result. When `child.exited === false`, `child.outputs` is the child's full declared `outputs` contract; when `child.exited === true`, `child.outputs` is `Partial<TOutputs>` because child `ctx.exit({ outputs })` may intentionally provide only a subset.
|
|
1211
1229
|
|
|
1212
|
-
Use `Static<typeof schema>` (both `Static` and `TSchema` are re-exported from `@bastani/workflows`) when you need the inferred TypeScript type of a schema directly — for example to type a helper that builds an output value.
|
|
1230
|
+
Use `Static<typeof schema>` (both `Static` and `TSchema` are re-exported from `@bastani/atomic/workflows`) when you need the inferred TypeScript type of a schema directly — for example to type a helper that builds an output value.
|
|
1213
1231
|
|
|
1214
1232
|
### Stage follow-on user messages
|
|
1215
1233
|
|
|
@@ -1333,7 +1351,7 @@ User-defined workflows are ordinary TypeScript modules. Import the workflow defi
|
|
|
1333
1351
|
|
|
1334
1352
|
```ts
|
|
1335
1353
|
// .atomic/workflows/shared-research.ts
|
|
1336
|
-
import { workflow } from "@bastani/workflows";
|
|
1354
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
1337
1355
|
import { Type } from "typebox";
|
|
1338
1356
|
|
|
1339
1357
|
export default workflow({
|
|
@@ -1353,7 +1371,7 @@ export default workflow({
|
|
|
1353
1371
|
});
|
|
1354
1372
|
|
|
1355
1373
|
// .atomic/workflows/research-and-synthesize.ts
|
|
1356
|
-
import { workflow } from "@bastani/workflows";
|
|
1374
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
1357
1375
|
import { Type } from "typebox";
|
|
1358
1376
|
import sharedResearch from "./shared-research.js";
|
|
1359
1377
|
|
|
@@ -1399,22 +1417,22 @@ import {
|
|
|
1399
1417
|
openClaudeDesign,
|
|
1400
1418
|
ralph,
|
|
1401
1419
|
tournament,
|
|
1402
|
-
} from "@bastani/workflows/builtin";
|
|
1420
|
+
} from "@bastani/atomic/workflows/builtin";
|
|
1403
1421
|
```
|
|
1404
1422
|
|
|
1405
1423
|
Or import one individual module:
|
|
1406
1424
|
|
|
1407
1425
|
```ts
|
|
1408
|
-
import goal from "@bastani/workflows/builtin/goal";
|
|
1409
|
-
import ralph from "@bastani/workflows/builtin/ralph";
|
|
1426
|
+
import goal from "@bastani/atomic/workflows/builtin/goal";
|
|
1427
|
+
import ralph from "@bastani/atomic/workflows/builtin/ralph";
|
|
1410
1428
|
```
|
|
1411
1429
|
|
|
1412
1430
|
Example parent that maps a repository and verifies the synthesis:
|
|
1413
1431
|
|
|
1414
1432
|
```ts
|
|
1415
|
-
import { workflow } from "@bastani/workflows";
|
|
1433
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
1416
1434
|
import { Type } from "typebox";
|
|
1417
|
-
import { adversarialVerification, fanOutAndSynthesize } from "@bastani/workflows/builtin";
|
|
1435
|
+
import { adversarialVerification, fanOutAndSynthesize } from "@bastani/atomic/workflows/builtin";
|
|
1418
1436
|
|
|
1419
1437
|
export default workflow({
|
|
1420
1438
|
name: "research-and-verify",
|
|
@@ -1574,7 +1592,7 @@ Use a fresh task when one check at a material boundary is enough. This complete
|
|
|
1574
1592
|
|
|
1575
1593
|
```ts
|
|
1576
1594
|
// .atomic/workflows/scope-guard-boundary.ts
|
|
1577
|
-
import { workflow } from "@bastani/workflows";
|
|
1595
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
1578
1596
|
import { Type, type Static } from "typebox";
|
|
1579
1597
|
|
|
1580
1598
|
const decisionLogSchema = Type.Object(
|
|
@@ -1684,7 +1702,7 @@ Use `ctx.stage(...)` when one independent checker needs a retained conversation.
|
|
|
1684
1702
|
|
|
1685
1703
|
```ts
|
|
1686
1704
|
// .atomic/workflows/scope-guard-retained.ts
|
|
1687
|
-
import { workflow } from "@bastani/workflows";
|
|
1705
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
1688
1706
|
import { Type } from "typebox";
|
|
1689
1707
|
|
|
1690
1708
|
function continueWorker(sessionFile: string | undefined) {
|
|
@@ -1759,7 +1777,7 @@ Use a live peer only when steering during generation adds clear value. Both bran
|
|
|
1759
1777
|
|
|
1760
1778
|
```ts
|
|
1761
1779
|
// .atomic/workflows/scope-guard-live.ts
|
|
1762
|
-
import { workflow } from "@bastani/workflows";
|
|
1780
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
1763
1781
|
import { Type, type Static } from "typebox";
|
|
1764
1782
|
|
|
1765
1783
|
const coordinationSchema = Type.Object(
|
|
@@ -1906,9 +1924,9 @@ export default workflow({
|
|
|
1906
1924
|
|
|
1907
1925
|
The parallel fan-out has one shared parent frontier and downstream persistence waits for both branches. Blocking asks use the guard's retained conversation; the fresh persistence task turns the final transcript into the bounded artifact before correctness review. If Intercom is unavailable, `warn` runs that task as a boundary check, `block` requires `ctx.ui`, and `off` records that no guard approval exists.
|
|
1908
1926
|
|
|
1909
|
-
## The `workflow()`
|
|
1927
|
+
## The `workflow()` definition
|
|
1910
1928
|
|
|
1911
|
-
`workflow(spec)`
|
|
1929
|
+
Use `workflow(spec)` to author a workflow. It validates the schema maps, normalizes or infers the name, and returns a frozen `WorkflowDefinition` for export, discovery, and `ctx.workflow(...)` composition.
|
|
1912
1930
|
|
|
1913
1931
|
```typescript
|
|
1914
1932
|
function workflow<
|
|
@@ -1978,6 +1996,19 @@ A recoverable provider or rate-limit block is not the terminal `blocked` status:
|
|
|
1978
1996
|
|
|
1979
1997
|
A heartbeat the host has already accepted into the parent's queue is beyond that pass, because nothing withdraws a queued message. It is invalidated instead at the moment the parent reads it: the typed card's exact `runId + scheduledAt` identity must still be pending for a current nonterminal run. If the run has since reached a terminal state, this process no longer knows that run, or a durable resume has reused the run id with a later pending boundary, the old heartbeat is excluded from the model's context and cannot steer the parent. That covers all ways a stale card survives — one parked through a long turn while its run finished, one recovered from a previous process at startup, and one admitted before a same-ID durable resume. The card already rendered in your transcript is deliberately left alone: it is a true record that the heartbeat was raised, and rewriting scrollback after the fact would be worse than leaving it. Only the model-facing steer is invalidated.
|
|
1980
1998
|
|
|
1999
|
+
### `budget`
|
|
2000
|
+
|
|
2001
|
+
```typescript
|
|
2002
|
+
readonly budget?: {
|
|
2003
|
+
readonly maxDurationMs?: number;
|
|
2004
|
+
readonly maxTokens?: number;
|
|
2005
|
+
readonly maxCost?: number;
|
|
2006
|
+
readonly warnAtPercent?: number;
|
|
2007
|
+
};
|
|
2008
|
+
```
|
|
2009
|
+
|
|
2010
|
+
The optional budget sets duration, token, and cost limits for this workflow. Atomic freezes the declaration into the compiled definition and resolves each field over the extension default when the workflow runs. See [Run budgets](#run-budgets) for precedence and validation rules.
|
|
2011
|
+
|
|
1981
2012
|
### `inputs`
|
|
1982
2013
|
|
|
1983
2014
|
```typescript
|
|
@@ -2046,25 +2077,23 @@ interface WorkflowDefinition<
|
|
|
2046
2077
|
TInputs extends WorkflowInputValues = WorkflowInputValues,
|
|
2047
2078
|
TOutputs extends WorkflowOutputValues = WorkflowOutputValues,
|
|
2048
2079
|
TRunInputs extends WorkflowInputValues = TInputs,
|
|
2049
|
-
TDefinitionBrand extends object = {},
|
|
2050
2080
|
> {
|
|
2051
|
-
readonly __piWorkflow: true;
|
|
2052
|
-
readonly __runInputs?: TRunInputs;
|
|
2053
2081
|
readonly name: string;
|
|
2054
2082
|
readonly normalizedName: string;
|
|
2055
2083
|
readonly description: string;
|
|
2056
2084
|
readonly autoAttach?: true;
|
|
2057
2085
|
readonly heartbeatIntervalMinutes: number;
|
|
2086
|
+
readonly budget?: WorkflowBudget;
|
|
2058
2087
|
readonly inputs: WorkflowInputSchemaMap;
|
|
2059
2088
|
readonly outputs?: WorkflowOutputSchemaMap;
|
|
2060
2089
|
readonly inputBindings?: { readonly worktree?: WorkflowWorktreeInputBinding };
|
|
2061
|
-
run(
|
|
2062
|
-
ctx: WorkflowRunContext<TInputs, TDefinitionBrand, TOutputs>,
|
|
2063
|
-
): Promise<TOutputs> | TOutputs;
|
|
2090
|
+
run(ctx: WorkflowRunContext<TInputs, TOutputs>): Promise<TOutputs> | TOutputs;
|
|
2064
2091
|
}
|
|
2065
2092
|
```
|
|
2066
2093
|
|
|
2067
|
-
`
|
|
2094
|
+
`TRunInputs` describes the validated inputs accepted by `run(...)` and `ctx.workflow(...)`; it defaults to the workflow's resolved input values.
|
|
2095
|
+
|
|
2096
|
+
`workflow({...})` returns a `WorkflowDefinition` with the resolved name, normalized lookup name, description, runtime defaults, optional budget, schema maps, optional worktree input binding, and `run` function. Authors provide the fields documented above, and Atomic fills the normalized and defaulted values.
|
|
2068
2097
|
|
|
2069
2098
|
## WorkflowContext
|
|
2070
2099
|
|
|
@@ -2150,7 +2179,7 @@ ctx.workflow<
|
|
|
2150
2179
|
TChildOutputs extends WorkflowOutputValues,
|
|
2151
2180
|
TChildRunInputs extends WorkflowInputValues = TChildInputs,
|
|
2152
2181
|
>(
|
|
2153
|
-
definition: WorkflowDefinition<TChildInputs, TChildOutputs, TChildRunInputs
|
|
2182
|
+
definition: WorkflowDefinition<TChildInputs, TChildOutputs, TChildRunInputs>,
|
|
2154
2183
|
...args: WorkflowRunChildArgs<TChildRunInputs>
|
|
2155
2184
|
): Promise<WorkflowChildResult<TChildOutputs>>;
|
|
2156
2185
|
|
|
@@ -2180,7 +2209,7 @@ const child = await ctx.workflow(sharedResearch, {
|
|
|
2180
2209
|
});
|
|
2181
2210
|
```
|
|
2182
2211
|
|
|
2183
|
-
|
|
2212
|
+
Pass a definition returned by `workflow({...})`. See [Workflow Composition](#workflow-composition) for graph flattening, replay, failure, and parent-exit behavior, and [`WorkflowChildResult`](#workflowchildresult) for the discriminated result.
|
|
2184
2213
|
|
|
2185
2214
|
### `ctx.stage(name, options?)`
|
|
2186
2215
|
|
|
@@ -3000,7 +3029,7 @@ The workflow tool action surface is:
|
|
|
3000
3029
|
- discovery: `list`, `get`, `inputs`, plus `models` for the configured model catalog
|
|
3001
3030
|
- execution: named `run` with validated `workflow` and `inputs`
|
|
3002
3031
|
- inspection: `status`, `stages`, `stage`, `transcript`
|
|
3003
|
-
-
|
|
3032
|
+
- prompt response: `answer`; run control: `pause`, `interrupt`, `quit`, `resume`; free-form stage communication: ordinary Intercom `send`/`ask` to exact `<runId>:<stageKey>` targets
|
|
3004
3033
|
- rediscovery: `reload`
|
|
3005
3034
|
|
|
3006
3035
|
Every registered `workflow` tool call has one hard two-minute wall-clock deadline at the shared public tool boundary. The deadline covers request handling through the returned result; for background `run` and `resume`, it therefore covers startup/resume admission and acknowledgement only, not the workflow execution that continues after acknowledgement. A deadline returns one structured result:
|
|
@@ -3016,9 +3045,9 @@ Every registered `workflow` tool call has one hard two-minute wall-clock deadlin
|
|
|
3016
3045
|
}
|
|
3017
3046
|
```
|
|
3018
3047
|
|
|
3019
|
-
Expiry aborts the request operation signal so work that supports cancellation can stop, discards any later success or error, and never retries the action. The interactive engine remains available for the next command. For mutating actions (`reload`, `run`, `
|
|
3048
|
+
Expiry aborts the request operation signal so work that supports cancellation can stop, discards any later success or error, and never retries the action. The interactive engine remains available for the next command. For mutating actions (`reload`, `run`, `answer`, `pause`, `resume`, `interrupt`, and `quit`), the error additionally says that the outcome is unknown and instructs you to inspect workflow status before retrying; a timeout never claims that a mutation succeeded. When a timed-out `run` has already allocated its detached run, the structured result includes that exact full `runId`; inspect `status` with that id before any retry. A timeout before run allocation has no `runId`. Read-only actions (`models`, `list`, `get`, `inputs`, `status`, `stages`, `stage`, and `transcript`) omit that unknown-state guidance.
|
|
3020
3049
|
|
|
3021
|
-
From interactive chat, named workflow launches run in the background so the parent chat stays available. Run `/workflow connect <run>` to see agents working and chat with and steer each stage. Inspection and control calls (`status`, `stages`, `stage`, `transcript`, `
|
|
3050
|
+
From interactive chat, named workflow launches run in the background so the parent chat stays available. Run `/workflow connect <run>` to see agents working and chat with and steer each stage. Inspection, prompt-response, and control calls (`status`, `stages`, `stage`, `transcript`, `answer`, `pause`, `resume`, `interrupt`, `quit`) remain available while work runs.
|
|
3022
3051
|
|
|
3023
3052
|
`workflow({ action: "models" })` returns the registry's configured-auth catalog snapshot in registry order. Each entry includes `provider`, `id`, `fullId`, an `isCurrent` marker, and `availableThinkingLevels` derived from the real model's `reasoning` and `thinkingLevelMap` metadata. This is not proof of credentials, entitlements, OAuth freshness, or live provider access, and it exposes no authentication details.
|
|
3024
3053
|
|
|
@@ -3129,10 +3158,10 @@ workflow({ action: "transcript", runId: "<full-run-uuid>", stageId: "review" })
|
|
|
3129
3158
|
workflow({ action: "transcript", runId: "<full-run-uuid>", stageId: "review", tail: 40 })
|
|
3130
3159
|
workflow({ action: "transcript", runId: "<full-run-uuid>", stageId: "review", limit: 20, includeToolOutput: true })
|
|
3131
3160
|
|
|
3132
|
-
//
|
|
3133
|
-
|
|
3134
|
-
workflow({ action: "
|
|
3135
|
-
workflow({ action: "
|
|
3161
|
+
// Free-form stage communication uses Intercom; prompt responses use workflow answer.
|
|
3162
|
+
intercom({ action: "send", to: "<full-run-uuid>:review", message: "please focus on tests" })
|
|
3163
|
+
workflow({ action: "answer", runId: "<full-run-uuid>", stageId: "approval", promptId: "prompt-1", response: true })
|
|
3164
|
+
workflow({ action: "resume", runId: "<full-run-uuid>", stageId: "review", message: "continue with tests" })
|
|
3136
3165
|
|
|
3137
3166
|
workflow({ action: "pause", runId: "<full-run-uuid>" })
|
|
3138
3167
|
workflow({ action: "pause", runId: "<full-run-uuid>", stageId: "review" })
|
|
@@ -3157,25 +3186,15 @@ Control behavior:
|
|
|
3157
3186
|
|
|
3158
3187
|
- `runId` requires the full 36-character run UUID for every lifecycle and inspection action, including `status`. User-facing status surfaces print that exact value, so pass it back verbatim; typed prefixes are rejected with a distinct `Run id must be a full 36-character UUID` diagnostic rather than resolved. Because ids are matched exactly and are unique, no run target is ambiguous. Status lists and run pickers show top-level user-launched workflows; nested child runs are implementation details of the expanded parent graph.
|
|
3159
3188
|
- `status`, `stages`, `stage`, and `transcript` with an explicit full `runId` first use the current session store, then perform one exact DBOS hydration when that id is absent locally. This is inspection only: Atomic does not claim ownership, change status, run workflow code, or resume the workflow. A stale durable `running` root is shown as `crashed` with its resumability and an explicit `/workflow resume <id>` hint; fresh work owned by another Atomic process remains `running`, offers read-only status guidance, and stays protected from local control or resume. Deleted/tombstoned, absent, malformed, cyclic, orphaned, nonreciprocal, out-of-scope, and duplicate-node records report distinct failures instead of inventing a partial graph. `status` without `runId` remains current-session-only and never scans durable history.
|
|
3160
|
-
- `status` without `runId` lists every top-level run in the session with a concise per-run summary: the full run id, workflow name, run status, started/ended timing with pause-adjusted elapsed time, currently active stages, and awaiting-input details (count plus the stage, prompt id, kind, and message for each pending human prompt). In-flight runs are listed first. The summaries carry the exact identifiers that `pause
|
|
3189
|
+
- `status` without `runId` lists every top-level run in the session with a concise per-run summary: the full run id, workflow name, run status, started/ended timing with pause-adjusted elapsed time, currently active stages, and awaiting-input details (count plus the stage, prompt id, kind, and message for each pending human prompt). In-flight runs are listed first. The summaries carry the exact identifiers that `answer`, `pause`, `resume`, `interrupt`, and `quit` accept, so an orchestrating agent can list runs and act on them directly.
|
|
3161
3190
|
- `statusFilter` narrows the `status` run listing: run statuses (`pending`, `running`, `paused`, `blocked`, `completed`, `failed`, `skipped`, `cancelled`, `killed`) match runs directly, `awaiting_input` selects runs with at least one stage awaiting input or pending human prompt, and `all` (the default) includes everything.
|
|
3162
3191
|
- `format: "json"` on data-bearing inspection actions (`status`, `stages`, `stage`, `transcript`) returns the full structured result; the default text output for `status` is the concise per-run summary list.
|
|
3163
3192
|
- `status` / `status <runId>` show terminal `ctx.exit(...)` statuses (`completed`, `skipped`, `cancelled`, or `blocked`) and the optional exit reason when one was supplied.
|
|
3164
3193
|
- `stages` lists stage summaries, including flattened stages from nested `ctx.workflow(...)` imports and `sessionFile`/`transcriptPath` when a stage has a persisted session. Use `statusFilter: "all"` to include completed, failed, skipped, and pending stages.
|
|
3165
3194
|
- `stage` returns details for one stage by exact stage id or exact stage name, including nested child stages shown in the expanded graph and the persisted `sessionFile` when available. User-facing graph and control messages print full stage IDs; pass one back verbatim, or use the stage's exact name. Prefixes and partial names no longer resolve. Two stages sharing an exact name return an ambiguity diagnostic rather than selecting one.
|
|
3166
3195
|
- `transcript` is reference-first with a small preview by default: it returns metadata, transcript paths, and up to 5 recent entries. For targeted lookup, quote the exact `sessionFile`/`transcriptPath` value without changing platform separators (preserve Windows backslashes), search it with `rg` or `grep`, then read only small surrounding ranges. Text results include JSON-escaped `sessionFileJson`/`transcriptPathJson` lines for copy-safe path literals. Pass explicit `tail` or `limit` to override the 5-entry preview; `tail` overrides `limit`; `includeToolOutput` includes captured snapshot tool output in snapshot transcript results.
|
|
3167
|
-
- `
|
|
3168
|
-
|
|
3169
|
-
- Atomic checks an already-terminal root before stage resolution, nested-owner routing, prompt inspection, retained-session probing or revival, handle lookup, message admission, and delivery selection. That rejection creates no agent session or handle, appends no transcript, starts no model/tool/file work, answers no input, and mutates no workflow/stage snapshot. Missing or malformed retained sessions receive the same root-terminal error without being probed.
|
|
3170
|
-
- Atomic checks the same shared terminal authority again at the final synchronous SDK message-admission boundary. If a live root terminates while retained-session creation is pending, the send fails with `WORKFLOW_TERMINAL`, disposes its unclaimed provisional session/handle, and admits no prompt, model request, tool/file work, transcript append, or workflow-state mutation. A user-driven attach or Intercom claim remains independent and keeps the retained handle.
|
|
3171
|
-
- Prompt answers on a nonterminal root can include `promptId` and can carry answer content in `response`, `text`, or `message`; structured UI prompts usually prefer `response`.
|
|
3172
|
-
- Primitive HIL answers are normalized by prompt kind: `input` and `editor` require a text string; `confirm` accepts booleans or trimmed, case-insensitive `true`/`false`, `yes`/`y`, `no`/`n`, `approve`/`reject`, and `confirm`/`deny`; `select` accepts a trimmed, case-insensitive choice label or a 1-based numeric index.
|
|
3173
|
-
- An answer that does not match the pending primitive prompt stays pending. Atomic returns a `noop` explaining the expected shape and, for `select`, the available choices; it never silently chooses the first option. Prompt-card answers from the interactive graph keep their existing typed path.
|
|
3174
|
-
- For a live idle, non-paused stage, `prompt`, `followUp`, and eligible `auto` delivery all start a fresh prompt immediately; an actively streaming `followUp` remains queued and `steer` remains steering, so neither starts a concurrent prompt. During controlled pause, every context-bearing delivery remains held instead. The result's `delivery` and message describe the action actually taken (`prompt`, `followUp`, `steer`, `answer`, or `resume`), not merely the requested mode. Explicit `resume` against a stage that is not paused is a truthful no-op, and explicit message deliveries cannot bypass a paused stage; resume it first.
|
|
3175
|
-
- Delivery timing is mode-specific and deterministic. `steer` (and `auto` against a streaming stage) enters the steering queue and is consumed after the current assistant response finishes its whole tool batch, before the next model request — never between two tool calls of the same response. `followUp` enters the follow-up queue and is consumed only when the agent would otherwise stop. Sequential sends keep submission order *within* the queue they select; there is no global FIFO across the two queues, so a steer submitted after a follow-up is still consumed first. Ordering is promised relative to admission into the selected queue, not relative to when a caller started a request whose session setup or admission finishes later.
|
|
3176
|
-
- While the root remains nonterminal, follow-up messaging to an eligible completed child stage can reuse its retained `sessionFile`. After the root terminates, use explicit `/workflow attach <run-id> <stage>` post-mortem chat instead; `workflow send` never admits a retained-session turn after terminal publication.
|
|
3177
|
-
- Arbitrary `ctx.ui.custom<T>` widget prompts require the interactive workflow graph and return a clear unsupported message when targeted through `send`.
|
|
3178
|
-
- On a nonterminal root, `delivery: "auto"` first answers a pending prompt, then resumes paused work, then steers a streaming stage, and finally starts a fresh prompt when the live stage is idle.
|
|
3196
|
+
- `answer` responds only to a pending primitive or structured human-input prompt. It accepts `promptId` plus `response`, `text`, or `message`, preserves prompt-kind validation, and never sends stage chat, steers, resumes, or starts a model turn.
|
|
3197
|
+
- Send free-form updates through ordinary Intercom to `<runId>:<stageKey>`. Atomic delivers immediately to live stages and queues messages for known stages that have not started, delivering them before their first model turn; unknown stages remain unknown targets. Use `ask` once the target has a reply-capable live session. Use `workflow resume` only for paused workflow control.
|
|
3179
3198
|
- `pause`, `interrupt`, and `quit` can target one top-level run or `all: true`; `stageId` cannot be combined with `all: true`. Stage-scoped `pause` and `interrupt` controls can target a visible nested child stage from the expanded graph. Atomic routes stage controls to the owning nested run internally.
|
|
3180
3199
|
- `interrupt` and `quit` can also name one in-flight `ctx.tool` node with `stageId`, by expanded node id, local `tool:<argsHash>` id, or tool name. Both mean the same thing for a tool: abort that single call now. Tool nodes stay non-attachable — this is an abort control, not a chat target. Identifiers resolve exactly first and then uniquely; a name shared by two tool nodes (or by a stage and a tool) returns the same ambiguity diagnostic stages get, listing each match as `<name> (tool)`.
|
|
3181
3200
|
- Aborting one tool node leaves every sibling stage and sibling tool node running and does not pause the run. The node becomes `cancelled`, writes no replayable checkpoint, and re-runs on a later resume. Whether the run itself survives is ordinary author control flow: an awaited `ctx.tool` that is aborted rejects, exactly as it would for any other failure, unless the workflow catches it. A node that has already settled reports that it is not running rather than silently succeeding.
|
|
@@ -3212,7 +3231,7 @@ When a callback was abandoned, its executor stays alive but stops owning the run
|
|
|
3212
3231
|
|
|
3213
3232
|
When a paused stage interrupted an active model turn, Atomic preserves that turn's existing pause loop: a non-empty resume message is delivered exactly once through the resumed loop, and (if the stage has not finalized) Atomic injects `Continue where you left off. If you believe you are finished with your original task (or a redefined task if the user told you), stop.` before normal completion/readiness handling. A no-message interrupted-turn resume injects the same continuation directly. A different state applies when the stage was idle and waiting for a new stage-chat turn: resuming with a non-empty message starts exactly one fresh prompt containing the text, while an empty resume only releases the pause and does not fabricate a user turn or continuation.
|
|
3214
3233
|
|
|
3215
|
-
The same continuation applies to user messages queued into a live streaming stage. Steering a turn (Enter in an attached stage chat)
|
|
3234
|
+
The same continuation applies to user messages queued into a live streaming stage. Steering a turn (Enter in an attached stage chat) or queueing a follow-up (Ctrl+F) arms the identical continuation prompt, which Atomic injects once when the interrupted turn ends — even if several messages were queued during that turn — so a steered stage returns to its original (or user-redefined) objective instead of stopping after answering the queued message.
|
|
3216
3235
|
|
|
3217
3236
|
Messages delivered to an idle stage start a fresh user turn immediately and receive no continuation nudge; abort, kill, workflow exit, and finalized/fail-fast stage boundaries suppress late prompt creation and continuation injection.
|
|
3218
3237
|
|
|
@@ -3224,7 +3243,7 @@ These are distinct operations. *Resuming workflow execution* (`/workflow resume`
|
|
|
3224
3243
|
|
|
3225
3244
|
Any eligible terminal agent stage with a valid retained session opens as an interactive post-mortem chat through the explicit user-driven TUI path: completed-workflow inspection, `/workflow attach`, or `/workflow connect` followed by stage selection, including restored/replayed durable snapshots after a restart. Explicit `/workflow attach <root-run> <nested-stage>` targets are resolved through the expanded graph and routed to the child run that owns the stage while the overlay remains rooted on the requested graph; the resolved owner is preserved when sibling child workflows reuse the same local stage ID.
|
|
3226
3245
|
|
|
3227
|
-
|
|
3246
|
+
Intercom and explicit `/workflow attach` own stage communication. The workflow tool has no free-form message action; start a new workflow if tracked work remains after a terminal root.
|
|
3228
3247
|
|
|
3229
3248
|
When a nested stage is reopened after a restart or from another checkout through the explicit TUI path, its session cwd comes from the durable root workflow (resolved workflow cwd first, then original invocation cwd) while stage-control ownership remains with the actual child run. Follow-up turns are appended in place to the stage's retained session (no separate fork), so the agent may still invoke its ordinary tools and cause side effects; only the workflow DAG, run/stage status, results, timings, checkpoints, and topology are immutable. Post-mortem chat does not resume or modify workflow execution state.
|
|
3230
3249
|
|
|
@@ -3234,7 +3253,7 @@ Every host session replacement or shutdown invalidates post-mortem handles, incl
|
|
|
3234
3253
|
|
|
3235
3254
|
When a known stage cannot be reopened, the attached chat shows the complete `SESSION UNAVAILABLE` explanation down to the supported 40-column minimum instead of incorrectly labeling an invalid file as an archived transcript. Recoverably failed stages keep their execution-resume semantics and are not silently reopened as post-mortem chat.
|
|
3236
3255
|
|
|
3237
|
-
Completed stages also remain addressable by blocking `intercom.ask` calls from sibling workflow stages. If an ask reaches a completed target with a retained conversation, Atomic schedules one serialized post-mortem turn in that exact conversation
|
|
3256
|
+
Completed stages also remain addressable by blocking `intercom.ask` calls from sibling workflow stages. If an ask reaches a completed target with a retained conversation, Atomic schedules one serialized post-mortem turn in that exact conversation.
|
|
3238
3257
|
|
|
3239
3258
|
The target sees the original ask, and its normal `intercom.reply` remains correlated to the originating child session and message ID. The parent chat or another session cannot satisfy the waiter. Late-message routing uses single-owner claiming: after the workflow post-mortem router claims a completed-stage ask and assigns its completion promise, later listeners preserve that claim, making bundled extension registration order irrelevant.
|
|
3240
3259
|
|
|
@@ -3293,13 +3312,13 @@ When a workflow needs human input, answer in the graph viewer or attached stage
|
|
|
3293
3312
|
/workflow attach <run-id> <stage-id-or-name>
|
|
3294
3313
|
```
|
|
3295
3314
|
|
|
3296
|
-
Agents can answer primitive and structured pending prompts programmatically with `workflow({ action: "
|
|
3315
|
+
Agents can answer primitive and structured pending prompts programmatically with `workflow({ action: "answer", ... })` only while the root workflow is nonterminal; use `promptId` when it is present in the stage details, and provide answer content with `response`, `text`, or `message`. Arbitrary custom TUI widget prompts intentionally refuse this path in iteration 1 because a generic `T` cannot be reconstructed safely from a non-TUI payload.
|
|
3297
3316
|
|
|
3298
3317
|
`ctx.ui.custom<T>(factory, options?)` reuses Atomic's TUI component path: the factory receives the same real `(tui, theme, keybindings, done)` types as extension `ctx.ui.custom`, and the workflow resumes with the value passed to `done(value)`. Use `options.label` for a safe display-only graph/status label and `options.replayIdentity` when widget semantics can change without the callsite changing. Do not put secrets in labels or replay identities; only a hash of the identity is stored, and label text is not part of replay identity. Both inline connected rendering and `overlay: true` mount in the graph viewer's attached stage chat: overlay is a placement hint rather than a capability request, so an in-stage `ask_user_question` — which always asks for an overlay — mounts, takes focus, and resolves like any other custom prompt. There is no nested host overlay above the graph chrome; the widget occupies the stage-chat custom-UI slot and `overlayOptions` / `onHandle` are not consumed there.
|
|
3299
3318
|
|
|
3300
3319
|
Prompt answers are replayable only while the source run remains in the live in-memory store. `StageSnapshot.promptAnswerState` is snapshot-safe metadata for continuation: `available` means a matching live answer can be replayed, `unavailable` means the matching prompt node exists but its private answer was purged, and `ambiguous` means multiple matching prompt nodes exist so Atomic asks again. The raw answer lives in a private `PromptAnswerRecord` ledger, is never written to snapshots or persistence, and remains resident in memory until the answer is cleared, the run is removed, or the store is cleared.
|
|
3301
3320
|
|
|
3302
|
-
Prompt replay keys include the prompt kind, message text, select choices, input/editor initial value, custom prompt identity hash, and hashed author callsite, so changing any of those inputs may intentionally re-ask on continuation. An empty `ctx.ui.select(..., [])` has no answerable choices and throws before creating a prompt node. Arbitrary custom-widget answers cannot be supplied through `workflow
|
|
3321
|
+
Prompt replay keys include the prompt kind, message text, select choices, input/editor initial value, custom prompt identity hash, and hashed author callsite, so changing any of those inputs may intentionally re-ask on continuation. An empty `ctx.ui.select(..., [])` has no answerable choices and throws before creating a prompt node. Arbitrary custom-widget answers cannot be supplied through `workflow answer`; focus the `custom` awaiting-input node in the interactive graph instead.
|
|
3303
3322
|
|
|
3304
3323
|
If the user answers a human-in-the-loop prompt in the workflow UI or stage UI broker, the stage receives the answer directly and the active main chat receives a display-only notice (`triggerTurn: false`, `excludeFromContext: true`) containing a concise answer summary. The notice is rendered for the user and persisted for audit, but it does not wake the model, enter LLM context, or authorize answering any other workflow prompt. Prompt answers sent by the main-chat `workflow` tool are suppressed from this notice because the tool result already informs the current turn.
|
|
3305
3324
|
|
|
@@ -3307,7 +3326,7 @@ When an interactive, non-schema workflow stage calls `ask_user_question`, Atomic
|
|
|
3307
3326
|
|
|
3308
3327
|
In this chat-answer flow, choosing the ready option completes the stage and releases dependent stages. Choosing the not-ready option keeps the stage open for a genuine stage-chat turn and brokers readiness again after that turn. A chat answer is never treated as an invisible stay decision. On the readiness gate, **Type something.** sends the typed text as the next stage-chat message (empty or whitespace-only text cannot be submitted). **Chat about this** is a plain option — it does not open an inline editor — and stays by sending `The user would like to chat more about this`.
|
|
3309
3328
|
|
|
3310
|
-
The readiness prompt can be answered in the attached stage UI or with `workflow({ action: "
|
|
3329
|
+
The readiness prompt can be answered in the attached stage UI or with `workflow({ action: "answer", ... })`. Ordinary structured-option answers retain their existing readiness behavior. A schema-backed stage that has successfully finalized through `structured_output` is terminal and does not reopen this readiness gate.
|
|
3311
3330
|
|
|
3312
3331
|
|
|
3313
3332
|
## Durable Workflows and Cross-Session Resume
|
|
@@ -3431,7 +3450,7 @@ A run quit while a `ctx.tool` call was in flight resumes the same way: the unfin
|
|
|
3431
3450
|
|
|
3432
3451
|
Selecting a completed target—or a checkpointed failed target marked non-resumable—follows a separate read-only open path. Atomic reconstructs root and reciprocal nested child-run snapshots from authoritative checkpoints, remaps persisted source-stage, boundary, and tool references into a stable expanded hierarchy, and never calls the resume dispatcher or runs workflow code, tools, tasks, or prompts. These graphs remain inspectable even when no retained chat transcript survives, including tool-only graphs.
|
|
3433
3452
|
|
|
3434
|
-
A terminal child stage with a valid retained session may be reopened for detached post-mortem conversation through `/workflow attach` or completed graph inspection. Follow-up is routed to that real child `{runId, stageId}` and may append chat, but it cannot pause, resume, retry, mutate root or child execution state, write a terminal checkpoint, or emit a duplicate lifecycle notice.
|
|
3453
|
+
A terminal child stage with a valid retained session may be reopened for detached post-mortem conversation through `/workflow attach` or completed graph inspection. Follow-up is routed to that real child `{runId, stageId}` and may append chat, but it cannot pause, resume, retry, mutate root or child execution state, write a terminal checkpoint, or emit a duplicate lifecycle notice. Tool nodes never offer chat attachment.
|
|
3435
3454
|
|
|
3436
3455
|
New tool checkpoints persist topology. A current-format tool checkpoint created before that additive topology existed still replays safely: its cached output remains authoritative and its callback is never rerun. Root-level inspection derives deterministic fallback identity/order from checkpoint identity and record order. If a topology-less cached tool replays inside a child workflow, Atomic first appends awaited topology metadata with the current child/boundary ownership, without replacing the original output checkpoint. Foreign or malformed checkpoint formats remain excluded.
|
|
3437
3456
|
|
|
@@ -3494,7 +3513,7 @@ Atomic discovers workflow definitions in this order:
|
|
|
3494
3513
|
| `~/.atomic/agent/extensions/workflow/config.json` | Global | `workflows.<name>.path` for user-wide configured paths |
|
|
3495
3514
|
| `~/.atomic/agent/workflows/*.{ts,js,mjs,cjs}` | Global | Legacy `~/.pi/agent/workflows/` is also checked |
|
|
3496
3515
|
| Installed Atomic packages | Package | Uses package metadata or conventional `workflows/` directories |
|
|
3497
|
-
| Bundled workflows | Built-in | Shipped with `@bastani/workflows` |
|
|
3516
|
+
| Bundled workflows | Built-in | Shipped with `@bastani/atomic/workflows` |
|
|
3498
3517
|
|
|
3499
3518
|
A workflow module may export one default workflow definition and/or named workflow definitions. Discovery checks the default export first, then named exports.
|
|
3500
3519
|
|
|
@@ -3511,14 +3530,14 @@ To co-locate reusable helpers with your workflows — for example a `ctx.ui.cust
|
|
|
3511
3530
|
|
|
3512
3531
|
```ts
|
|
3513
3532
|
// .atomic/workflows/release-picker.ts
|
|
3514
|
-
import { workflow } from "@bastani/workflows";
|
|
3533
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
3515
3534
|
import { Type } from "typebox";
|
|
3516
3535
|
import { tableSelectorFactory } from "./lib/table-selector.js";
|
|
3517
3536
|
```
|
|
3518
3537
|
|
|
3519
3538
|
```ts
|
|
3520
3539
|
// .atomic/workflows/lib/table-selector.ts
|
|
3521
|
-
import type { WorkflowCustomUiFactory } from "@bastani/workflows";
|
|
3540
|
+
import type { WorkflowCustomUiFactory } from "@bastani/atomic/workflows";
|
|
3522
3541
|
|
|
3523
3542
|
export const tableSelectorFactory: WorkflowCustomUiFactory<{ id: string; name: string }> = (
|
|
3524
3543
|
tui,
|
|
@@ -3718,22 +3737,14 @@ atomic -e ./local-workflow-package
|
|
|
3718
3737
|
|
|
3719
3738
|
Workflow stage sessions inherit the same package and temporary `-e` resource discovery snapshot as the main chat. That means a workflow loaded from an external package or directory can start stages that see the package's extensions/tools, subagents and agent definitions, skills, prompt templates, themes, workflows, and trusted borrowed project-local resources without sharing the parent chat's resource-loader instance. Passing an explicit `resourceLoader` in stage options still opts that stage out of this inheritance.
|
|
3720
3739
|
|
|
3721
|
-
## Programmatic
|
|
3722
|
-
|
|
3723
|
-
`@bastani/workflows` is an Atomic package extension. It registers:
|
|
3740
|
+
## Programmatic usage
|
|
3724
3741
|
|
|
3725
|
-
|
|
3726
|
-
- `/workflow connect|attach|pause|interrupt|quit|resume|status|inputs|reload` for live control, inspection, and rediscovery
|
|
3727
|
-
- the `workflow` tool for named execution, discovery, inspection, messaging, run control, and reload
|
|
3742
|
+
`@bastani/atomic/workflows` is Atomic's published workflow SDK. Import `workflow` from that specifier, import `Type` from `typebox`, and export the definition returned by `workflow({...})`. Keep runtime helpers such as widget factories and shared utilities in a subdirectory outside the top-level discovery scan, such as `.atomic/workflows/lib/`; see [Workflow Locations](#workflow-locations).
|
|
3728
3743
|
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
Workflow definition files must export definitions produced by `workflow({...})`. Keep non-workflow runtime helpers (widget factories, shared utilities) in a subdirectory the discovery scan ignores, such as `.atomic/workflows/lib/` — see [Workflow Locations](#workflow-locations). The former imperative object-form runner is not part of the public SDK, and authored workflow files cannot use `runWorkflow` as a runner from `@bastani/workflows`.
|
|
3732
|
-
|
|
3733
|
-
Standalone TypeScript workflow packages type-check the SDK import without a hand-authored `.d.ts`, `declare module` shim, or `tsconfig` `paths` alias. The SDK types ship with `@bastani/atomic`, so a workflow package depends only on `@bastani/atomic` (plus a `typebox` peer):
|
|
3744
|
+
Package authors list both `@bastani/atomic` and `typebox` in `peerDependencies`. The `@bastani/atomic` package publishes compiled JavaScript and declarations for `@bastani/atomic/workflows`, `@bastani/atomic/workflows/builtin`, and each `@bastani/atomic/workflows/builtin/*` module. TypeScript resolves those exports directly under `moduleResolution: NodeNext`. When Atomic executes a workflow file, its runtime loader resolves the same published specifiers to Atomic's in-memory SDK.
|
|
3734
3745
|
|
|
3735
3746
|
```ts
|
|
3736
|
-
import { workflow } from "@bastani/workflows";
|
|
3747
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
3737
3748
|
import { Type } from "typebox";
|
|
3738
3749
|
|
|
3739
3750
|
export default workflow({
|
|
@@ -3750,29 +3761,7 @@ export default workflow({
|
|
|
3750
3761
|
});
|
|
3751
3762
|
```
|
|
3752
3763
|
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
- A package that imports `@bastani/atomic` anywhere (for example, an extension shipped in the same package) automatically resolves the workflow SDK types. `@bastani/atomic`'s root declarations reference the ambient bridge, so no extra configuration is needed.
|
|
3756
|
-
- A pure workflow-only package — one that imports nothing but `@bastani/workflows` — adds a single opt-in so TypeScript loads the ambient bridge. Set it once for the project in `tsconfig.json`:
|
|
3757
|
-
|
|
3758
|
-
```jsonc
|
|
3759
|
-
{
|
|
3760
|
-
"compilerOptions": {
|
|
3761
|
-
"module": "NodeNext",
|
|
3762
|
-
"moduleResolution": "NodeNext",
|
|
3763
|
-
"types": ["@bastani/atomic/workflows/ambient"]
|
|
3764
|
-
}
|
|
3765
|
-
}
|
|
3766
|
-
```
|
|
3767
|
-
|
|
3768
|
-
or add a single reference directive at the top of one workflow file:
|
|
3769
|
-
|
|
3770
|
-
```ts
|
|
3771
|
-
/// <reference types="@bastani/atomic/workflows/ambient" />
|
|
3772
|
-
```
|
|
3773
|
-
|
|
3774
|
-
Either form makes `import { workflow } from "@bastani/workflows"
|
|
3775
|
-
import { Type } from "typebox"` and the `@bastani/workflows/builtin/*` composition imports resolve under `tsc` (`moduleResolution: NodeNext`) with no hand-authored `.d.ts`, no `declare module` shim, and no `paths` alias. `@bastani/workflows` is not a separate npm package — its types ship with `@bastani/atomic` — so list both `@bastani/atomic` and `typebox` (workflow files import `Type` from `typebox`) in `peerDependencies`. Runtime discovery and loading via `atomic.workflows` are unchanged: Atomic's loader still supplies the SDK when workflow files execute.
|
|
3764
|
+
Programmatic callers import `run` and call `run(definition, inputs)` with an exported definition and validated inputs. Use `createRegistry()` when an integration needs to register, merge, or look up several definitions before selecting one to run. The extension also registers the `/workflow` commands and the `workflow` tool for named execution, discovery, inspection, messaging, run control, and reload.
|
|
3776
3765
|
|
|
3777
3766
|
|
|
3778
3767
|
### `workflow(spec)`
|
|
@@ -3787,7 +3776,7 @@ function workflow<
|
|
|
3787
3776
|
): AuthoredWorkflowDefinition<TInputs, TOutputs>;
|
|
3788
3777
|
```
|
|
3789
3778
|
|
|
3790
|
-
Creates the frozen
|
|
3779
|
+
Creates the frozen definition documented in [The `workflow()` definition](#the-workflow-definition). Export the returned definition or pass it to `ctx.workflow(...)`, `run(...)`, or a registry.
|
|
3791
3780
|
|
|
3792
3781
|
### `createRegistry(initial?)`
|
|
3793
3782
|
|
|
@@ -3812,7 +3801,7 @@ interface WorkflowRegistry {
|
|
|
3812
3801
|
Creates an immutable-style registry keyed by normalized workflow name. `register`, `merge`, and `remove` return registries rather than mutating the current registry.
|
|
3813
3802
|
|
|
3814
3803
|
```ts
|
|
3815
|
-
import { createRegistry, workflow } from "@bastani/workflows";
|
|
3804
|
+
import { createRegistry, workflow } from "@bastani/atomic/workflows";
|
|
3816
3805
|
import { Type } from "typebox";
|
|
3817
3806
|
|
|
3818
3807
|
const alpha = workflow({
|
|
@@ -4029,16 +4018,8 @@ The factory creates an isolated registry; `cancellationRegistry` is the default
|
|
|
4029
4018
|
export type { Static, TSchema } from "typebox";
|
|
4030
4019
|
```
|
|
4031
4020
|
|
|
4032
|
-
These TypeBox types are re-exported for authoring helpers.
|
|
4021
|
+
These TypeBox types are re-exported for authoring helpers. Import the runtime `Type` builder from `typebox`.
|
|
4033
4022
|
|
|
4034
|
-
### `runWorkflow` (removed)
|
|
4035
|
-
|
|
4036
|
-
```typescript
|
|
4037
|
-
/** @deprecated Always throws a migration error. */
|
|
4038
|
-
const runWorkflow: never;
|
|
4039
|
-
```
|
|
4040
|
-
|
|
4041
|
-
This runtime migration stub exists only so old modules fail at the callsite with a clear error. Use `workflow({...})` for authoring and `run(...)` for programmatic execution.
|
|
4042
4023
|
|
|
4043
4024
|
### Builtin workflow exports
|
|
4044
4025
|
|
|
@@ -4053,23 +4034,25 @@ import {
|
|
|
4053
4034
|
openClaudeDesign,
|
|
4054
4035
|
ralph,
|
|
4055
4036
|
tournament,
|
|
4056
|
-
} from "@bastani/workflows/builtin";
|
|
4037
|
+
} from "@bastani/atomic/workflows/builtin";
|
|
4057
4038
|
```
|
|
4058
4039
|
|
|
4059
4040
|
Each export is a workflow definition. All nine definitions are available through individual module paths. See [Compose with builtin workflows](#compose-with-builtin-workflows) for a parent workflow example.
|
|
4060
4041
|
|
|
4061
4042
|
|
|
4062
|
-
## Fast
|
|
4043
|
+
## Fast inference for workflow stages
|
|
4044
|
+
|
|
4045
|
+
Workflow stages can use faster inference on supported OpenAI and GitHub Copilot models so multi-stage runs finish sooner.
|
|
4063
4046
|
|
|
4064
|
-
|
|
4047
|
+
### Fast mode
|
|
4065
4048
|
|
|
4066
|
-
|
|
4049
|
+
Use `/fast` to manage fast mode separately for normal chat and workflow-stage sessions. The settings are `codexFastMode.chat` and `codexFastMode.workflow`; workflow stages use the workflow scope, not the chat scope. A stage inside a nested `ctx.workflow(...)` call keeps that workflow scope, and subagents launched by the stage inherit it.
|
|
4067
4050
|
|
|
4068
|
-
|
|
4051
|
+
Fast mode is eligible for supported `openai/*` and `openai-codex/*` providers, provider aliases that use the shared `openai-codex-responses` transport, and GitHub Copilot models whose OAuth account catalog advertises a fast variant. OpenAI requests use the priority service tier. GitHub Copilot requests use the account-supported fast variant without adding the OpenAI service-tier field. Azure OpenAI, OpenRouter, and generic OpenAI-compatible providers are not eligible.
|
|
4069
4052
|
|
|
4070
|
-
|
|
4053
|
+
Atomic resolves eligibility for the effective model on every fallback attempt. A supported fallback can use fast mode even when the primary failed, while an unsupported fallback keeps its normal request behavior. Workflow stage model labels and stage-launched subagent result labels keep the raw model ID and append a separate `fast` marker; graph node cards keep their dependency metadata focused on topology and do not repeat that marker.
|
|
4071
4054
|
|
|
4072
|
-
Enable workflow fast mode deliberately for broad workflows
|
|
4055
|
+
Enable workflow fast mode deliberately for broad workflows. Parallel fan-out and fallback attempts can multiply fast provider requests and usage.
|
|
4073
4056
|
|
|
4074
4057
|
## Context Engineering
|
|
4075
4058
|
|
|
@@ -4245,120 +4228,6 @@ Before turning a process into a workflow, confirm that it suits automation:
|
|
|
4245
4228
|
|
|
4246
4229
|
For complex workflows, structure the implementation as a pipeline: acquire context, prepare prompts/artifacts, process with LLM stages, parse or validate outputs, and render the final result.
|
|
4247
4230
|
|
|
4248
|
-
## Migrating from the `defineWorkflow()` Builder API
|
|
4249
|
-
|
|
4250
|
-
[#1457](https://github.com/bastani-inc/atomic/pull/1457) removed the chained builder API — `defineWorkflow(name).description(...).input(...).output(...).worktreeFromInputs(...).run(...).compile()` — and made the single `workflow({ name?, description, inputs, outputs, run })` object form the only authoring API. There is no shim and no deprecation period: workflow files that still call `defineWorkflow(...).compile()` fail discovery with a module-load error until authors migrate them.
|
|
4251
|
-
|
|
4252
|
-
Use this section for workflow files that use the previous API. If you are authoring a new workflow, skip it and start from [Writing a Workflow](#writing-a-workflow).
|
|
4253
|
-
|
|
4254
|
-
### What changed
|
|
4255
|
-
|
|
4256
|
-
- `import { defineWorkflow, Type } from "@bastani/workflows"` → `workflow` now comes from `@bastani/workflows`, and `Type` comes from the `typebox` package directly. `@bastani/workflows` no longer re-exports `Type`. The `Static` and `TSchema` *type* exports are still re-exported from `@bastani/workflows`, so `import type { Static } from "@bastani/workflows"` keeps working — only the runtime `Type` builder moved.
|
|
4257
|
-
- The fluent builder chain became one object literal passed to `workflow({ ... })`.
|
|
4258
|
-
- `name` moved from the `defineWorkflow(name)` argument into the object. It is now **optional** — omit it and discovery derives the name from the filename (the recommended style used by the builtins and most examples), or keep it when you want the name to differ from the file's basename.
|
|
4259
|
-
- `outputs` is now **required**. Workflows that declared no outputs before must now pass `outputs: {}`.
|
|
4260
|
-
- `.compile()` is gone. `workflow({ ... })` returns the frozen, branded definition directly; `export default` it.
|
|
4261
|
-
- The imperative object-form `runWorkflow(...)` runner is also removed (it is a `never` placeholder that throws on access). Programmatic execution uses the exported `run(def, inputs)` helper or a registry — see [Programmatic Usage](#programmatic-usage).
|
|
4262
|
-
|
|
4263
|
-
### Builder method → object key
|
|
4264
|
-
|
|
4265
|
-
| Removed builder API | New `workflow({ ... })` key |
|
|
4266
|
-
| --- | --- |
|
|
4267
|
-
| `defineWorkflow("name")` argument | `name: "name"` (optional; derived from the filename when omitted) |
|
|
4268
|
-
| `.description(text)` | `description: text` |
|
|
4269
|
-
| `.input(key, schema)` (repeatable) | `inputs: { key: schema, ... }` |
|
|
4270
|
-
| `.output(key, schema)` (repeatable) | `outputs: { key: schema, ... }` (required, even if `{}`) |
|
|
4271
|
-
| `.worktreeFromInputs(binding)` | `worktreeFromInputs: binding` (binding shape unchanged) |
|
|
4272
|
-
| `.run(fn)` callback | `run: fn` |
|
|
4273
|
-
| `.compile()` terminal | delete — `workflow({ ... })` returns the definition |
|
|
4274
|
-
|
|
4275
|
-
`ctx` and every primitive (`ctx.task`, `ctx.chain`, `ctx.parallel`, `ctx.stage`, `ctx.workflow`, `ctx.exit`, `ctx.ui`) are unchanged, so **you do not need to rewrite workflow bodies** — only the authoring wrapper changes.
|
|
4276
|
-
|
|
4277
|
-
### Full before / after
|
|
4278
|
-
|
|
4279
|
-
Before (removed API):
|
|
4280
|
-
|
|
4281
|
-
```ts
|
|
4282
|
-
import { defineWorkflow, Type } from "@bastani/workflows";
|
|
4283
|
-
|
|
4284
|
-
export default defineWorkflow("review-changes")
|
|
4285
|
-
.description("Run two reviewers in parallel and synthesize a decision.")
|
|
4286
|
-
.input("target", Type.String({ description: "Path or change target to review." }))
|
|
4287
|
-
.input("base_branch", Type.String({ default: "origin/main" }))
|
|
4288
|
-
.output("decision", Type.String())
|
|
4289
|
-
.output("concerns", Type.Optional(Type.Array(Type.String())))
|
|
4290
|
-
.worktreeFromInputs({ baseBranch: "base_branch" })
|
|
4291
|
-
.run(async (ctx) => {
|
|
4292
|
-
const target = String(ctx.inputs.target);
|
|
4293
|
-
const [quality, runtime] = await ctx.parallel(
|
|
4294
|
-
[
|
|
4295
|
-
{ name: "quality", prompt: `Review quality of ${target}` },
|
|
4296
|
-
{ name: "runtime", prompt: `Review runtime behavior of ${target}` },
|
|
4297
|
-
],
|
|
4298
|
-
{ concurrency: 2 },
|
|
4299
|
-
);
|
|
4300
|
-
return { decision: `${quality.text}\n${runtime.text}`, concerns: [] };
|
|
4301
|
-
})
|
|
4302
|
-
.compile();
|
|
4303
|
-
```
|
|
4304
|
-
|
|
4305
|
-
After (current API):
|
|
4306
|
-
|
|
4307
|
-
```ts
|
|
4308
|
-
import { workflow } from "@bastani/workflows";
|
|
4309
|
-
import { Type } from "typebox";
|
|
4310
|
-
|
|
4311
|
-
export default workflow({
|
|
4312
|
-
name: "review-changes", // optional — omit to derive from filename
|
|
4313
|
-
description: "Run two reviewers in parallel and synthesize a decision.",
|
|
4314
|
-
inputs: {
|
|
4315
|
-
target: Type.String({ description: "Path or change target to review." }),
|
|
4316
|
-
base_branch: Type.String({ default: "origin/main" }),
|
|
4317
|
-
},
|
|
4318
|
-
outputs: {
|
|
4319
|
-
decision: Type.String(),
|
|
4320
|
-
concerns: Type.Optional(Type.Array(Type.String())),
|
|
4321
|
-
},
|
|
4322
|
-
worktreeFromInputs: { baseBranch: "base_branch" },
|
|
4323
|
-
run: async (ctx) => {
|
|
4324
|
-
const target = String(ctx.inputs.target);
|
|
4325
|
-
const [quality, runtime] = await ctx.parallel(
|
|
4326
|
-
[
|
|
4327
|
-
{ name: "quality", prompt: `Review quality of ${target}` },
|
|
4328
|
-
{ name: "runtime", prompt: `Review runtime behavior of ${target}` },
|
|
4329
|
-
],
|
|
4330
|
-
{ concurrency: 2 },
|
|
4331
|
-
);
|
|
4332
|
-
return { decision: `${quality.text}\n${runtime.text}`, concerns: [] };
|
|
4333
|
-
},
|
|
4334
|
-
});
|
|
4335
|
-
```
|
|
4336
|
-
|
|
4337
|
-
### Conversion checklist
|
|
4338
|
-
|
|
4339
|
-
For each `.atomic/workflows/*.ts` (or workflow-package) file:
|
|
4340
|
-
|
|
4341
|
-
1. Swap the import to `import { workflow } from "@bastani/workflows"` and add `import { Type } from "typebox"`. Drop `defineWorkflow` from the `@bastani/workflows` import. `import type { Static, TSchema }` can stay on the `@bastani/workflows` import if you use those types.
|
|
4342
|
-
2. Replace `defineWorkflow("<name>")` with `workflow({`. You may keep `name: "<name>"` or drop the key entirely to derive the name from the filename.
|
|
4343
|
-
3. Move `.description("<text>")` to a `description: "<text>",` property.
|
|
4344
|
-
4. Collect every `.input(key, schema)` into one `inputs: { key: schema, ... },` map.
|
|
4345
|
-
5. Collect every `.output(key, schema)` into one `outputs: { key: schema, ... },` map. If there were no `.output(...)` calls, add `outputs: {},` — it is now required.
|
|
4346
|
-
6. Move `.worktreeFromInputs(binding)` to a `worktreeFromInputs: binding,` property (same binding shape, unchanged).
|
|
4347
|
-
7. Move the `.run(fn)` callback to a `run: fn,` property; keep the body byte-for-byte identical.
|
|
4348
|
-
8. Delete the trailing `.compile()`, close the object with `})`, and keep `export default`.
|
|
4349
|
-
9. Run `/workflow reload` (or restart Atomic) and `/workflow list` to confirm the file loads. Because `ctx` and its primitives are unchanged, stage behavior, graph layout, resume/quit, and human-input prompts are unaffected.
|
|
4350
|
-
|
|
4351
|
-
### Gotchas
|
|
4352
|
-
|
|
4353
|
-
- **`outputs` is required.** The old `.output(...)` calls were optional, and a workflow without outputs compiled successfully. The new object form throws `workflow: outputs must be a schema map` when `outputs` is missing, so declare `outputs: {}` for outputless workflows.
|
|
4354
|
-
- **`Type` is no longer re-exported.** `import { Type } from "@bastani/workflows"` fails type-checking; import it from `typebox` instead. (`Static` and `TSchema` *types* are still re-exported from `@bastani/workflows`, so those imports do not need to change.)
|
|
4355
|
-
- **`.compile()` does not exist.** Leaving it produces a runtime `TypeError`; `workflow({ ... })` already returns the frozen, branded definition.
|
|
4356
|
-
- **`name` is derived from the filename when omitted.** Discovery derives the name from the filename: `review-changes.ts` becomes `review-changes`, so an explicit `name` is only needed when it should differ from the basename.
|
|
4357
|
-
- **Do not construct definitions manually.** Discovery rejects hand-built objects carrying `__piWorkflow: true`, and `ctx.workflow(...)` rejects them too. Both accept only definitions minted by `workflow({ ... })`.
|
|
4358
|
-
- **The imperative `runWorkflow` runner is gone.** It is now a `never` placeholder that throws on access; use the exported `run(def, inputs)` helper or a registry for programmatic execution.
|
|
4359
|
-
- **Keep `outputs` inline for the strictest type checking.** The old builder enforced no-extra-output keys through a `NoExtraOutputs` generic on `.run(fn)`; the object form re-creates that check for inline `outputs` maps, but cannot recover output keys when a schema map is widened or built up before being passed to `workflow({ ... })`. Keep the `outputs` literal inline so the declared-key check stays exact.
|
|
4360
|
-
|
|
4361
|
-
Everything else — stage primitives, `ctx.inputs` typing, runtime validation, DAG inference, MCP scoping, resume/quit, worktree binding, model fallback, and the `/workflow` tool contract — is unchanged.
|
|
4362
4231
|
|
|
4363
4232
|
## Design Checklist
|
|
4364
4233
|
|
|
@@ -4388,7 +4257,7 @@ Good workflows are information-flow systems, not just prompt sequences. Keep sta
|
|
|
4388
4257
|
- Do not guess input keys; inspect with `inputs` or `get` first.
|
|
4389
4258
|
- Do not call `create`, `update`, or `delete` on the workflow tool; definitions are code-authored.
|
|
4390
4259
|
- Do not use legacy workflow tool fields like `agent`, `stage`, or run-control `name`.
|
|
4391
|
-
- Do not pass strings or path objects to `ctx.workflow(...)`; import the workflow definition from `@bastani/workflows/builtin` or another TypeScript module first.
|
|
4260
|
+
- Do not pass strings or path objects to `ctx.workflow(...)`; import the workflow definition from `@bastani/atomic/workflows/builtin` or another TypeScript module first.
|
|
4392
4261
|
- Do not create a self-edge or a dependency edge from the current frontier to an existing ancestor. Cyclic workflow graphs are unsupported; redesign or stop before launch when a cycle cannot be removed.
|
|
4393
4262
|
- Do not model a bounded loop by reopening an earlier node beneath its downstream work. Create distinct tracked work per iteration and keep retained-session follow-up as non-topological activity when it adds no dependency work.
|
|
4394
4263
|
- Do not claim TypeScript or workflow discovery proves a dynamic workflow acyclic. Discovery diagnoses imports and definition shape; execution, replay, and DBOS hydration are the runtime topology boundary.
|
|
@@ -4853,7 +4722,7 @@ During the pre-launch architecture pass, enumerate the slices in the coverage ma
|
|
|
4853
4722
|
└─────────────────────────────────────────────────────────────┘
|
|
4854
4723
|
```
|
|
4855
4724
|
|
|
4856
|
-
Run each slice through a child workflow that owns its implement/review/repair lifecycle. Import `goal` or `ralph` from `@bastani/workflows/builtin`, or use a task-specific child when neither builtin matches. Before each child, use a durable `ctx.tool(...)` step to create or check out the slice's explicit branch in its worktree. `worktreeFromInputs` creates a missing target with a detached checkout and reuses an existing target as-is; `base_branch` and `git_worktree_dir` do not create or check out a feature branch by themselves. Create slice N+1's branch from slice N's verified branch, then pass that previous branch as `base_branch` and give the child a distinct `git_worktree_dir`.
|
|
4725
|
+
Run each slice through a child workflow that owns its implement/review/repair lifecycle. Import `goal` or `ralph` from `@bastani/atomic/workflows/builtin`, or use a task-specific child when neither builtin matches. Before each child, use a durable `ctx.tool(...)` step to create or check out the slice's explicit branch in its worktree. `worktreeFromInputs` creates a missing target with a detached checkout and reuses an existing target as-is; `base_branch` and `git_worktree_dir` do not create or check out a feature branch by themselves. Create slice N+1's branch from slice N's verified branch, then pass that previous branch as `base_branch` and give the child a distinct `git_worktree_dir`.
|
|
4857
4726
|
|
|
4858
4727
|
The parent should verify each child before creating the next boundary. If a gate fails, stop at the first failed gate, report that slice as unverified, and retain the earlier verified slices and their branch/worktree records. Do not roll earlier slices back and do not continue past the failure.
|
|
4859
4728
|
|
|
@@ -4863,8 +4732,8 @@ The calls below are deliberately unrolled. Repeat the downstream shape for the p
|
|
|
4863
4732
|
import { spawnSync } from "node:child_process";
|
|
4864
4733
|
import { resolve } from "node:path";
|
|
4865
4734
|
import { Type } from "typebox";
|
|
4866
|
-
import { workflow } from "@bastani/workflows";
|
|
4867
|
-
import { goal } from "@bastani/workflows/builtin";
|
|
4735
|
+
import { workflow } from "@bastani/atomic/workflows";
|
|
4736
|
+
import { goal } from "@bastani/atomic/workflows/builtin";
|
|
4868
4737
|
|
|
4869
4738
|
function spawnCommand(argv: readonly string[], cwd: string) {
|
|
4870
4739
|
const [command, ...args] = argv;
|