@boardwalk-labs/workflow 0.1.0
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/LICENSE +18 -0
- package/README.md +51 -0
- package/dist/events.d.ts +191 -0
- package/dist/events.js +213 -0
- package/dist/extract.d.ts +27 -0
- package/dist/extract.js +192 -0
- package/dist/host.d.ts +62 -0
- package/dist/host.js +70 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +106 -0
- package/dist/manifest.d.ts +143 -0
- package/dist/manifest.js +227 -0
- package/dist/meta.d.ts +164 -0
- package/dist/meta.js +8 -0
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.js +6 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.js +2 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Boardwalk
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
6
|
+
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
12
|
+
portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
15
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
16
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
18
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @boardwalk-labs/workflow
|
|
2
|
+
|
|
3
|
+
Author **Boardwalk workflows** in plain TypeScript — agent loops, schedules, durable sleeps, and cross-workflow composition, in a single program file that runs identically on your laptop, your own server, or the hosted Boardwalk platform.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { agent, output, secrets, type WorkflowMeta } from "@boardwalk-labs/workflow";
|
|
7
|
+
|
|
8
|
+
export const meta = {
|
|
9
|
+
name: "morning-digest",
|
|
10
|
+
description: "Summarize my open issues every weekday at 9am",
|
|
11
|
+
triggers: [{ kind: "cron", expr: "0 9 * * 1-5" }],
|
|
12
|
+
secrets: [{ name: "GITHUB_TOKEN" }],
|
|
13
|
+
} satisfies WorkflowMeta;
|
|
14
|
+
|
|
15
|
+
const token = await secrets.get("GITHUB_TOKEN");
|
|
16
|
+
const issues = await fetch("https://api.github.com/issues", {
|
|
17
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
18
|
+
}).then((r) => r.text());
|
|
19
|
+
|
|
20
|
+
const summary = await agent(`Summarize for a morning digest:\n${issues}`);
|
|
21
|
+
output(summary);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
A workflow is **a script**: the `meta` export is a **pure literal** (engines derive the manifest from it statically, without executing your code), and the module body is the program — importing the file is running it. Top-level `await` is the norm; `output(value)` declares the result. Ordinary TypeScript throughout: any import, any control flow, any npm dependency.
|
|
25
|
+
|
|
26
|
+
## What's in this package
|
|
27
|
+
|
|
28
|
+
| Import | What it is |
|
|
29
|
+
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
30
|
+
| `@boardwalk-labs/workflow` | The author API: `agent()`, `sleep()`, `workflows.call()`, `secrets.get()`, `artifacts.write()`, `parallel()`, `input` / `output()` / `config`, `Phase()` — plus the manifest schema and run-event wire format |
|
|
31
|
+
| `@boardwalk-labs/workflow/runtime` | The **engine-facing** API: install a `WorkflowHost` before evaluating a program. Authors never import this |
|
|
32
|
+
| `@boardwalk-labs/workflow/extract` | Static `meta` → manifest extraction (AST-based, never executes the program). Used by engines and tooling |
|
|
33
|
+
|
|
34
|
+
## The primitives, in one minute
|
|
35
|
+
|
|
36
|
+
- **`agent(prompt, opts?)`** — run an agent loop and get its final text (or `schema`-validated JSON). `model` is optional: name one explicitly, or let the engine resolve it. Loops can use **tools** (built-in or program-defined), **MCP servers**, **skills**, and **memory** — each brought **per call** on `agent()`; the manifest declares none of them.
|
|
37
|
+
- **`sleep(ms | { until })`** — durable wait; the run holds, locals survive.
|
|
38
|
+
- **`workflows.call(name, input)`** — durably invoke another workflow and await its result; idempotent across restarts. `workflows.run` is the fire-and-forget sibling.
|
|
39
|
+
- **`secrets.get(name)`** — read a secret declared in `meta.secrets`. Resolved from your `.env` locally, from the encrypted vault on hosted Boardwalk. Secret values never reach model context — the SDK contract requires engines to redact them.
|
|
40
|
+
- **`output(value)`** — declare the run's result.
|
|
41
|
+
- **Memory = a persistent directory, per agent.** `agent(prompt, { memory: "memory/triager" })` names any workspace-relative directory; the engine auto-persists it across runs — no declaration needed. The loop gets read/write file tools scoped to it, and your code can read and write the same files. (`workspace.persist` is the separate knob for non-memory state your program manages directly.)
|
|
42
|
+
|
|
43
|
+
## Where workflows run
|
|
44
|
+
|
|
45
|
+
One file, three engines: `boardwalk dev` (run it now, locally, no account), the self-hosted Boardwalk engine (your server), or [the Boardwalk platform](https://boardwalk.sh) (`boardwalk deploy` — hosted, scheduled, with automatic model routing). The same manifest schema and event stream everywhere; engine differences are limited to documented resolution behavior.
|
|
46
|
+
|
|
47
|
+
The full authoring contract — every primitive, the manifest field inventory, and the run-event wire format — is in [`SPEC.md`](./SPEC.md).
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Cursor stride per turn: `cursor = turnNumber * STRIDE + seq`. seq is 1-based per turn. */
|
|
3
|
+
export declare const TURN_CURSOR_STRIDE = 1000000;
|
|
4
|
+
/** Compute the run-global cursor for an event. `turnNumber` is 0-based; `seq` is 1-based. */
|
|
5
|
+
export declare function makeCursor(turnNumber: number, seq: number): number;
|
|
6
|
+
export declare const tokenUsageSchema: z.ZodObject<{
|
|
7
|
+
inputTokens: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
cacheCreationTokens: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
cacheReadTokens: z.ZodOptional<z.ZodNumber>;
|
|
11
|
+
totalTokens: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
}, z.core.$strict>;
|
|
13
|
+
export type TokenUsage = z.infer<typeof tokenUsageSchema>;
|
|
14
|
+
export declare const toolReturnSchema: z.ZodObject<{
|
|
15
|
+
kind: z.ZodOptional<z.ZodString>;
|
|
16
|
+
humanSummary: z.ZodOptional<z.ZodString>;
|
|
17
|
+
data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
18
|
+
}, z.core.$strict>;
|
|
19
|
+
export type ToolReturn = z.infer<typeof toolReturnSchema>;
|
|
20
|
+
declare const runStatusValues: readonly ["queued", "pending", "running", "completed", "failed", "cancelled", "cancelling"];
|
|
21
|
+
export declare const runEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
22
|
+
kind: z.ZodLiteral<"run_status">;
|
|
23
|
+
status: z.ZodEnum<{
|
|
24
|
+
queued: "queued";
|
|
25
|
+
pending: "pending";
|
|
26
|
+
running: "running";
|
|
27
|
+
completed: "completed";
|
|
28
|
+
failed: "failed";
|
|
29
|
+
cancelled: "cancelled";
|
|
30
|
+
cancelling: "cancelling";
|
|
31
|
+
}>;
|
|
32
|
+
error: z.ZodOptional<z.ZodObject<{
|
|
33
|
+
code: z.ZodString;
|
|
34
|
+
message: z.ZodString;
|
|
35
|
+
}, z.core.$strict>>;
|
|
36
|
+
runId: z.ZodString;
|
|
37
|
+
turnId: z.ZodString;
|
|
38
|
+
seq: z.ZodNumber;
|
|
39
|
+
t: z.ZodNumber;
|
|
40
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
41
|
+
kind: z.ZodLiteral<"phase">;
|
|
42
|
+
name: z.ZodString;
|
|
43
|
+
id: z.ZodString;
|
|
44
|
+
runId: z.ZodString;
|
|
45
|
+
turnId: z.ZodString;
|
|
46
|
+
seq: z.ZodNumber;
|
|
47
|
+
t: z.ZodNumber;
|
|
48
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
49
|
+
kind: z.ZodLiteral<"output">;
|
|
50
|
+
value: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
51
|
+
runId: z.ZodString;
|
|
52
|
+
turnId: z.ZodString;
|
|
53
|
+
seq: z.ZodNumber;
|
|
54
|
+
t: z.ZodNumber;
|
|
55
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
56
|
+
kind: z.ZodLiteral<"program_output">;
|
|
57
|
+
stream: z.ZodEnum<{
|
|
58
|
+
stdout: "stdout";
|
|
59
|
+
stderr: "stderr";
|
|
60
|
+
}>;
|
|
61
|
+
text: z.ZodString;
|
|
62
|
+
runId: z.ZodString;
|
|
63
|
+
turnId: z.ZodString;
|
|
64
|
+
seq: z.ZodNumber;
|
|
65
|
+
t: z.ZodNumber;
|
|
66
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
67
|
+
kind: z.ZodLiteral<"turn_started">;
|
|
68
|
+
runId: z.ZodString;
|
|
69
|
+
turnId: z.ZodString;
|
|
70
|
+
seq: z.ZodNumber;
|
|
71
|
+
t: z.ZodNumber;
|
|
72
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
73
|
+
kind: z.ZodLiteral<"turn_ended">;
|
|
74
|
+
reason: z.ZodEnum<{
|
|
75
|
+
error: "error";
|
|
76
|
+
cancelled: "cancelled";
|
|
77
|
+
complete: "complete";
|
|
78
|
+
}>;
|
|
79
|
+
usage: z.ZodOptional<z.ZodObject<{
|
|
80
|
+
inputTokens: z.ZodOptional<z.ZodNumber>;
|
|
81
|
+
outputTokens: z.ZodOptional<z.ZodNumber>;
|
|
82
|
+
cacheCreationTokens: z.ZodOptional<z.ZodNumber>;
|
|
83
|
+
cacheReadTokens: z.ZodOptional<z.ZodNumber>;
|
|
84
|
+
totalTokens: z.ZodOptional<z.ZodNumber>;
|
|
85
|
+
}, z.core.$strict>>;
|
|
86
|
+
error: z.ZodOptional<z.ZodObject<{
|
|
87
|
+
code: z.ZodString;
|
|
88
|
+
message: z.ZodString;
|
|
89
|
+
}, z.core.$strict>>;
|
|
90
|
+
runId: z.ZodString;
|
|
91
|
+
turnId: z.ZodString;
|
|
92
|
+
seq: z.ZodNumber;
|
|
93
|
+
t: z.ZodNumber;
|
|
94
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
95
|
+
kind: z.ZodLiteral<"text_start">;
|
|
96
|
+
blockId: z.ZodString;
|
|
97
|
+
runId: z.ZodString;
|
|
98
|
+
turnId: z.ZodString;
|
|
99
|
+
seq: z.ZodNumber;
|
|
100
|
+
t: z.ZodNumber;
|
|
101
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
102
|
+
kind: z.ZodLiteral<"text_delta">;
|
|
103
|
+
blockId: z.ZodString;
|
|
104
|
+
text: z.ZodString;
|
|
105
|
+
runId: z.ZodString;
|
|
106
|
+
turnId: z.ZodString;
|
|
107
|
+
seq: z.ZodNumber;
|
|
108
|
+
t: z.ZodNumber;
|
|
109
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
110
|
+
kind: z.ZodLiteral<"text_end">;
|
|
111
|
+
blockId: z.ZodString;
|
|
112
|
+
runId: z.ZodString;
|
|
113
|
+
turnId: z.ZodString;
|
|
114
|
+
seq: z.ZodNumber;
|
|
115
|
+
t: z.ZodNumber;
|
|
116
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
117
|
+
kind: z.ZodLiteral<"tool_call_start">;
|
|
118
|
+
toolCallId: z.ZodString;
|
|
119
|
+
toolName: z.ZodString;
|
|
120
|
+
runId: z.ZodString;
|
|
121
|
+
turnId: z.ZodString;
|
|
122
|
+
seq: z.ZodNumber;
|
|
123
|
+
t: z.ZodNumber;
|
|
124
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
125
|
+
kind: z.ZodLiteral<"tool_call_input_delta">;
|
|
126
|
+
toolCallId: z.ZodString;
|
|
127
|
+
partialJson: z.ZodString;
|
|
128
|
+
runId: z.ZodString;
|
|
129
|
+
turnId: z.ZodString;
|
|
130
|
+
seq: z.ZodNumber;
|
|
131
|
+
t: z.ZodNumber;
|
|
132
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
133
|
+
kind: z.ZodLiteral<"tool_call_input_complete">;
|
|
134
|
+
toolCallId: z.ZodString;
|
|
135
|
+
input: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
136
|
+
runId: z.ZodString;
|
|
137
|
+
turnId: z.ZodString;
|
|
138
|
+
seq: z.ZodNumber;
|
|
139
|
+
t: z.ZodNumber;
|
|
140
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
141
|
+
kind: z.ZodLiteral<"tool_call_executing">;
|
|
142
|
+
toolCallId: z.ZodString;
|
|
143
|
+
runId: z.ZodString;
|
|
144
|
+
turnId: z.ZodString;
|
|
145
|
+
seq: z.ZodNumber;
|
|
146
|
+
t: z.ZodNumber;
|
|
147
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
148
|
+
kind: z.ZodLiteral<"tool_call_result">;
|
|
149
|
+
toolCallId: z.ZodString;
|
|
150
|
+
result: z.ZodObject<{
|
|
151
|
+
kind: z.ZodOptional<z.ZodString>;
|
|
152
|
+
humanSummary: z.ZodOptional<z.ZodString>;
|
|
153
|
+
data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
154
|
+
}, z.core.$strict>;
|
|
155
|
+
runId: z.ZodString;
|
|
156
|
+
turnId: z.ZodString;
|
|
157
|
+
seq: z.ZodNumber;
|
|
158
|
+
t: z.ZodNumber;
|
|
159
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
160
|
+
kind: z.ZodLiteral<"tool_call_error">;
|
|
161
|
+
toolCallId: z.ZodString;
|
|
162
|
+
error: z.ZodObject<{
|
|
163
|
+
code: z.ZodString;
|
|
164
|
+
message: z.ZodString;
|
|
165
|
+
}, z.core.$strict>;
|
|
166
|
+
runId: z.ZodString;
|
|
167
|
+
turnId: z.ZodString;
|
|
168
|
+
seq: z.ZodNumber;
|
|
169
|
+
t: z.ZodNumber;
|
|
170
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
171
|
+
kind: z.ZodLiteral<"reasoning_delta">;
|
|
172
|
+
text: z.ZodString;
|
|
173
|
+
runId: z.ZodString;
|
|
174
|
+
turnId: z.ZodString;
|
|
175
|
+
seq: z.ZodNumber;
|
|
176
|
+
t: z.ZodNumber;
|
|
177
|
+
}, z.core.$strict>], "kind">;
|
|
178
|
+
export type RunEvent = z.infer<typeof runEventSchema>;
|
|
179
|
+
export type RunEventKind = RunEvent["kind"];
|
|
180
|
+
export type EventEnvelope = Pick<RunEvent, "runId" | "turnId" | "seq" | "t">;
|
|
181
|
+
export type RunStatus = (typeof runStatusValues)[number];
|
|
182
|
+
/** Subscription channels. Every event kind maps to exactly one channel. */
|
|
183
|
+
export declare const CHANNELS: readonly ["lifecycle", "phase", "output", "log", "agent"];
|
|
184
|
+
export type Channel = (typeof CHANNELS)[number];
|
|
185
|
+
/** The default subscription — quiet and readable. `verbose` = all of {@link CHANNELS}. */
|
|
186
|
+
export declare const DEFAULT_CHANNELS: readonly Channel[];
|
|
187
|
+
/** The channel an event belongs to. */
|
|
188
|
+
export declare function channelOf(event: Pick<RunEvent, "kind">): Channel;
|
|
189
|
+
/** Server-side subscription filter: does this event belong to one of the subscribed channels? */
|
|
190
|
+
export declare function matchesChannels(event: Pick<RunEvent, "kind">, channels: readonly Channel[]): boolean;
|
|
191
|
+
export {};
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
// The run-event wire format — one typed, ordered stream per run, identical in every engine.
|
|
2
|
+
//
|
|
3
|
+
// Every event carries an envelope (runId, turnId, per-turn 1-based seq, server timestamp) and
|
|
4
|
+
// a RUN-GLOBAL MONOTONIC CURSOR derived from (turn number, seq). Consumers resume from a
|
|
5
|
+
// cursor (SSE `Last-Event-ID` semantics). Every event kind maps to exactly ONE subscription
|
|
6
|
+
// channel; filtering happens server-side, and cursors stay globally consistent so a filtered
|
|
7
|
+
// subscription resumes correctly.
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Cursor
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/** Cursor stride per turn: `cursor = turnNumber * STRIDE + seq`. seq is 1-based per turn. */
|
|
13
|
+
export const TURN_CURSOR_STRIDE = 1_000_000;
|
|
14
|
+
/** Compute the run-global cursor for an event. `turnNumber` is 0-based; `seq` is 1-based. */
|
|
15
|
+
export function makeCursor(turnNumber, seq) {
|
|
16
|
+
if (!Number.isInteger(turnNumber) || turnNumber < 0) {
|
|
17
|
+
throw new RangeError(`turnNumber must be a non-negative integer (got ${String(turnNumber)})`);
|
|
18
|
+
}
|
|
19
|
+
if (!Number.isInteger(seq) || seq < 1 || seq >= TURN_CURSOR_STRIDE) {
|
|
20
|
+
throw new RangeError(`seq must be an integer in [1, ${String(TURN_CURSOR_STRIDE)}) (got ${String(seq)})`);
|
|
21
|
+
}
|
|
22
|
+
return turnNumber * TURN_CURSOR_STRIDE + seq;
|
|
23
|
+
}
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// Envelope + shared shapes
|
|
26
|
+
// ============================================================================
|
|
27
|
+
const envelopeShape = {
|
|
28
|
+
/** The run this event belongs to. */
|
|
29
|
+
runId: z.string().min(1),
|
|
30
|
+
/** Identifies one logical stream segment (a new id per agent turn; run-level frames reuse the run's). */
|
|
31
|
+
turnId: z.string().min(1),
|
|
32
|
+
/** Monotonic 1-based sequence within the turn. */
|
|
33
|
+
seq: z.number().int().min(1),
|
|
34
|
+
/** Server time at emission, ms since epoch. */
|
|
35
|
+
t: z.number().int().nonnegative(),
|
|
36
|
+
};
|
|
37
|
+
export const tokenUsageSchema = z.strictObject({
|
|
38
|
+
inputTokens: z.number().int().nonnegative().optional(),
|
|
39
|
+
outputTokens: z.number().int().nonnegative().optional(),
|
|
40
|
+
cacheCreationTokens: z.number().int().nonnegative().optional(),
|
|
41
|
+
cacheReadTokens: z.number().int().nonnegative().optional(),
|
|
42
|
+
totalTokens: z.number().int().nonnegative().optional(),
|
|
43
|
+
});
|
|
44
|
+
export const toolReturnSchema = z.strictObject({
|
|
45
|
+
/** Opaque discriminator for client rendering. */
|
|
46
|
+
kind: z.string().optional(),
|
|
47
|
+
/** One-sentence summary suitable for a log line. */
|
|
48
|
+
humanSummary: z.string().optional(),
|
|
49
|
+
/** Tool-specific payload. */
|
|
50
|
+
data: z.record(z.string(), z.unknown()).optional(),
|
|
51
|
+
});
|
|
52
|
+
const eventErrorSchema = z.strictObject({
|
|
53
|
+
code: z.string(),
|
|
54
|
+
message: z.string(),
|
|
55
|
+
});
|
|
56
|
+
const jsonValueSchema = z.unknown();
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Event kinds
|
|
59
|
+
// ============================================================================
|
|
60
|
+
const runStatusValues = [
|
|
61
|
+
"queued",
|
|
62
|
+
"pending",
|
|
63
|
+
"running",
|
|
64
|
+
"completed",
|
|
65
|
+
"failed",
|
|
66
|
+
"cancelled",
|
|
67
|
+
"cancelling",
|
|
68
|
+
];
|
|
69
|
+
// -- lifecycle channel --------------------------------------------------------
|
|
70
|
+
const runStatusEvent = z.strictObject({
|
|
71
|
+
...envelopeShape,
|
|
72
|
+
kind: z.literal("run_status"),
|
|
73
|
+
status: z.enum(runStatusValues),
|
|
74
|
+
/** Present on `failed` — why the run failed. */
|
|
75
|
+
error: eventErrorSchema.optional(),
|
|
76
|
+
});
|
|
77
|
+
// -- phase channel -------------------------------------------------------------
|
|
78
|
+
const phaseEvent = z.strictObject({
|
|
79
|
+
...envelopeShape,
|
|
80
|
+
kind: z.literal("phase"),
|
|
81
|
+
name: z.string().min(1),
|
|
82
|
+
/** Stable phase identifier (author-supplied or engine-assigned in marker order). */
|
|
83
|
+
id: z.string().min(1),
|
|
84
|
+
});
|
|
85
|
+
// -- output channel -------------------------------------------------------------
|
|
86
|
+
const outputEvent = z.strictObject({
|
|
87
|
+
...envelopeShape,
|
|
88
|
+
kind: z.literal("output"),
|
|
89
|
+
value: jsonValueSchema,
|
|
90
|
+
});
|
|
91
|
+
// -- log channel ----------------------------------------------------------------
|
|
92
|
+
const programOutputEvent = z.strictObject({
|
|
93
|
+
...envelopeShape,
|
|
94
|
+
kind: z.literal("program_output"),
|
|
95
|
+
stream: z.enum(["stdout", "stderr"]),
|
|
96
|
+
text: z.string(),
|
|
97
|
+
});
|
|
98
|
+
// -- agent channel ----------------------------------------------------------------
|
|
99
|
+
const turnStarted = z.strictObject({ ...envelopeShape, kind: z.literal("turn_started") });
|
|
100
|
+
const turnEnded = z.strictObject({
|
|
101
|
+
...envelopeShape,
|
|
102
|
+
kind: z.literal("turn_ended"),
|
|
103
|
+
reason: z.enum(["complete", "cancelled", "error"]),
|
|
104
|
+
usage: tokenUsageSchema.optional(),
|
|
105
|
+
error: eventErrorSchema.optional(),
|
|
106
|
+
});
|
|
107
|
+
const textStart = z.strictObject({
|
|
108
|
+
...envelopeShape,
|
|
109
|
+
kind: z.literal("text_start"),
|
|
110
|
+
blockId: z.string(),
|
|
111
|
+
});
|
|
112
|
+
const textDelta = z.strictObject({
|
|
113
|
+
...envelopeShape,
|
|
114
|
+
kind: z.literal("text_delta"),
|
|
115
|
+
blockId: z.string(),
|
|
116
|
+
text: z.string(),
|
|
117
|
+
});
|
|
118
|
+
const textEnd = z.strictObject({
|
|
119
|
+
...envelopeShape,
|
|
120
|
+
kind: z.literal("text_end"),
|
|
121
|
+
blockId: z.string(),
|
|
122
|
+
});
|
|
123
|
+
const toolCallStart = z.strictObject({
|
|
124
|
+
...envelopeShape,
|
|
125
|
+
kind: z.literal("tool_call_start"),
|
|
126
|
+
toolCallId: z.string(),
|
|
127
|
+
toolName: z.string(),
|
|
128
|
+
});
|
|
129
|
+
const toolCallInputDelta = z.strictObject({
|
|
130
|
+
...envelopeShape,
|
|
131
|
+
kind: z.literal("tool_call_input_delta"),
|
|
132
|
+
toolCallId: z.string(),
|
|
133
|
+
partialJson: z.string(),
|
|
134
|
+
});
|
|
135
|
+
const toolCallInputComplete = z.strictObject({
|
|
136
|
+
...envelopeShape,
|
|
137
|
+
kind: z.literal("tool_call_input_complete"),
|
|
138
|
+
toolCallId: z.string(),
|
|
139
|
+
input: z.record(z.string(), z.unknown()),
|
|
140
|
+
});
|
|
141
|
+
const toolCallExecuting = z.strictObject({
|
|
142
|
+
...envelopeShape,
|
|
143
|
+
kind: z.literal("tool_call_executing"),
|
|
144
|
+
toolCallId: z.string(),
|
|
145
|
+
});
|
|
146
|
+
const toolCallResult = z.strictObject({
|
|
147
|
+
...envelopeShape,
|
|
148
|
+
kind: z.literal("tool_call_result"),
|
|
149
|
+
toolCallId: z.string(),
|
|
150
|
+
result: toolReturnSchema,
|
|
151
|
+
});
|
|
152
|
+
const toolCallError = z.strictObject({
|
|
153
|
+
...envelopeShape,
|
|
154
|
+
kind: z.literal("tool_call_error"),
|
|
155
|
+
toolCallId: z.string(),
|
|
156
|
+
error: eventErrorSchema,
|
|
157
|
+
});
|
|
158
|
+
const reasoningDelta = z.strictObject({
|
|
159
|
+
...envelopeShape,
|
|
160
|
+
kind: z.literal("reasoning_delta"),
|
|
161
|
+
text: z.string(),
|
|
162
|
+
});
|
|
163
|
+
export const runEventSchema = z.discriminatedUnion("kind", [
|
|
164
|
+
runStatusEvent,
|
|
165
|
+
phaseEvent,
|
|
166
|
+
outputEvent,
|
|
167
|
+
programOutputEvent,
|
|
168
|
+
turnStarted,
|
|
169
|
+
turnEnded,
|
|
170
|
+
textStart,
|
|
171
|
+
textDelta,
|
|
172
|
+
textEnd,
|
|
173
|
+
toolCallStart,
|
|
174
|
+
toolCallInputDelta,
|
|
175
|
+
toolCallInputComplete,
|
|
176
|
+
toolCallExecuting,
|
|
177
|
+
toolCallResult,
|
|
178
|
+
toolCallError,
|
|
179
|
+
reasoningDelta,
|
|
180
|
+
]);
|
|
181
|
+
// ============================================================================
|
|
182
|
+
// Channels
|
|
183
|
+
// ============================================================================
|
|
184
|
+
/** Subscription channels. Every event kind maps to exactly one channel. */
|
|
185
|
+
export const CHANNELS = ["lifecycle", "phase", "output", "log", "agent"];
|
|
186
|
+
/** The default subscription — quiet and readable. `verbose` = all of {@link CHANNELS}. */
|
|
187
|
+
export const DEFAULT_CHANNELS = ["lifecycle", "phase", "output"];
|
|
188
|
+
const KIND_TO_CHANNEL = {
|
|
189
|
+
run_status: "lifecycle",
|
|
190
|
+
phase: "phase",
|
|
191
|
+
output: "output",
|
|
192
|
+
program_output: "log",
|
|
193
|
+
turn_started: "agent",
|
|
194
|
+
turn_ended: "agent",
|
|
195
|
+
text_start: "agent",
|
|
196
|
+
text_delta: "agent",
|
|
197
|
+
text_end: "agent",
|
|
198
|
+
tool_call_start: "agent",
|
|
199
|
+
tool_call_input_delta: "agent",
|
|
200
|
+
tool_call_input_complete: "agent",
|
|
201
|
+
tool_call_executing: "agent",
|
|
202
|
+
tool_call_result: "agent",
|
|
203
|
+
tool_call_error: "agent",
|
|
204
|
+
reasoning_delta: "agent",
|
|
205
|
+
};
|
|
206
|
+
/** The channel an event belongs to. */
|
|
207
|
+
export function channelOf(event) {
|
|
208
|
+
return KIND_TO_CHANNEL[event.kind];
|
|
209
|
+
}
|
|
210
|
+
/** Server-side subscription filter: does this event belong to one of the subscribed channels? */
|
|
211
|
+
export function matchesChannels(event, channels) {
|
|
212
|
+
return channels.includes(channelOf(event));
|
|
213
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type WorkflowManifest } from "./manifest.js";
|
|
2
|
+
/** Thrown when a program's `meta` cannot be statically extracted as a pure literal. */
|
|
3
|
+
export declare class MetaExtractionError extends Error {
|
|
4
|
+
constructor(message: string);
|
|
5
|
+
}
|
|
6
|
+
export interface MetaExtractionOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Logical file name. Drives TS-vs-JS parsing (the `.ts`/`.js` extension) and the
|
|
9
|
+
* `file:line:col` prefix in error messages. Defaults to `index.ts`.
|
|
10
|
+
*/
|
|
11
|
+
fileName?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Statically extract the raw `meta` pure-literal object from a workflow program's source.
|
|
15
|
+
*
|
|
16
|
+
* Returns the plain JS object exactly as written (no schema defaults applied). Throws
|
|
17
|
+
* {@link MetaExtractionError} when there is no `meta` declaration, when it is not an object
|
|
18
|
+
* literal, or when any part of it is not a pure literal.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractMetaLiteral(source: string, options?: MetaExtractionOptions): Record<string, unknown>;
|
|
21
|
+
/**
|
|
22
|
+
* Statically extract `meta` and validate it against the manifest schema, returning the
|
|
23
|
+
* fully-defaulted, validated manifest (the contract every engine consumes). Throws
|
|
24
|
+
* {@link MetaExtractionError} on an unextractable literal, or `MetaValidationError` (from
|
|
25
|
+
* `validateMeta`) on a schema violation.
|
|
26
|
+
*/
|
|
27
|
+
export declare function extractManifest(source: string, options?: MetaExtractionOptions): WorkflowManifest;
|