@bastani/atomic 0.8.24 → 0.8.25
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 +12 -0
- package/dist/builtin/intercom/CHANGELOG.md +12 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +12 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +12 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/CHANGELOG.md +12 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +12 -0
- package/dist/builtin/workflows/README.md +21 -12
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/src/tui/inputs-overlay.ts +56 -23
- package/docs/workflows.md +14 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.8.25] - 2026-06-04
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Promoted the 0.8.25 prerelease package version to a stable release.
|
|
10
|
+
|
|
11
|
+
## [0.8.25-alpha.1] - 2026-06-04
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Bumped package version for the Atomic 0.8.25-alpha.1 prerelease.
|
|
16
|
+
|
|
5
17
|
## [0.8.24] - 2026-06-04
|
|
6
18
|
|
|
7
19
|
### Changed
|
|
@@ -4,6 +4,18 @@ All notable changes to the `pi-intercom` extension will be documented in this fi
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.8.25] - 2026-06-04
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Promoted the 0.8.25 prerelease package version to a stable release.
|
|
12
|
+
|
|
13
|
+
## [0.8.25-alpha.1] - 2026-06-04
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Bumped package version for the Atomic 0.8.25-alpha.1 prerelease.
|
|
18
|
+
|
|
7
19
|
## [0.8.24] - 2026-06-04
|
|
8
20
|
|
|
9
21
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/intercom",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.25",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension providing a private coordination channel between parent and child agent sessions. Fork of: https://github.com/nicobailon/pi-intercom",
|
|
6
6
|
"contributors": [
|
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.25] - 2026-06-04
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Promoted the 0.8.25 prerelease package version to a stable release.
|
|
15
|
+
|
|
16
|
+
## [0.8.25-alpha.1] - 2026-06-04
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Bumped package version for the Atomic 0.8.25-alpha.1 prerelease.
|
|
21
|
+
|
|
10
22
|
## [0.8.24] - 2026-06-04
|
|
11
23
|
|
|
12
24
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/mcp",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.25",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension that adapts MCP (Model Context Protocol) servers into the coding agent. Fork of: https://github.com/nicobailon/pi-mcp-adapter",
|
|
6
6
|
"contributors": [
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.8.25] - 2026-06-04
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- Promoted the 0.8.25 prerelease package version to a stable release.
|
|
10
|
+
|
|
11
|
+
## [0.8.25-alpha.1] - 2026-06-04
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Bumped package version for the Atomic 0.8.25-alpha.1 prerelease.
|
|
16
|
+
|
|
5
17
|
## [0.8.24] - 2026-06-04
|
|
6
18
|
|
|
7
19
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/subagents",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.25",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification. Fork of: https://github.com/nicobailon/pi-subagents",
|
|
6
6
|
"contributors": [
|
|
@@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.8.25] - 2026-06-04
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Promoted the 0.8.25 prerelease package version to a stable release.
|
|
12
|
+
|
|
13
|
+
## [0.8.25-alpha.1] - 2026-06-04
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Bumped package version for the Atomic 0.8.25-alpha.1 prerelease.
|
|
18
|
+
|
|
7
19
|
## [0.8.24] - 2026-06-04
|
|
8
20
|
|
|
9
21
|
### Changed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bastani/web-access",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.25",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Atomic extension for web search, URL fetching, GitHub repo cloning, PDF/video extraction. Fork of: https://github.com/nicobailon/pi-web-access",
|
|
6
6
|
"contributors": [
|
|
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.8.25] - 2026-06-04
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Promoted the 0.8.25 prerelease package version to a stable release.
|
|
14
|
+
|
|
15
|
+
## [0.8.25-alpha.1] - 2026-06-04
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Fixed the interactive workflow inputs selector so it hides the working loader while replacing the editor, keeping the picker pinned to the bottom like `ask_user_question` without wedged host chrome ([#1224](https://github.com/bastani-inc/atomic/issues/1224)).
|
|
20
|
+
|
|
9
21
|
## [0.8.24] - 2026-06-04
|
|
10
22
|
|
|
11
23
|
### Changed
|
|
@@ -273,9 +273,9 @@ export default defineWorkflow("fallback-review")
|
|
|
273
273
|
.description("Review with a model fallback chain.")
|
|
274
274
|
.input("topic", Type.String())
|
|
275
275
|
.output("review", Type.String({ description: "Reviewer output text." }))
|
|
276
|
-
.output("model", Type.String({ description: "Model that produced the review." }))
|
|
277
|
-
.output("attemptedModels", Type.Array(Type.String(), { description: "Models tried, in fallback order." }))
|
|
278
|
-
.output("modelAttempts", Type.Array(Type.Unknown(), { description: "Per-attempt model fallback details." }))
|
|
276
|
+
.output("model", Type.Optional(Type.String({ description: "Model that produced the review." })))
|
|
277
|
+
.output("attemptedModels", Type.Optional(Type.Array(Type.String(), { description: "Models tried, in fallback order." })))
|
|
278
|
+
.output("modelAttempts", Type.Optional(Type.Array(Type.Unknown(), { description: "Per-attempt model fallback details." })))
|
|
279
279
|
.run(async (ctx) => {
|
|
280
280
|
const review = await ctx.task("reviewer", {
|
|
281
281
|
prompt: `Review this topic: ${String(ctx.inputs.topic)}`,
|
|
@@ -286,8 +286,8 @@ export default defineWorkflow("fallback-review")
|
|
|
286
286
|
return {
|
|
287
287
|
review: review.text,
|
|
288
288
|
model: review.model,
|
|
289
|
-
attemptedModels: review.attemptedModels,
|
|
290
|
-
modelAttempts: review.modelAttempts,
|
|
289
|
+
attemptedModels: review.attemptedModels ? [...review.attemptedModels] : undefined,
|
|
290
|
+
modelAttempts: review.modelAttempts ? [...review.modelAttempts] : undefined,
|
|
291
291
|
};
|
|
292
292
|
})
|
|
293
293
|
.compile();
|
|
@@ -362,7 +362,7 @@ A required input is any schema that is neither `Type.Optional(...)` nor carries
|
|
|
362
362
|
|
|
363
363
|
### Output types
|
|
364
364
|
|
|
365
|
-
Declare outputs with `.output(key, schema
|
|
365
|
+
Declare outputs with `.output(key, schema)` when a workflow result should be part of its runtime contract, especially when another workflow will call it as a child. Lead with the most precise schema you can express — the loose rows at the bottom are last resorts for genuinely dynamic data.
|
|
366
366
|
|
|
367
367
|
| Schema | Runtime value accepted |
|
|
368
368
|
| --------------------------------------------------- | --------------------------------------------------- |
|
|
@@ -399,16 +399,16 @@ A loose schema types the value as `unknown`/`Record<string, unknown>` everywhere
|
|
|
399
399
|
|
|
400
400
|
#### `Type.Unsafe<T>()` escape hatch
|
|
401
401
|
|
|
402
|
-
When you already have a precise TypeScript
|
|
402
|
+
When you already have a precise TypeScript type for a deeply-nested serializable value and don't want to hand-write the full 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** 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:
|
|
403
403
|
|
|
404
404
|
```typescript
|
|
405
405
|
import { defineWorkflow, Type } from "@bastani/workflows";
|
|
406
406
|
|
|
407
|
-
|
|
407
|
+
type ResearchPacket = {
|
|
408
408
|
readonly topic: string;
|
|
409
409
|
readonly score: number;
|
|
410
410
|
readonly sections: readonly { readonly heading: string; readonly body: string }[];
|
|
411
|
-
}
|
|
411
|
+
};
|
|
412
412
|
|
|
413
413
|
export default defineWorkflow("research-packet")
|
|
414
414
|
.input("topic", Type.String())
|
|
@@ -460,7 +460,7 @@ Input overrides are bare `key=value` tokens (no leading `--`). Values are JSON-p
|
|
|
460
460
|
|
|
461
461
|
Workflows always run as **background tasks** in interactive sessions — the chat editor stays free while a run executes. Press **F2** (or `/workflow connect <run-id>`) to attach to the live graph viewer; HIL prompts (`ctx.ui.input/confirm/select/editor`) appear as awaiting-input graph nodes. Press Enter on the node to answer locally, never as a modal dialog over the chat. Human input is detected when those runtime `ctx.ui.*` calls execute; workflows no longer have a declaration-time HIL flag.
|
|
462
462
|
|
|
463
|
-
Nested `ctx.workflow(...)` calls are displayed as an expanded graph within the top-level run. `/workflow status` and run pickers list only top-level user-launched workflows, not implementation-owned child runs.
|
|
463
|
+
Nested `ctx.workflow(...)` calls are displayed as an expanded graph within the top-level run. `/workflow status` and run pickers list only top-level user-launched workflows, not implementation-owned child runs. The `workflow` tool's `stages`, `stage`, `transcript`, `send`, `pause`, `interrupt`, and `resume` actions can still target visible child stage ids, prefixes, or names from the expanded graph; Atomic routes the control action to the owning nested run internally. (`stages`, `stage`, `transcript`, and `send` are `workflow` tool actions, not `/workflow` slash subcommands; the slash command exposes `connect`, `attach`, `pause`, `list`, `status`, `interrupt`, `kill`, `resume`, `reload`, and `inputs`.)
|
|
464
464
|
|
|
465
465
|
Prompt answer replay is live-memory only. `StageSnapshot.promptAnswerState` reports whether continuation can replay a prompt answer (`available`), must ask again because the private ledger entry is gone (`unavailable`), or must ask again because multiple matching prompt nodes are ambiguous (`ambiguous`). Raw answers stay in a private `PromptAnswerRecord` ledger, are never serialized to snapshots or persistence, and remain resident in memory until the answer is cleared, the run is removed, or the store is cleared. Replay keys include prompt kind, message text, select choices, input/editor initial value, and hashed author callsite, so changing any of those inputs may intentionally re-ask on continuation. Empty `ctx.ui.select(..., [])` calls throw before creating a prompt node.
|
|
466
466
|
|
|
@@ -490,7 +490,16 @@ Prompt answer replay is live-memory only. `StageSnapshot.promptAnswerState` repo
|
|
|
490
490
|
"promptId": "optional pending prompt identifier for send/answer",
|
|
491
491
|
"reason": "optional human-readable reload reason",
|
|
492
492
|
"all": "optional boolean for pause/interrupt/kill all; cannot be combined with stageId",
|
|
493
|
-
"task
|
|
493
|
+
"task": "optional direct task object (name + prompt/task) or root task string for direct chain/parallel runs",
|
|
494
|
+
"tasks": "optional array of direct task objects (parallel direct run)",
|
|
495
|
+
"chain": "optional array of direct task objects and/or { parallel: [...] } groups (sequential direct run)",
|
|
496
|
+
"chainName": "optional label for a direct chain run",
|
|
497
|
+
"concurrency": "optional parallelism limit for direct tasks/chain",
|
|
498
|
+
"failFast": "optional fail-fast toggle for direct parallel work",
|
|
499
|
+
"async": "optional boolean to dispatch a run in the background",
|
|
500
|
+
"intercom": "optional intercom coordination options",
|
|
501
|
+
"chainDir": "optional directory for direct chain artifacts",
|
|
502
|
+
"session/task options": "per-stage overrides also accepted at the top level and on direct task items — model, thinkingLevel, fallbackModels, tools, noTools, customTools, mcp, context, cwd, output, outputMode, reads, worktree, gitWorktreeDir, baseBranch, maxOutput, artifacts, and more"
|
|
494
503
|
}
|
|
495
504
|
}
|
|
496
505
|
```
|
|
@@ -574,7 +583,7 @@ Goal Runner workflow: initialize a persisted goal ledger with a per-run goal id
|
|
|
574
583
|
|
|
575
584
|
`goal` defaults to 10 worker/review turns. Reviewer quorum is fixed internally at 2 reviewer `complete` votes. The repeated-blocker threshold defaults to 3 consecutive same-blocker turns and is clamped to `max_turns` when you run fewer than 3 turns.
|
|
576
585
|
|
|
577
|
-
Child workflow outputs: `result`, `status`, `approved`, `goal_id`, `objective`, `ledger_path`, `turns_completed`, `iterations_completed`, `receipts`, `remaining_work`, and `
|
|
586
|
+
Child workflow outputs: `result`, `status`, `approved`, `goal_id`, `objective`, `ledger_path`, `turns_completed`, `iterations_completed`, `receipts`, `remaining_work`, `review_report`, and `review_report_path`.
|
|
578
587
|
|
|
579
588
|
### `ralph`
|
|
580
589
|
|
|
@@ -15,14 +15,17 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Mount mode
|
|
17
17
|
* ----------
|
|
18
|
-
* Uses
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
18
|
+
* Uses the same bottom-pinned primitive as `ask_user_question`: hide the
|
|
19
|
+
* host working-loader row for the lifetime of the blocking picker and mount
|
|
20
|
+
* with `{ overlay: false }`. pi's interactive `ExtensionUiController.custom`
|
|
21
|
+
* then REPLACES the editor with the mounted component
|
|
22
|
+
* (`editorContainer.clear(); addChild(component)`), which puts the focused
|
|
23
|
+
* picker in the bottom editor slot instead of a floating overlay. Suppressing
|
|
24
|
+
* `Working…` while mounted keeps host chrome from being wedged between the
|
|
25
|
+
* `/workflow …` command and the picker, avoiding the prior bottom-anchored
|
|
26
|
+
* overlay regression captured in
|
|
27
|
+
* `ui/workflows/Screenshot 2026-05-13 at 1.09.32 AM.png` without overlay
|
|
28
|
+
* anchor/padding tricks — the host owns geometry.
|
|
26
29
|
*
|
|
27
30
|
* cross-ref:
|
|
28
31
|
* - src/tui/inputs-picker.ts (pure state + render)
|
|
@@ -48,6 +51,7 @@ import {
|
|
|
48
51
|
|
|
49
52
|
export interface InputsUiSurface {
|
|
50
53
|
custom?: PiCustomOverlayFunction;
|
|
54
|
+
setWorkingVisible?: (visible: boolean) => void;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
export type InputsPickerResult =
|
|
@@ -68,7 +72,7 @@ export interface OpenInputsPickerOpts {
|
|
|
68
72
|
* confirm, or `cancel` on esc / no UI surface.
|
|
69
73
|
*
|
|
70
74
|
* Behaviour matrix:
|
|
71
|
-
* - `pi.ui.custom` present: mounted
|
|
75
|
+
* - `pi.ui.custom` present: mounted in the editor slot, settled by `done()`
|
|
72
76
|
* - no `pi.ui.custom` at all: resolves `cancel` immediately so the slash
|
|
73
77
|
* command can fall back to the "missing
|
|
74
78
|
* required input" text path
|
|
@@ -79,11 +83,6 @@ export function openInputsPicker(
|
|
|
79
83
|
): Promise<InputsPickerResult> {
|
|
80
84
|
return new Promise<InputsPickerResult>((resolve) => {
|
|
81
85
|
const { workflowName, fields, prefilled, theme } = opts;
|
|
82
|
-
const custom = ui.custom;
|
|
83
|
-
if (typeof custom !== "function") {
|
|
84
|
-
resolve({ kind: "cancel" });
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
86
|
if (fields.length === 0) {
|
|
88
87
|
// No inputs to collect — treat as immediate run with whatever the
|
|
89
88
|
// caller already prefilled (likely empty).
|
|
@@ -91,11 +90,40 @@ export function openInputsPicker(
|
|
|
91
90
|
return;
|
|
92
91
|
}
|
|
93
92
|
|
|
93
|
+
let workingHidden = false;
|
|
94
|
+
const hideWorking = (): void => {
|
|
95
|
+
ui.setWorkingVisible?.(false);
|
|
96
|
+
workingHidden = true;
|
|
97
|
+
};
|
|
98
|
+
const restoreWorking = (): void => {
|
|
99
|
+
if (!workingHidden) return;
|
|
100
|
+
workingHidden = false;
|
|
101
|
+
ui.setWorkingVisible?.(true);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
hideWorking();
|
|
105
|
+
|
|
106
|
+
const custom = ui.custom;
|
|
107
|
+
if (typeof custom !== "function") {
|
|
108
|
+
restoreWorking();
|
|
109
|
+
resolve({ kind: "cancel" });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
94
113
|
const state = createInputsPickerState(fields, prefilled);
|
|
95
114
|
let settled = false;
|
|
96
115
|
let cursorOn = true;
|
|
97
116
|
let cursorTimer: ReturnType<typeof setInterval> | null = null;
|
|
98
117
|
|
|
118
|
+
const settleWithoutDone = (result: InputsPickerResult): void => {
|
|
119
|
+
if (settled) return;
|
|
120
|
+
settled = true;
|
|
121
|
+
if (cursorTimer) clearInterval(cursorTimer);
|
|
122
|
+
cursorTimer = null;
|
|
123
|
+
restoreWorking();
|
|
124
|
+
resolve(result);
|
|
125
|
+
};
|
|
126
|
+
|
|
99
127
|
const factory = (
|
|
100
128
|
tui: PiCustomOverlayFactoryTui,
|
|
101
129
|
_theme: unknown,
|
|
@@ -114,8 +142,12 @@ export function openInputsPicker(
|
|
|
114
142
|
settled = true;
|
|
115
143
|
if (cursorTimer) clearInterval(cursorTimer);
|
|
116
144
|
cursorTimer = null;
|
|
117
|
-
|
|
118
|
-
|
|
145
|
+
try {
|
|
146
|
+
done(undefined);
|
|
147
|
+
} finally {
|
|
148
|
+
restoreWorking();
|
|
149
|
+
resolve(result);
|
|
150
|
+
}
|
|
119
151
|
};
|
|
120
152
|
|
|
121
153
|
return {
|
|
@@ -144,12 +176,7 @@ export function openInputsPicker(
|
|
|
144
176
|
},
|
|
145
177
|
invalidate: () => tui.requestRender?.(),
|
|
146
178
|
dispose: () => {
|
|
147
|
-
|
|
148
|
-
cursorTimer = null;
|
|
149
|
-
if (!settled) {
|
|
150
|
-
settled = true;
|
|
151
|
-
resolve({ kind: "cancel" });
|
|
152
|
-
}
|
|
179
|
+
settleWithoutDone({ kind: "cancel" });
|
|
153
180
|
},
|
|
154
181
|
};
|
|
155
182
|
};
|
|
@@ -157,6 +184,12 @@ export function openInputsPicker(
|
|
|
157
184
|
// overlay: false — picker replaces the editor in-place (see header
|
|
158
185
|
// comment). The host owns geometry/focus; no overlayOptions are
|
|
159
186
|
// forwarded by interactive pi today.
|
|
160
|
-
|
|
187
|
+
try {
|
|
188
|
+
void Promise.resolve(custom(factory, { overlay: false })).catch(() => {
|
|
189
|
+
settleWithoutDone({ kind: "cancel" });
|
|
190
|
+
});
|
|
191
|
+
} catch {
|
|
192
|
+
settleWithoutDone({ kind: "cancel" });
|
|
193
|
+
}
|
|
161
194
|
});
|
|
162
195
|
}
|
package/docs/workflows.md
CHANGED
|
@@ -41,7 +41,7 @@ Use a workflow when a task should be repeatable, inspectable, resumable, or spli
|
|
|
41
41
|
- [Monitor and Control Runs](#monitor-and-control-runs)
|
|
42
42
|
- [Lifecycle Notices and Human Input](#lifecycle-notices-and-human-input)
|
|
43
43
|
- [Direct One-Off Runs](#direct-one-off-runs)
|
|
44
|
-
- [
|
|
44
|
+
- [Fast Inference for Workflow Stages](#fast-inference-for-workflow-stages)
|
|
45
45
|
- [Writing a Workflow](#writing-a-workflow)
|
|
46
46
|
- [Workflow Primitives](#workflow-primitives)
|
|
47
47
|
- [Task and Stage Options](#task-and-stage-options)
|
|
@@ -930,7 +930,11 @@ Direct mode supports top-level/default options and per-task options such as `con
|
|
|
930
930
|
|
|
931
931
|
For large fan-outs, prefer `outputMode: "file-only"` so the parent result contains compact file references instead of full output. Treat intercom payloads from async direct runs as user-visible workflow output.
|
|
932
932
|
|
|
933
|
-
##
|
|
933
|
+
## Fast Inference for Workflow Stages
|
|
934
|
+
|
|
935
|
+
Workflow stages can opt into faster, higher-priority inference on supported providers so multi-stage runs finish sooner. This is currently delivered through Codex fast mode.
|
|
936
|
+
|
|
937
|
+
### Codex fast mode
|
|
934
938
|
|
|
935
939
|
Use `/fast` to manage Codex 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.
|
|
936
940
|
|
|
@@ -1007,7 +1011,7 @@ Builder basics:
|
|
|
1007
1011
|
- `.description(text)` sets the listing text.
|
|
1008
1012
|
- `.input(key, schema)` declares typed user inputs.
|
|
1009
1013
|
- `.worktreeFromInputs({ gitWorktreeDir, baseBranch })` optionally maps input names to workflow-wide reusable Git worktree defaults.
|
|
1010
|
-
- `.output(key, schema
|
|
1014
|
+
- `.output(key, schema)` declares typed outputs that parent workflows receive from `ctx.workflow(childWorkflow, ...)`.
|
|
1011
1015
|
- `.run(async (ctx) => { ... })` defines the workflow body.
|
|
1012
1016
|
- `.compile()` returns the workflow definition for discovery.
|
|
1013
1017
|
|
|
@@ -1035,7 +1039,7 @@ In TypeScript workflow files, `.input(...)` also narrows `ctx.inputs` for better
|
|
|
1035
1039
|
|
|
1036
1040
|
### Outputs
|
|
1037
1041
|
|
|
1038
|
-
Workflow outputs are runtime contracts for completed workflow runs and for parent workflows that call a child with `ctx.workflow(childWorkflow, ...)`. A workflow returns a JSON-serializable object from `.run()`, and `.output(key, schema
|
|
1042
|
+
Workflow outputs are runtime contracts for completed workflow runs and for parent workflows that call a child with `ctx.workflow(childWorkflow, ...)`. A workflow returns a JSON-serializable object from `.run()`, and `.output(key, schema)` documents, validates, and exposes keys from that returned object. Primitives, arrays, `null`, functions, symbols, `undefined` properties, `NaN`, and infinite numbers fail validation.
|
|
1039
1043
|
|
|
1040
1044
|
**Return convention:** outputs are return-object keys. Atomic never infers child workflow outputs from stage names, stage order, or the final assistant message. If a parent should read `child.outputs.foo`, the child workflow's `.run()` must both declare `.output("foo", schema)` and return `{ foo: value }`. `result` is not special and is never added for you: to expose `result`, declare `.output("result", schema)` and return `{ result }` exactly like any other output. Returning a key that is not declared with `.output(...)` fails the run with `atomic-workflows: workflow "<name>" returned undeclared output "<key>"; declare it with .output("<key>", Type....) or remove it from the .run() return`.
|
|
1041
1045
|
|
|
@@ -1065,7 +1069,7 @@ export default defineWorkflow("review-with-summary")
|
|
|
1065
1069
|
.compile();
|
|
1066
1070
|
```
|
|
1067
1071
|
|
|
1068
|
-
There is no automatic `result` output. A workflow exposes exactly the keys it declares with `.output(...)` and returns from `.run()` — nothing more. To expose `result`, declare `.output("result", schema)` and return `{ result }` like any other output. If `.run()` returns a key that was never declared with `.output(...)`, the run fails with `atomic-workflows: workflow "<name>" returned undeclared output "<key>"; declare it with .output("<key>", Type....) or remove it from the .run() return` (
|
|
1072
|
+
There is no automatic `result` output. A workflow exposes exactly the keys it declares with `.output(...)` and returns from `.run()` — nothing more. To expose `result`, declare `.output("result", schema)` and return `{ result }` like any other output. If `.run()` returns a key that was never declared with `.output(...)`, the run fails with `atomic-workflows: workflow "<name>" returned undeclared output "<key>"; declare it with .output("<key>", Type....) or remove it from the .run() return` (for a child workflow call, `<name>` is the child's own name, and the parent surfaces the failure through the child-failure wrapper `atomic-workflows: child workflow "<childName>" (<displayName>) failed with status failed: ...`).
|
|
1069
1073
|
|
|
1070
1074
|
Outputs are declared with TypeBox `Type.*` schemas passed to `.output(key, schema)`. **Prefer precise schemas.** A precise schema gives a precise `Static<>` type for the `.run()` return and for any parent reading `child.outputs`, and it makes runtime validation enforce the real shape instead of waving values through. Reach for `Type.Unknown()`, `Type.Any()`, `Type.Array(Type.Unknown())`, or `Type.Object({}, { additionalProperties: true })` only for genuinely dynamic data whose shape you cannot know ahead of time.
|
|
1071
1075
|
|
|
@@ -1109,16 +1113,16 @@ The same rule applies to inputs: `.input("counts", Type.Array(Type.Number()))` m
|
|
|
1109
1113
|
|
|
1110
1114
|
#### `Type.Unsafe<T>()` escape hatch for deeply-nested values
|
|
1111
1115
|
|
|
1112
|
-
When you already have a precise TypeScript
|
|
1116
|
+
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:
|
|
1113
1117
|
|
|
1114
1118
|
```ts
|
|
1115
1119
|
import { defineWorkflow, Type } from "@bastani/workflows";
|
|
1116
1120
|
|
|
1117
|
-
|
|
1121
|
+
type ResearchPacket = {
|
|
1118
1122
|
readonly topic: string;
|
|
1119
1123
|
readonly score: number;
|
|
1120
1124
|
readonly sections: readonly { readonly heading: string; readonly body: string }[];
|
|
1121
|
-
}
|
|
1125
|
+
};
|
|
1122
1126
|
|
|
1123
1127
|
export default defineWorkflow("research-packet")
|
|
1124
1128
|
.input("topic", Type.String())
|
|
@@ -1233,7 +1237,7 @@ export default defineWorkflow("research-then-implement")
|
|
|
1233
1237
|
description: "Use goal for bounded changes or Ralph for broad spec-to-PR work.",
|
|
1234
1238
|
}),
|
|
1235
1239
|
)
|
|
1236
|
-
.output("research_doc_path", Type.String({ description: "Path to the deep-research document used for implementation." }))
|
|
1240
|
+
.output("research_doc_path", Type.Optional(Type.String({ description: "Path to the deep-research document used for implementation." })))
|
|
1237
1241
|
.output("runner", Type.String({ description: "Which nested runner executed: \"goal\" or \"ralph\"." }))
|
|
1238
1242
|
// Genuinely dynamic: the nested runner (goal vs ralph) is chosen at runtime and
|
|
1239
1243
|
// each exposes a different declared output shape, so a loose object is appropriate here.
|
|
@@ -1304,7 +1308,7 @@ child.outputs.summary; // declared by sharedResearch.output("summary", ...)
|
|
|
1304
1308
|
child.outputs.sources; // declared by sharedResearch.output("sources", ...)
|
|
1305
1309
|
```
|
|
1306
1310
|
|
|
1307
|
-
A child exposes exactly its declared outputs — the keys it declared with `.output(...)` and returned from `.run()`. There are no implicit outputs and no raw return-object passthrough. If `.run()` returns a key that was not declared with `.output(...)`, the child
|
|
1311
|
+
A child exposes exactly its declared outputs — the keys it declared with `.output(...)` and returned from `.run()`. There are no implicit outputs and no raw return-object passthrough. If `.run()` returns a key that was not declared with `.output(...)`, the child run fails with `atomic-workflows: workflow "<childName>" returned undeclared output "<key>"; declare it with .output("<key>", Type....) or remove it from the .run() return`, and the parent surfaces that failure through the wrapper `atomic-workflows: child workflow "<childName>" (<displayName>) failed with status failed: ...`. A child with no declared outputs therefore exposes no outputs. Missing required outputs, schema type mismatches, and non-JSON-serializable returned values fail the child workflow call before the parent continues.
|
|
1308
1312
|
|
|
1309
1313
|
Only compiled workflow definitions can be passed to `ctx.workflow(...)`. Import reusable workflows with TypeScript `import` statements first; use `/workflow` names such as `goal` only for launching named runs, not as `ctx.workflow(...)` arguments. If a module is missing or does not export a compiled workflow definition, workflow discovery fails when loading that module. Nested child workflows count against `maxDepth` (default `4` total workflow levels).
|
|
1310
1314
|
|