@darqlabs/curator-sdk 0.1.6 → 0.2.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/README.md CHANGED
@@ -304,12 +304,30 @@ Same send, streamed. Yields events as they arrive:
304
304
  | `state` | `metadata`, complete structured snapshot for this turn | no |
305
305
  | `plan_progress` | `phase`, `message`, `completed`/`total`, `labels` | no |
306
306
  | `tool_call.started` / `tool_call.completed` | tool call id / name | no |
307
+ | `tool_call.failed` | tool call id / name, `reason` | no |
307
308
  | `done` | `message` (with `message.metadata`) | yes |
308
309
  | `error` | `failureClass`, `message` | yes |
309
310
  | `paused_for_approval` | `approval` | yes |
310
311
 
311
312
  `state` fires on every turn the agent reports structured state, see [Structured results](#structured-results-metadata).
312
313
 
314
+ Tool calls have a symmetrical lifecycle: every `tool_call.started` is followed by exactly one `tool_call.completed` or `tool_call.failed` carrying the same `toolCallId`, so you can always close the right pending indicator:
315
+
316
+ ```ts
317
+ for await (const event of chat.stream("Find the Q3 contract")) {
318
+ if (event.type === "tool_call.started") pending.add(event.toolCallId)
319
+ else if (event.type === "tool_call.completed") pending.delete(event.toolCallId)
320
+ else if (event.type === "tool_call.failed") {
321
+ pending.delete(event.toolCallId)
322
+ setNotice(event.reason === "policy_denied"
323
+ ? "This action wasn't allowed."
324
+ : "The tool couldn't complete.")
325
+ }
326
+ }
327
+ ```
328
+
329
+ `tool_call.failed` is **not** terminal — a tool failing doesn't end the run, and the agent usually keeps working. Its `reason` is a closed vocabulary (`policy_denied` for a policy or budget gate, `tool_error` for a tool that ran and failed); there is deliberately no free-text detail, since the underlying reason can reference internal policies and user identifiers. Treat an unrecognised `reason` as a generic failure — more may be added.
330
+
313
331
  `plan_progress` fires for **planned** agents (ones that decompose a request into a task plan). It reports coarse progress through `phase` (`planning` → `executing` → `finalizing`) with a user-facing `message` (e.g. "Working on 3 tasks") and, once tasks are running, `completed`/`total`. A planned run is otherwise quiet until the finalizer streams `delta`s, so handle this to show progress while it works:
314
332
 
315
333
  ```ts
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Flow-graph authoring + run surface for the Curator SDK.
3
+ *
4
+ * import { Curator, reducers, agentNode, toolNode, transformNode, edge, branch, when } from "@darqlabs/curator-sdk"
5
+ *
6
+ * const draft = curator.defineGraph({
7
+ * name: "copy-loop",
8
+ * state: { messages: reducers.append(), draft: reducers.lastValue(), revisions: reducers.counter() },
9
+ * nodes: {
10
+ * write: agentNode("copywriter"),
11
+ * review: toolNode("api.grammar.check"),
12
+ * score: transformNode({ transformId: "tf_quality", version: 1 }),
13
+ * },
14
+ * entry: "write",
15
+ * edges: [
16
+ * edge("write", "review"),
17
+ * edge("review", "score"),
18
+ * branch("score", when("draft_ok", "==", true).then("END").else("write")),
19
+ * ],
20
+ * recursionLimit: 12,
21
+ * })
22
+ *
23
+ * const graph = await curator.graphs.create(draft) // POST /api/graphs
24
+ * await graph.deploy({ expectedDraftRevision: 1 }) // versioned snapshot
25
+ *
26
+ * // run + stream node/state events
27
+ * for await (const ev of graph.stream({ messages: [] })) {
28
+ * if (ev.type === "node.completed") console.log("done:", ev.node)
29
+ * if (ev.type === "done") console.log("final:", ev.state)
30
+ * }
31
+ *
32
+ * These graph types are SDK-owned (the SDK is published and cannot depend on the
33
+ * private @darq/shared-types); they mirror the backend spec shape exactly so the
34
+ * assembled JSON validates + compiles server-side.
35
+ */
36
+ import type { HttpClient } from "./http.js";
37
+ import type { Environment } from "./types.js";
38
+ export interface TransformRef {
39
+ transformId: string;
40
+ version: number;
41
+ autoUpdate?: boolean;
42
+ }
43
+ export type ReducerKind = "append" | "lastValue" | "merge" | "counter" | "custom";
44
+ export interface ChannelDescriptor {
45
+ reducer: ReducerKind;
46
+ reducerRef?: TransformRef;
47
+ initialValue?: unknown;
48
+ description?: string;
49
+ }
50
+ export type JoinPolicy = "all" | "any";
51
+ export interface NodeCommon {
52
+ inputChannels?: string[];
53
+ inputTransform?: TransformRef;
54
+ outputTransform?: TransformRef;
55
+ join?: JoinPolicy;
56
+ timeout?: number;
57
+ maxVisits?: number;
58
+ label?: string;
59
+ }
60
+ export interface AgentGraphNode extends NodeCommon {
61
+ type: "agent";
62
+ agentId: string;
63
+ instructions?: string;
64
+ }
65
+ export interface ToolGraphNode extends NodeCommon {
66
+ type: "tool";
67
+ action: string;
68
+ parameters?: Record<string, unknown>;
69
+ }
70
+ export interface TransformGraphNode extends NodeCommon {
71
+ type: "transform";
72
+ transform: TransformRef;
73
+ }
74
+ /**
75
+ * Fans its inline `body` out over the elements of the `over` channel (an array),
76
+ * appending each body output into `collect`. Body params/instructions can reference
77
+ * the current element via `{{item}}` / `{{item.field}}` / `{{index}}`. Body may be
78
+ * an agent/tool/transform node — not another forEach (no nesting).
79
+ */
80
+ export interface ForEachGraphNode extends NodeCommon {
81
+ type: "forEach";
82
+ over: string;
83
+ body: GraphNode;
84
+ collect?: string;
85
+ itemAs?: string;
86
+ }
87
+ export type GraphNode = AgentGraphNode | ToolGraphNode | TransformGraphNode | ForEachGraphNode;
88
+ export type BranchOp = "==" | "!=" | ">" | ">=" | "<" | "<=" | "in" | "not_in" | "exists" | "truthy" | "falsy";
89
+ export interface BranchPredicate {
90
+ channel: string;
91
+ op: BranchOp;
92
+ value?: unknown;
93
+ }
94
+ export type Branch = {
95
+ kind: "predicate";
96
+ predicate: BranchPredicate;
97
+ then: string;
98
+ else: string;
99
+ } | {
100
+ kind: "router";
101
+ transform: TransformRef;
102
+ targets: string[];
103
+ };
104
+ export interface StaticEdge {
105
+ kind: "static";
106
+ from: string;
107
+ to: string;
108
+ }
109
+ export interface ConditionalEdge {
110
+ kind: "conditional";
111
+ from: string;
112
+ branch: Branch;
113
+ }
114
+ export type GraphEdge = StaticEdge | ConditionalEdge;
115
+ export interface GraphSpec {
116
+ state: Record<string, ChannelDescriptor>;
117
+ nodes: Record<string, GraphNode>;
118
+ entry: string;
119
+ edges: GraphEdge[];
120
+ recursionLimit?: number;
121
+ toolFailureMode?: "strict" | "best_effort";
122
+ }
123
+ /** The `END` sentinel target. */
124
+ export declare const GRAPH_END: "END";
125
+ export type GraphRunStatus = "running" | "paused" | "succeeded" | "failed" | "cancelled";
126
+ export type GraphNodeRunStatus = "pending" | "ready" | "running" | "succeeded" | "failed" | "skipped";
127
+ export interface GraphNodeRunView {
128
+ key: string;
129
+ type: GraphNode["type"];
130
+ status: GraphNodeRunStatus;
131
+ visits: number;
132
+ childRunId?: string;
133
+ }
134
+ export interface GraphRunView {
135
+ runId: string;
136
+ graphId: string;
137
+ versionId: string;
138
+ status: GraphRunStatus;
139
+ spec: GraphSpec;
140
+ nodes: GraphNodeRunView[];
141
+ hops: number;
142
+ recursionLimit: number;
143
+ state: Record<string, unknown>;
144
+ errorCode?: string;
145
+ errorMessage?: string;
146
+ }
147
+ /** Streamed node/state lifecycle, derived from run-view snapshots. */
148
+ export type GraphRunEvent = {
149
+ type: "run.started";
150
+ runId: string;
151
+ } | {
152
+ type: "node.started";
153
+ node: string;
154
+ visit: number;
155
+ } | {
156
+ type: "node.completed";
157
+ node: string;
158
+ } | {
159
+ type: "node.failed";
160
+ node: string;
161
+ } | {
162
+ type: "state.delta";
163
+ channels: Record<string, unknown>;
164
+ } | {
165
+ type: "done";
166
+ status: GraphRunStatus;
167
+ state: Record<string, unknown>;
168
+ } | {
169
+ type: "error";
170
+ message: string;
171
+ };
172
+ export declare const reducers: {
173
+ append: () => ChannelDescriptor;
174
+ lastValue: () => ChannelDescriptor;
175
+ merge: () => ChannelDescriptor;
176
+ counter: () => ChannelDescriptor;
177
+ /** A custom fold `(prev, update) => next`, run as a sandboxed transform. */
178
+ custom: (transform: TransformRef) => ChannelDescriptor;
179
+ };
180
+ export declare function agentNode(agentId: string, opts?: Omit<AgentGraphNode, "type" | "agentId">): AgentGraphNode;
181
+ export declare function toolNode(action: string, opts?: Omit<ToolGraphNode, "type" | "action">): ToolGraphNode;
182
+ export declare function transformNode(transform: TransformRef, opts?: Omit<TransformGraphNode, "type" | "transform">): TransformGraphNode;
183
+ /**
184
+ * A `forEach` (map) node: run `body` per element of the `over` channel, appending
185
+ * each output into `collect`. Body params can reference the element via
186
+ * `{{item}}` / `{{item.field}}` / `{{index}}`.
187
+ */
188
+ export declare function forEachNode(over: string, body: GraphNode, opts?: Omit<ForEachGraphNode, "type" | "over" | "body">): ForEachGraphNode;
189
+ export declare function edge(from: string, to: string): StaticEdge;
190
+ /** Attach a conditional edge to `from`. Pass a predicate builder or a router. */
191
+ export declare function branch(from: string, spec: Branch | PredicateBuilder): ConditionalEdge;
192
+ /** A sandboxed router transform that returns a next-node key (or END). */
193
+ export declare function router(transform: TransformRef, targets: string[]): Branch;
194
+ /**
195
+ * Fluent declarative predicate: `when("draft_ok", "==", true).then("END").else("write")`.
196
+ * Unary ops (`exists`/`truthy`/`falsy`) omit the value.
197
+ */
198
+ export declare function when(channel: string, op: BranchOp, value?: unknown): PredicateBuilder;
199
+ export declare class PredicateBuilder {
200
+ private readonly predicate;
201
+ private thenKey?;
202
+ private elseKey?;
203
+ constructor(predicate: BranchPredicate);
204
+ then(nodeKey: string): this;
205
+ else(nodeKey: string): this;
206
+ /** @internal */
207
+ build(): Branch;
208
+ }
209
+ export interface DefineGraphConfig {
210
+ name: string;
211
+ description?: string;
212
+ state: Record<string, ChannelDescriptor>;
213
+ nodes: Record<string, GraphNode>;
214
+ entry: string;
215
+ edges: GraphEdge[];
216
+ recursionLimit?: number;
217
+ toolFailureMode?: "strict" | "best_effort";
218
+ tags?: string[];
219
+ }
220
+ /** A locally-assembled graph, ready to `curator.graphs.create(...)`. */
221
+ export interface GraphDraft {
222
+ name: string;
223
+ description?: string;
224
+ tags?: string[];
225
+ spec: GraphSpec;
226
+ }
227
+ /** Assemble a serializable graph spec locally. Pure — no network. */
228
+ export declare function defineGraph(config: DefineGraphConfig): GraphDraft;
229
+ export interface GraphRunOptions {
230
+ /** Target environment for the deployed spec (default: the client default). */
231
+ environment?: Environment;
232
+ /** Run the draft spec instead of the live deployed version. */
233
+ useDraft?: boolean;
234
+ /** Sync-wait timeout, in ms (server-capped). Only used by `run` (non-stream). */
235
+ timeoutMs?: number;
236
+ }
237
+ export interface GraphRunResult {
238
+ runId: string;
239
+ status: GraphRunStatus | "timeout";
240
+ state?: Record<string, unknown>;
241
+ errorCode?: string;
242
+ errorMessage?: string;
243
+ }
244
+ export interface DeployGraphOptions {
245
+ environment?: Environment;
246
+ slug?: string;
247
+ changeSummary?: string;
248
+ /** Optimistic-concurrency token — the draft revision you reviewed. */
249
+ expectedDraftRevision: number;
250
+ }
251
+ /** A handle to one graph resource, addressed by its `graph_id`. */
252
+ export declare class Graph {
253
+ private readonly http;
254
+ private readonly baseUrl;
255
+ private readonly defaultTimeoutMs;
256
+ readonly graphId: string;
257
+ private readonly defaultEnvironment;
258
+ /** @internal */
259
+ constructor(http: HttpClient, baseUrl: string, defaultTimeoutMs: number, graphId: string, defaultEnvironment: Environment);
260
+ private get base();
261
+ /** Deploy the current draft as an immutable versioned snapshot. */
262
+ deploy(opts: DeployGraphOptions): Promise<{
263
+ versionId: string;
264
+ versionNumber: number;
265
+ slug: string;
266
+ }>;
267
+ /**
268
+ * Start a run and, unless `{ wait: false }`, block until it settles.
269
+ * Returns the terminal channel state (or `status: "timeout"` if the wait
270
+ * expires — the run keeps going server-side).
271
+ */
272
+ run(input: Record<string, unknown>, opts?: GraphRunOptions & {
273
+ wait?: boolean;
274
+ }): Promise<GraphRunResult>;
275
+ /**
276
+ * Start a run and stream node/state lifecycle events until it settles.
277
+ * Events (`run.started` / `node.started` / `node.completed` / `node.failed`
278
+ * / `state.delta` / `done` / `error`) are derived from the server's run-view
279
+ * snapshot stream. Breaking out of the loop closes the connection; the run
280
+ * continues server-side.
281
+ */
282
+ stream(input: Record<string, unknown>, opts?: GraphRunOptions): AsyncGenerator<GraphRunEvent, void, void>;
283
+ /** Fetch the read-only run projection (spec + per-node status + state). */
284
+ runStatus(runId: string): Promise<GraphRunView>;
285
+ /** List this graph's version snapshots (newest first). */
286
+ versions(): Promise<Array<{
287
+ versionId: string;
288
+ versionNumber: number;
289
+ createdAt: string;
290
+ }>>;
291
+ }
292
+ /** Collection surface for creating + addressing graphs. */
293
+ export declare class Graphs {
294
+ private readonly http;
295
+ private readonly baseUrl;
296
+ private readonly defaultTimeoutMs;
297
+ private readonly defaultEnvironment;
298
+ /** @internal */
299
+ constructor(http: HttpClient, baseUrl: string, defaultTimeoutMs: number, defaultEnvironment: Environment);
300
+ /** Create a new graph resource from a draft (or inline config). Returns a
301
+ * handle bound to the new `graph_id`. */
302
+ create(draft: GraphDraft | DefineGraphConfig): Promise<Graph>;
303
+ /** Reattach to an existing graph by id. */
304
+ get(graphId: string): Graph;
305
+ private handle;
306
+ }
307
+ //# sourceMappingURL=graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/graph.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,336 @@
1
+ "use strict";
2
+ /**
3
+ * Flow-graph authoring + run surface for the Curator SDK.
4
+ *
5
+ * import { Curator, reducers, agentNode, toolNode, transformNode, edge, branch, when } from "@darqlabs/curator-sdk"
6
+ *
7
+ * const draft = curator.defineGraph({
8
+ * name: "copy-loop",
9
+ * state: { messages: reducers.append(), draft: reducers.lastValue(), revisions: reducers.counter() },
10
+ * nodes: {
11
+ * write: agentNode("copywriter"),
12
+ * review: toolNode("api.grammar.check"),
13
+ * score: transformNode({ transformId: "tf_quality", version: 1 }),
14
+ * },
15
+ * entry: "write",
16
+ * edges: [
17
+ * edge("write", "review"),
18
+ * edge("review", "score"),
19
+ * branch("score", when("draft_ok", "==", true).then("END").else("write")),
20
+ * ],
21
+ * recursionLimit: 12,
22
+ * })
23
+ *
24
+ * const graph = await curator.graphs.create(draft) // POST /api/graphs
25
+ * await graph.deploy({ expectedDraftRevision: 1 }) // versioned snapshot
26
+ *
27
+ * // run + stream node/state events
28
+ * for await (const ev of graph.stream({ messages: [] })) {
29
+ * if (ev.type === "node.completed") console.log("done:", ev.node)
30
+ * if (ev.type === "done") console.log("final:", ev.state)
31
+ * }
32
+ *
33
+ * These graph types are SDK-owned (the SDK is published and cannot depend on the
34
+ * private @darq/shared-types); they mirror the backend spec shape exactly so the
35
+ * assembled JSON validates + compiles server-side.
36
+ */
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.Graphs = exports.Graph = exports.PredicateBuilder = exports.reducers = exports.GRAPH_END = void 0;
39
+ exports.agentNode = agentNode;
40
+ exports.toolNode = toolNode;
41
+ exports.transformNode = transformNode;
42
+ exports.forEachNode = forEachNode;
43
+ exports.edge = edge;
44
+ exports.branch = branch;
45
+ exports.router = router;
46
+ exports.when = when;
47
+ exports.defineGraph = defineGraph;
48
+ const errors_js_1 = require("./errors.js");
49
+ const sse_js_1 = require("./sse.js");
50
+ /** The `END` sentinel target. */
51
+ exports.GRAPH_END = "END";
52
+ // ── Builders (pure) ───────────────────────────────────────────────
53
+ exports.reducers = {
54
+ append: () => ({ reducer: "append" }),
55
+ lastValue: () => ({ reducer: "lastValue" }),
56
+ merge: () => ({ reducer: "merge" }),
57
+ counter: () => ({ reducer: "counter" }),
58
+ /** A custom fold `(prev, update) => next`, run as a sandboxed transform. */
59
+ custom: (transform) => ({ reducer: "custom", reducerRef: transform }),
60
+ };
61
+ function agentNode(agentId, opts = {}) {
62
+ return { type: "agent", agentId, ...opts };
63
+ }
64
+ function toolNode(action, opts = {}) {
65
+ return { type: "tool", action, ...opts };
66
+ }
67
+ function transformNode(transform, opts = {}) {
68
+ return { type: "transform", transform, ...opts };
69
+ }
70
+ /**
71
+ * A `forEach` (map) node: run `body` per element of the `over` channel, appending
72
+ * each output into `collect`. Body params can reference the element via
73
+ * `{{item}}` / `{{item.field}}` / `{{index}}`.
74
+ */
75
+ function forEachNode(over, body, opts = {}) {
76
+ return { type: "forEach", over, body, ...opts };
77
+ }
78
+ function edge(from, to) {
79
+ return { kind: "static", from, to };
80
+ }
81
+ /** Attach a conditional edge to `from`. Pass a predicate builder or a router. */
82
+ function branch(from, spec) {
83
+ const b = spec instanceof PredicateBuilder ? spec.build() : spec;
84
+ return { kind: "conditional", from, branch: b };
85
+ }
86
+ /** A sandboxed router transform that returns a next-node key (or END). */
87
+ function router(transform, targets) {
88
+ return { kind: "router", transform, targets };
89
+ }
90
+ /**
91
+ * Fluent declarative predicate: `when("draft_ok", "==", true).then("END").else("write")`.
92
+ * Unary ops (`exists`/`truthy`/`falsy`) omit the value.
93
+ */
94
+ function when(channel, op, value) {
95
+ return new PredicateBuilder({ channel, op, value });
96
+ }
97
+ class PredicateBuilder {
98
+ predicate;
99
+ thenKey;
100
+ elseKey;
101
+ constructor(predicate) {
102
+ this.predicate = predicate;
103
+ }
104
+ then(nodeKey) {
105
+ this.thenKey = nodeKey;
106
+ return this;
107
+ }
108
+ else(nodeKey) {
109
+ this.elseKey = nodeKey;
110
+ return this;
111
+ }
112
+ /** @internal */
113
+ build() {
114
+ if (this.thenKey === undefined || this.elseKey === undefined) {
115
+ throw new errors_js_1.CuratorError("A `when(...)` branch requires both `.then(...)` and `.else(...)`.");
116
+ }
117
+ return { kind: "predicate", predicate: this.predicate, then: this.thenKey, else: this.elseKey };
118
+ }
119
+ }
120
+ exports.PredicateBuilder = PredicateBuilder;
121
+ /** Assemble a serializable graph spec locally. Pure — no network. */
122
+ function defineGraph(config) {
123
+ const { name, description, tags, state, nodes, entry, edges, recursionLimit, toolFailureMode } = config;
124
+ return {
125
+ name,
126
+ description,
127
+ tags,
128
+ spec: { state, nodes, entry, edges, recursionLimit, toolFailureMode },
129
+ };
130
+ }
131
+ // ── Client handles ────────────────────────────────────────────────
132
+ /** A handle to one graph resource, addressed by its `graph_id`. */
133
+ class Graph {
134
+ http;
135
+ baseUrl;
136
+ defaultTimeoutMs;
137
+ graphId;
138
+ defaultEnvironment;
139
+ /** @internal */
140
+ constructor(http, baseUrl, defaultTimeoutMs, graphId, defaultEnvironment) {
141
+ this.http = http;
142
+ this.baseUrl = baseUrl;
143
+ this.defaultTimeoutMs = defaultTimeoutMs;
144
+ this.graphId = graphId;
145
+ this.defaultEnvironment = defaultEnvironment;
146
+ }
147
+ get base() {
148
+ return `${this.baseUrl}/api/graphs/${encodeURIComponent(this.graphId)}`;
149
+ }
150
+ /** Deploy the current draft as an immutable versioned snapshot. */
151
+ async deploy(opts) {
152
+ const env = opts.environment ?? this.defaultEnvironment;
153
+ const res = await this.http.request(`${this.base}/deploy`, {
154
+ method: "POST",
155
+ body: {
156
+ environment: env,
157
+ slug: opts.slug,
158
+ change_summary: opts.changeSummary,
159
+ expected_draft_revision: opts.expectedDraftRevision,
160
+ },
161
+ });
162
+ return {
163
+ versionId: res.data.version.version_id,
164
+ versionNumber: res.data.version.version_number,
165
+ slug: res.data.deployment.slug,
166
+ };
167
+ }
168
+ /**
169
+ * Start a run and, unless `{ wait: false }`, block until it settles.
170
+ * Returns the terminal channel state (or `status: "timeout"` if the wait
171
+ * expires — the run keeps going server-side).
172
+ */
173
+ async run(input, opts = {}) {
174
+ const wait = opts.wait !== false;
175
+ const timeoutMs = opts.timeoutMs ?? this.defaultTimeoutMs;
176
+ const res = await this.http.request(`${this.base}/run`, {
177
+ method: "POST",
178
+ body: {
179
+ input,
180
+ environment: opts.environment ?? this.defaultEnvironment,
181
+ use_draft: opts.useDraft,
182
+ wait,
183
+ timeout_ms: wait ? timeoutMs : undefined,
184
+ },
185
+ signalTimeoutMs: wait ? timeoutMs + 10_000 : undefined,
186
+ });
187
+ return {
188
+ runId: res.data.run_id,
189
+ status: res.data.status ?? "running",
190
+ state: res.data.state,
191
+ errorCode: res.data.errorCode,
192
+ errorMessage: res.data.errorMessage,
193
+ };
194
+ }
195
+ /**
196
+ * Start a run and stream node/state lifecycle events until it settles.
197
+ * Events (`run.started` / `node.started` / `node.completed` / `node.failed`
198
+ * / `state.delta` / `done` / `error`) are derived from the server's run-view
199
+ * snapshot stream. Breaking out of the loop closes the connection; the run
200
+ * continues server-side.
201
+ */
202
+ async *stream(input, opts = {}) {
203
+ // Start async (no wait) to get a run id, then attach to the snapshot stream.
204
+ const started = await this.run(input, { ...opts, wait: false });
205
+ const runId = started.runId;
206
+ yield { type: "run.started", runId };
207
+ const controller = new AbortController();
208
+ const response = await this.http.streamPost(`${this.base}/runs/${encodeURIComponent(runId)}/stream`, { query: { stream: "true" }, signal: controller.signal });
209
+ const differ = new SnapshotDiffer();
210
+ try {
211
+ for await (const frame of (0, sse_js_1.parseSseStream)(response.body)) {
212
+ if (frame.event === "snapshot") {
213
+ const view = safeParse(frame.data);
214
+ if (!view)
215
+ continue;
216
+ for (const ev of differ.diff(view))
217
+ yield ev;
218
+ }
219
+ else if (frame.event === "done") {
220
+ const done = safeParse(frame.data);
221
+ const last = differ.lastView;
222
+ yield {
223
+ type: "done",
224
+ status: done?.status ?? last?.status ?? "succeeded",
225
+ state: last?.state ?? {},
226
+ };
227
+ return;
228
+ }
229
+ }
230
+ }
231
+ finally {
232
+ try {
233
+ controller.abort();
234
+ }
235
+ catch {
236
+ /* ignore */
237
+ }
238
+ }
239
+ }
240
+ /** Fetch the read-only run projection (spec + per-node status + state). */
241
+ async runStatus(runId) {
242
+ const res = await this.http.request(`${this.base}/runs/${encodeURIComponent(runId)}`);
243
+ return res.data;
244
+ }
245
+ /** List this graph's version snapshots (newest first). */
246
+ async versions() {
247
+ const res = await this.http.request(`${this.base}/versions`);
248
+ return res.data.map((v) => ({
249
+ versionId: v.version_id,
250
+ versionNumber: v.version_number,
251
+ createdAt: v.created_at,
252
+ }));
253
+ }
254
+ }
255
+ exports.Graph = Graph;
256
+ /** Collection surface for creating + addressing graphs. */
257
+ class Graphs {
258
+ http;
259
+ baseUrl;
260
+ defaultTimeoutMs;
261
+ defaultEnvironment;
262
+ /** @internal */
263
+ constructor(http, baseUrl, defaultTimeoutMs, defaultEnvironment) {
264
+ this.http = http;
265
+ this.baseUrl = baseUrl;
266
+ this.defaultTimeoutMs = defaultTimeoutMs;
267
+ this.defaultEnvironment = defaultEnvironment;
268
+ }
269
+ /** Create a new graph resource from a draft (or inline config). Returns a
270
+ * handle bound to the new `graph_id`. */
271
+ async create(draft) {
272
+ const normalized = "spec" in draft ? draft : defineGraph(draft);
273
+ const res = await this.http.request(`${this.baseUrl}/api/graphs`, {
274
+ method: "POST",
275
+ body: {
276
+ name: normalized.name,
277
+ description: normalized.description,
278
+ tags: normalized.tags,
279
+ spec: normalized.spec,
280
+ },
281
+ });
282
+ return this.handle(res.data.graph_id);
283
+ }
284
+ /** Reattach to an existing graph by id. */
285
+ get(graphId) {
286
+ return this.handle(graphId);
287
+ }
288
+ handle(graphId) {
289
+ return new Graph(this.http, this.baseUrl, this.defaultTimeoutMs, graphId, this.defaultEnvironment);
290
+ }
291
+ }
292
+ exports.Graphs = Graphs;
293
+ // ── Snapshot → event diffing ──────────────────────────────────────
294
+ class SnapshotDiffer {
295
+ lastView = null;
296
+ prevStatuses = new Map();
297
+ prevState = {};
298
+ diff(view) {
299
+ const events = [];
300
+ for (const node of view.nodes) {
301
+ const prev = this.prevStatuses.get(node.key);
302
+ if (prev !== node.status) {
303
+ if (node.status === "running") {
304
+ events.push({ type: "node.started", node: node.key, visit: node.visits });
305
+ }
306
+ else if (node.status === "succeeded") {
307
+ events.push({ type: "node.completed", node: node.key });
308
+ }
309
+ else if (node.status === "failed") {
310
+ events.push({ type: "node.failed", node: node.key });
311
+ }
312
+ this.prevStatuses.set(node.key, node.status);
313
+ }
314
+ }
315
+ const changed = {};
316
+ for (const [k, v] of Object.entries(view.state)) {
317
+ if (JSON.stringify(this.prevState[k]) !== JSON.stringify(v))
318
+ changed[k] = v;
319
+ }
320
+ if (Object.keys(changed).length > 0) {
321
+ events.push({ type: "state.delta", channels: changed });
322
+ }
323
+ this.prevState = view.state;
324
+ this.lastView = view;
325
+ return events;
326
+ }
327
+ }
328
+ function safeParse(text) {
329
+ try {
330
+ return JSON.parse(text);
331
+ }
332
+ catch {
333
+ return null;
334
+ }
335
+ }
336
+ //# sourceMappingURL=graph.js.map