acpx 0.4.0 → 0.5.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.
Files changed (47) hide show
  1. package/dist/agent-registry-DGw0-3Tc.js +54 -0
  2. package/dist/agent-registry-DGw0-3Tc.js.map +1 -0
  3. package/dist/{cli-5s-E-Y99.js → cli-CLRrs6eQ.js} +8 -12
  4. package/dist/cli-CLRrs6eQ.js.map +1 -0
  5. package/dist/cli.d.ts +2 -2
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +1018 -1009
  8. package/dist/cli.js.map +1 -1
  9. package/dist/client-DLTWuu4w.d.ts +116 -0
  10. package/dist/client-DLTWuu4w.d.ts.map +1 -0
  11. package/dist/{flags-BkWInxAq.js → flags-BmubjvOw.js} +5 -55
  12. package/dist/flags-BmubjvOw.js.map +1 -0
  13. package/dist/{flows-DnIYoHI1.js → flows-CR7xCmkR.js} +471 -281
  14. package/dist/flows-CR7xCmkR.js.map +1 -0
  15. package/dist/flows.d.ts +5 -9
  16. package/dist/flows.d.ts.map +1 -1
  17. package/dist/flows.js +1 -1
  18. package/dist/{queue-ipc-CgWf63GN.js → ipc-DN6M4Ui9.js} +12 -571
  19. package/dist/ipc-DN6M4Ui9.js.map +1 -0
  20. package/dist/{acp-jsonrpc-C7pPk9Tw.js → jsonrpc-M3y-qzy8.js} +16 -3
  21. package/dist/jsonrpc-M3y-qzy8.js.map +1 -0
  22. package/dist/{output-C58ukIo3.js → output-Di0M9Et8.js} +8 -22
  23. package/dist/output-Di0M9Et8.js.map +1 -0
  24. package/dist/perf-metrics-D9QC81lB.js +568 -0
  25. package/dist/perf-metrics-D9QC81lB.js.map +1 -0
  26. package/dist/{session-BtpTC2pM.js → prompt-turn-Bt8T3SRR.js} +2304 -3632
  27. package/dist/prompt-turn-Bt8T3SRR.js.map +1 -0
  28. package/dist/{output-render-C7w9NZ2H.js → render-BL5ynRkN.js} +7 -6
  29. package/dist/render-BL5ynRkN.js.map +1 -0
  30. package/dist/runtime.d.ts +266 -0
  31. package/dist/runtime.d.ts.map +1 -0
  32. package/dist/runtime.js +984 -0
  33. package/dist/runtime.js.map +1 -0
  34. package/dist/session-BbN0SBgf.js +1488 -0
  35. package/dist/session-BbN0SBgf.js.map +1 -0
  36. package/dist/{types-CeRKmEQ1.d.ts → types-DXxLBQc3.d.ts} +40 -3
  37. package/dist/types-DXxLBQc3.d.ts.map +1 -0
  38. package/package.json +20 -9
  39. package/dist/acp-jsonrpc-C7pPk9Tw.js.map +0 -1
  40. package/dist/cli-5s-E-Y99.js.map +0 -1
  41. package/dist/flags-BkWInxAq.js.map +0 -1
  42. package/dist/flows-DnIYoHI1.js.map +0 -1
  43. package/dist/output-C58ukIo3.js.map +0 -1
  44. package/dist/output-render-C7w9NZ2H.js.map +0 -1
  45. package/dist/queue-ipc-CgWf63GN.js.map +0 -1
  46. package/dist/session-BtpTC2pM.js.map +0 -1
  47. package/dist/types-CeRKmEQ1.d.ts.map +0 -1
@@ -1,44 +1,233 @@
1
- import { L as promptToDisplayText, R as textPrompt, j as SESSION_RECORD_SCHEMA } from "./queue-ipc-CgWf63GN.js";
2
- import { C as TimeoutError, S as InterruptedError, T as withTimeout, _ as recordClientOperation, a as runOnce, g as createSessionConversation, h as cloneSessionAcpxState, i as createSessionWithClient, m as defaultSessionEventLog, p as resolveSessionRecord, r as cancelSessionPrompt, s as sendSessionDirect, v as recordPromptSubmission, w as withInterrupt, y as recordSessionUpdate } from "./session-BtpTC2pM.js";
3
- import { t as createOutputFormatter } from "./output-C58ukIo3.js";
4
- import fs from "node:fs/promises";
1
+ import { A as PERMISSION_MODES, g as textPrompt, h as promptToDisplayText, j as SESSION_RECORD_SCHEMA } from "./perf-metrics-D9QC81lB.js";
2
+ import { A as resolveSessionRecord, H as TimeoutError, P as defaultSessionEventLog, U as withInterrupt, V as InterruptedError, W as withTimeout, _ as recordSessionUpdate, f as cloneSessionAcpxState, g as recordPromptSubmission, h as recordClientOperation, m as createSessionConversation } from "./prompt-turn-Bt8T3SRR.js";
3
+ import { c as createSessionWithClient, i as sendSessionDirect, l as cancelSessionPrompt, r as runOnce } from "./session-BbN0SBgf.js";
4
+ import { t as createOutputFormatter } from "./output-Di0M9Et8.js";
5
5
  import path from "node:path";
6
+ import fs from "node:fs/promises";
6
7
  import os from "node:os";
7
8
  import { spawn } from "node:child_process";
8
9
  import { createHash, randomUUID } from "node:crypto";
10
+ import { ZodError, z } from "zod";
11
+ //#region src/flows/authoring.ts
12
+ const FLOW_DEFINITION_BRAND = Symbol.for("acpx.flow.definition");
13
+ function markDefinedFlow(definition) {
14
+ if (isDefinedFlow(definition)) return definition;
15
+ Object.defineProperty(definition, FLOW_DEFINITION_BRAND, {
16
+ value: true,
17
+ enumerable: false,
18
+ configurable: false,
19
+ writable: false
20
+ });
21
+ return definition;
22
+ }
23
+ function isDefinedFlow(value) {
24
+ return value != null && typeof value === "object" && value[FLOW_DEFINITION_BRAND] === true;
25
+ }
26
+ //#endregion
27
+ //#region src/flows/schema.ts
28
+ const FLOW_NODE_TYPES = [
29
+ "acp",
30
+ "compute",
31
+ "action",
32
+ "checkpoint"
33
+ ];
34
+ const finiteNonNegativeNumberSchema = z.number().finite().nonnegative();
35
+ const nonEmptyTrimmedStringSchema = z.string().refine((value) => value.trim().length > 0, { message: "must not be empty" });
36
+ function extensibleObject(shape) {
37
+ return z.object(shape).passthrough();
38
+ }
39
+ function functionSchema(label) {
40
+ return z.custom((value) => typeof value === "function", { message: `${label} must be a function` });
41
+ }
42
+ const flowNodeCommonShape = {
43
+ timeoutMs: finiteNonNegativeNumberSchema.optional(),
44
+ heartbeatMs: finiteNonNegativeNumberSchema.optional(),
45
+ statusDetail: z.string().optional()
46
+ };
47
+ const flowPermissionRequirementsSchema = extensibleObject({
48
+ requiredMode: z.enum(PERMISSION_MODES),
49
+ requireExplicitGrant: z.boolean().optional(),
50
+ reason: nonEmptyTrimmedStringSchema.optional()
51
+ });
52
+ const flowRunDefinitionSchema = extensibleObject({ title: z.union([z.string(), functionSchema("run.title")]).optional() });
53
+ const acpSessionSchema = extensibleObject({
54
+ handle: z.string().optional(),
55
+ isolated: z.boolean().optional()
56
+ });
57
+ const acpNodeSchema = extensibleObject({
58
+ ...flowNodeCommonShape,
59
+ nodeType: z.literal("acp"),
60
+ profile: z.string().optional(),
61
+ cwd: z.union([z.string(), functionSchema("cwd")]).optional(),
62
+ session: acpSessionSchema.optional(),
63
+ prompt: functionSchema("prompt"),
64
+ parse: functionSchema("parse").optional()
65
+ });
66
+ const computeNodeSchema = extensibleObject({
67
+ ...flowNodeCommonShape,
68
+ nodeType: z.literal("compute"),
69
+ run: functionSchema("run")
70
+ });
71
+ const functionActionNodeSchema = extensibleObject({
72
+ ...flowNodeCommonShape,
73
+ nodeType: z.literal("action"),
74
+ run: functionSchema("run")
75
+ });
76
+ const shellActionNodeSchema = extensibleObject({
77
+ ...flowNodeCommonShape,
78
+ nodeType: z.literal("action"),
79
+ exec: functionSchema("exec"),
80
+ parse: functionSchema("parse").optional()
81
+ }).refine((node) => !hasOwn(node, "run"), { message: "shell action nodes must not define run" });
82
+ const checkpointNodeSchema = extensibleObject({
83
+ ...flowNodeCommonShape,
84
+ nodeType: z.literal("checkpoint"),
85
+ summary: z.string().optional(),
86
+ run: functionSchema("run").optional()
87
+ });
88
+ const directFlowEdgeSchema = extensibleObject({
89
+ from: z.string(),
90
+ to: z.string()
91
+ });
92
+ const switchFlowEdgeSchema = extensibleObject({
93
+ from: z.string(),
94
+ switch: extensibleObject({
95
+ on: z.string(),
96
+ cases: z.record(z.string(), z.string())
97
+ })
98
+ });
99
+ const flowDefinitionSchema = extensibleObject({
100
+ name: nonEmptyTrimmedStringSchema,
101
+ run: flowRunDefinitionSchema.optional(),
102
+ permissions: flowPermissionRequirementsSchema.optional(),
103
+ startAt: z.string(),
104
+ nodes: z.record(z.string(), z.unknown()),
105
+ edges: z.array(z.unknown())
106
+ });
107
+ const flowNodeTypeSchema = z.object({ nodeType: z.enum(FLOW_NODE_TYPES) });
108
+ function assertValidFlowDefinitionShape(flow) {
109
+ const parsed = parseWithSchema("flow definition", flowDefinitionSchema, flow);
110
+ for (const [nodeId, node] of Object.entries(parsed.nodes)) assertValidFlowNodeDefinitionShape(node, `flow node "${nodeId}"`);
111
+ parsed.edges.forEach((edge, index) => {
112
+ assertValidFlowEdgeShape(edge, `flow definition: edges.${index}`);
113
+ });
114
+ }
115
+ function assertValidAcpNodeDefinition(node) {
116
+ parseWithSchema("acp node definition", acpNodeSchema, node);
117
+ }
118
+ function assertValidComputeNodeDefinition(node) {
119
+ parseWithSchema("compute node definition", computeNodeSchema, node);
120
+ }
121
+ function assertValidActionNodeDefinition(node) {
122
+ assertValidActionNodeDefinitionShape(node, "action node definition");
123
+ }
124
+ function assertValidShellActionNodeDefinition(node) {
125
+ parseWithSchema("shell action node definition", shellActionNodeSchema, node);
126
+ }
127
+ function assertValidCheckpointNodeDefinition(node) {
128
+ parseWithSchema("checkpoint node definition", checkpointNodeSchema, node);
129
+ }
130
+ function assertValidFlowNodeDefinitionShape(node, label) {
131
+ const { nodeType } = parseWithSchema(label, flowNodeTypeSchema, node);
132
+ switch (nodeType) {
133
+ case "acp":
134
+ parseWithSchema(label, acpNodeSchema, node);
135
+ return;
136
+ case "compute":
137
+ parseWithSchema(label, computeNodeSchema, node);
138
+ return;
139
+ case "action":
140
+ assertValidActionNodeDefinitionShape(node, label);
141
+ return;
142
+ case "checkpoint":
143
+ parseWithSchema(label, checkpointNodeSchema, node);
144
+ return;
145
+ }
146
+ }
147
+ function assertValidActionNodeDefinitionShape(node, label) {
148
+ const hasRun = hasOwn(node, "run");
149
+ const hasExec = hasOwn(node, "exec");
150
+ if (hasRun === hasExec) throw new Error(`Invalid ${label}: action nodes must define exactly one of run or exec`);
151
+ if (hasExec) {
152
+ parseWithSchema(label, shellActionNodeSchema, node);
153
+ return;
154
+ }
155
+ parseWithSchema(label, functionActionNodeSchema, node);
156
+ }
157
+ function assertValidFlowEdgeShape(edge, label) {
158
+ const hasTo = hasOwn(edge, "to");
159
+ if (hasTo === hasOwn(edge, "switch")) throw new Error(`Invalid ${label}: edge must define exactly one of to or switch`);
160
+ if (hasTo) {
161
+ parseWithSchema(label, directFlowEdgeSchema, edge);
162
+ return;
163
+ }
164
+ parseWithSchema(label, switchFlowEdgeSchema, edge);
165
+ }
166
+ function parseWithSchema(label, schema, value) {
167
+ try {
168
+ return schema.parse(value);
169
+ } catch (error) {
170
+ if (error instanceof ZodError) throw new Error(formatValidationError(label, error), { cause: error });
171
+ throw error;
172
+ }
173
+ }
174
+ function formatValidationError(label, error) {
175
+ return `Invalid ${label}: ${Array.from(new Set(error.issues.flatMap((issue) => formatIssue(issue)))).join("; ")}`;
176
+ }
177
+ function formatIssue(issue, parentPath = []) {
178
+ const path = [...parentPath, ...issue.path.map(String)];
179
+ if (issue.code === "invalid_union") return issue.errors.flatMap((branch) => branch.flatMap((nestedIssue) => formatIssue(nestedIssue, path)));
180
+ const renderedPath = path.join(".");
181
+ return [renderedPath ? `${renderedPath}: ${issue.message}` : issue.message];
182
+ }
183
+ function hasOwn(value, key) {
184
+ return value != null && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, key);
185
+ }
186
+ //#endregion
9
187
  //#region src/flows/definition.ts
10
188
  function defineFlow(definition) {
11
- return definition;
189
+ assertValidFlowDefinitionShape(definition);
190
+ return markDefinedFlow(definition);
12
191
  }
13
192
  function acp(definition) {
14
- return {
193
+ const node = {
15
194
  nodeType: "acp",
16
195
  ...definition
17
196
  };
197
+ assertValidAcpNodeDefinition(node);
198
+ return node;
18
199
  }
19
200
  function compute(definition) {
20
- return {
201
+ const node = {
21
202
  nodeType: "compute",
22
203
  ...definition
23
204
  };
205
+ assertValidComputeNodeDefinition(node);
206
+ return node;
24
207
  }
25
208
  function action(definition) {
26
- return {
209
+ const node = {
27
210
  nodeType: "action",
28
211
  ...definition
29
212
  };
213
+ assertValidActionNodeDefinition(node);
214
+ return node;
30
215
  }
31
216
  function shell(definition) {
32
- return {
217
+ const node = {
33
218
  nodeType: "action",
34
219
  ...definition
35
220
  };
221
+ assertValidShellActionNodeDefinition(node);
222
+ return node;
36
223
  }
37
224
  function checkpoint(definition = {}) {
38
- return {
225
+ const node = {
39
226
  nodeType: "checkpoint",
40
227
  ...definition
41
228
  };
229
+ assertValidCheckpointNodeDefinition(node);
230
+ return node;
42
231
  }
43
232
  //#endregion
44
233
  //#region src/flows/executors/shell.ts
@@ -122,8 +311,7 @@ async function runShellAction(spec) {
122
311
  //#endregion
123
312
  //#region src/flows/graph.ts
124
313
  function validateFlowDefinition(flow) {
125
- if (!flow.name.trim()) throw new Error("Flow name must not be empty");
126
- if (flow.permissions?.reason !== void 0 && !flow.permissions.reason.trim()) throw new Error("Flow permission reason must not be empty");
314
+ assertValidFlowDefinitionShape(flow);
127
315
  if (!flow.nodes[flow.startAt]) throw new Error(`Flow start node is missing: ${flow.startAt}`);
128
316
  const outgoingEdges = /* @__PURE__ */ new Set();
129
317
  for (const edge of flow.edges) {
@@ -170,6 +358,244 @@ function getByPath(value, jsonPath) {
170
358
  }, value);
171
359
  }
172
360
  //#endregion
361
+ //#region src/flows/runtime-support.ts
362
+ function isoNow$1() {
363
+ return (/* @__PURE__ */ new Date()).toISOString();
364
+ }
365
+ function persistRunFailure(store, runDir, state, error) {
366
+ if (state.finishedAt !== void 0 && (state.status === "failed" || state.status === "timed_out")) return Promise.resolve();
367
+ state.status = error instanceof TimeoutError ? "timed_out" : "failed";
368
+ state.updatedAt = isoNow$1();
369
+ state.finishedAt = state.updatedAt;
370
+ state.error = error instanceof Error ? error.message : String(error);
371
+ state.statusDetail = state.currentNode ? `Failed in ${state.currentNode}: ${state.error}` : state.error;
372
+ return store.writeSnapshot(runDir, state, {
373
+ scope: "run",
374
+ type: "run_failed",
375
+ payload: {
376
+ status: state.status,
377
+ error: state.error
378
+ }
379
+ });
380
+ }
381
+ function makeFlowNodeContext(state, input, services) {
382
+ return {
383
+ input,
384
+ outputs: state.outputs,
385
+ results: state.results,
386
+ state,
387
+ services
388
+ };
389
+ }
390
+ function markNodeStarted(state, nodeId, attemptId, nodeType, startedAt, detail) {
391
+ state.status = "running";
392
+ state.waitingOn = void 0;
393
+ state.currentNode = nodeId;
394
+ state.currentAttemptId = attemptId;
395
+ state.currentNodeType = nodeType;
396
+ state.currentNodeStartedAt = startedAt;
397
+ state.lastHeartbeatAt = startedAt;
398
+ state.statusDetail = detail ?? `Running ${nodeType} node ${nodeId}`;
399
+ }
400
+ function clearActiveNode(state, detail) {
401
+ state.currentNode = void 0;
402
+ state.currentAttemptId = void 0;
403
+ state.currentNodeType = void 0;
404
+ state.currentNodeStartedAt = void 0;
405
+ state.lastHeartbeatAt = void 0;
406
+ state.statusDetail = detail;
407
+ }
408
+ function updateStatusDetail(state, detail) {
409
+ if (!detail) return;
410
+ state.statusDetail = detail;
411
+ }
412
+ async function finalizeStepTrace(store, runDir, state, nodeId, attemptId, output, baseTrace) {
413
+ const trace = baseTrace ? structuredClone(baseTrace) : {};
414
+ if (output !== void 0) {
415
+ const inlineOutput = toInlineOutput(output);
416
+ if (inlineOutput !== void 0) trace.outputInline = inlineOutput;
417
+ else trace.outputArtifact = await store.writeArtifact(runDir, state, output, {
418
+ mediaType: outputArtifactMediaType(output),
419
+ extension: outputArtifactExtension(output),
420
+ nodeId,
421
+ attemptId
422
+ });
423
+ }
424
+ return Object.keys(trace).length > 0 ? trace : null;
425
+ }
426
+ function normalizePromptInput(prompt) {
427
+ return typeof prompt === "string" ? textPrompt(prompt) : prompt;
428
+ }
429
+ async function resolveNodeCwd(defaultCwd, cwd, context) {
430
+ if (typeof cwd === "function") {
431
+ const resolved = await cwd(context) ?? defaultCwd;
432
+ return path.resolve(defaultCwd, resolved);
433
+ }
434
+ return path.resolve(defaultCwd, cwd ?? defaultCwd);
435
+ }
436
+ function resolveShellActionCwd(defaultCwd, cwd) {
437
+ return path.resolve(defaultCwd, cwd ?? defaultCwd);
438
+ }
439
+ function summarizePrompt(promptText, explicitDetail) {
440
+ if (explicitDetail) return explicitDetail;
441
+ const line = promptText.split("\n").map((candidate) => candidate.trim()).find((candidate) => candidate.length > 0);
442
+ if (!line) return "Running ACP prompt";
443
+ return `ACP: ${line.length > 120 ? `${line.slice(0, 117)}...` : line}`;
444
+ }
445
+ function createQuietCaptureOutput() {
446
+ const chunks = [];
447
+ return {
448
+ formatter: createOutputFormatter("quiet", { stdout: { write(chunk) {
449
+ chunks.push(chunk);
450
+ } } }),
451
+ read: () => chunks.join("").trim()
452
+ };
453
+ }
454
+ async function resolveFlowRunTitle(flow, input, flowPath) {
455
+ const titleDefinition = flow.run?.title;
456
+ if (titleDefinition === void 0) return;
457
+ return normalizeFlowRunTitle(typeof titleDefinition === "function" ? await Promise.resolve(titleDefinition({
458
+ input,
459
+ flowName: flow.name,
460
+ flowPath
461
+ })) : titleDefinition);
462
+ }
463
+ function normalizeFlowRunTitle(value) {
464
+ const trimmed = value?.trim();
465
+ return trimmed ? trimmed : void 0;
466
+ }
467
+ function createRunId(flowName) {
468
+ return `${isoNow$1().replaceAll(":", "").replaceAll(".", "")}-${flowName.replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "").toLowerCase()}-${randomUUID().slice(0, 8)}`;
469
+ }
470
+ function createSessionBindingKey(agentCommand, cwd, handle) {
471
+ return `${agentCommand}::${cwd}::${handle}`;
472
+ }
473
+ function createSessionName(flowName, handle, cwd, runId) {
474
+ return `${flowName}-${handle}-${stableShortHash(cwd)}-${runId.slice(-8)}`;
475
+ }
476
+ function createSessionBundleId(handle, key) {
477
+ return `${handle.replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "").toLowerCase() || "session"}-${stableShortHash(key)}`;
478
+ }
479
+ function createIsolatedSessionBinding(flowName, runId, attemptId, profile, agent) {
480
+ const key = `isolated::${attemptId}`;
481
+ const handle = "isolated";
482
+ return {
483
+ key,
484
+ handle,
485
+ bundleId: createSessionBundleId(`${handle}-${attemptId}`, `${key}::${agent.cwd}`),
486
+ name: `${flowName}-${attemptId}-${runId.slice(-8)}`,
487
+ profile,
488
+ agentName: agent.agentName,
489
+ agentCommand: agent.agentCommand,
490
+ cwd: agent.cwd,
491
+ acpxRecordId: key,
492
+ acpSessionId: key
493
+ };
494
+ }
495
+ function createSyntheticSessionRecord(options) {
496
+ return {
497
+ schema: SESSION_RECORD_SCHEMA,
498
+ acpxRecordId: options.binding.acpxRecordId,
499
+ acpSessionId: options.binding.acpSessionId,
500
+ agentSessionId: options.binding.agentSessionId,
501
+ agentCommand: options.binding.agentCommand,
502
+ cwd: options.binding.cwd,
503
+ name: options.binding.name,
504
+ createdAt: options.createdAt,
505
+ lastUsedAt: options.updatedAt,
506
+ lastSeq: options.lastSeq,
507
+ lastRequestId: void 0,
508
+ eventLog: defaultSessionEventLog(options.binding.acpxRecordId),
509
+ closed: true,
510
+ closedAt: options.updatedAt,
511
+ title: options.conversation.title,
512
+ messages: options.conversation.messages,
513
+ updated_at: options.conversation.updated_at,
514
+ cumulative_token_usage: options.conversation.cumulative_token_usage,
515
+ request_token_usage: options.conversation.request_token_usage,
516
+ acpx: options.acpxState
517
+ };
518
+ }
519
+ function createNodeResult(options) {
520
+ return {
521
+ attemptId: options.attemptId,
522
+ nodeId: options.nodeId,
523
+ nodeType: options.nodeType,
524
+ outcome: options.outcome,
525
+ startedAt: options.startedAt,
526
+ finishedAt: options.finishedAt,
527
+ durationMs: new Date(options.finishedAt).getTime() - new Date(options.startedAt).getTime(),
528
+ output: options.output,
529
+ error: options.error
530
+ };
531
+ }
532
+ function outcomeForError(error) {
533
+ if (error instanceof TimeoutError) return "timed_out";
534
+ if (error instanceof InterruptedError) return "cancelled";
535
+ return "failed";
536
+ }
537
+ function stableShortHash(value) {
538
+ return createHash("sha1").update(value).digest("hex").slice(0, 8);
539
+ }
540
+ function nextAttemptId(attemptCounts, nodeId) {
541
+ const next = (attemptCounts.get(nodeId) ?? 0) + 1;
542
+ attemptCounts.set(nodeId, next);
543
+ return `${nodeId}#${next}`;
544
+ }
545
+ function createNodeOutcomePayload(result, trace) {
546
+ return {
547
+ nodeType: result.nodeType,
548
+ outcome: result.outcome,
549
+ durationMs: result.durationMs,
550
+ error: result.error ?? null,
551
+ ...trace
552
+ };
553
+ }
554
+ function attachStepTrace(error, trace) {
555
+ const attached = error instanceof Error ? error : new Error(typeof error === "string" ? error : String(error));
556
+ attached.flowStepTrace = trace;
557
+ return attached;
558
+ }
559
+ function extractAttachedStepTrace(error) {
560
+ if (!(error instanceof Error)) return;
561
+ return error.flowStepTrace;
562
+ }
563
+ function toInlineOutput(value) {
564
+ if (value == null || typeof value === "number" || typeof value === "boolean") return value;
565
+ if (typeof value === "string") return value.length <= 200 && !value.includes("\n") ? value : void 0;
566
+ try {
567
+ const serialized = JSON.stringify(value);
568
+ if (serialized.length <= 200 && !serialized.includes("\n")) return value;
569
+ } catch {
570
+ return;
571
+ }
572
+ }
573
+ function outputArtifactMediaType(value) {
574
+ return typeof value === "string" ? "text/plain" : "application/json";
575
+ }
576
+ function outputArtifactExtension(value) {
577
+ return typeof value === "string" ? "txt" : "json";
578
+ }
579
+ function findConversationDeltaStart(before, after) {
580
+ const maxOverlap = Math.min(before.length, after.length);
581
+ for (let overlap = maxOverlap; overlap >= 0; overlap -= 1) {
582
+ let matches = true;
583
+ for (let index = 0; index < overlap; index += 1) {
584
+ const beforeMessage = before[before.length - overlap + index];
585
+ const afterMessage = after[index];
586
+ if (!deepEqualJson(beforeMessage, afterMessage)) {
587
+ matches = false;
588
+ break;
589
+ }
590
+ }
591
+ if (matches) return overlap;
592
+ }
593
+ return 0;
594
+ }
595
+ function deepEqualJson(left, right) {
596
+ return JSON.stringify(left) === JSON.stringify(right);
597
+ }
598
+ //#endregion
173
599
  //#region src/flows/store.ts
174
600
  const FLOW_BUNDLE_SCHEMA = "acpx.flow-run-bundle.v1";
175
601
  const FLOW_TRACE_SCHEMA = "acpx.flow-trace-event.v1";
@@ -245,7 +671,7 @@ var FlowRunStore = class {
245
671
  });
246
672
  }
247
673
  async writeSnapshot(runDir, state, event) {
248
- state.updatedAt = isoNow$1();
674
+ state.updatedAt = isoNow();
249
675
  await writeJsonAtomic(this.resolveRunPath(runDir, RUN_PROJECTION_PATH), state);
250
676
  await writeJsonAtomic(this.resolveRunPath(runDir, LIVE_PROJECTION_PATH), createLiveState(state));
251
677
  await writeJsonAtomic(this.resolveRunPath(runDir, STEPS_PROJECTION_PATH), state.steps);
@@ -253,7 +679,7 @@ var FlowRunStore = class {
253
679
  await this.appendTrace(runDir, state, event);
254
680
  }
255
681
  async writeLive(runDir, state, event) {
256
- state.updatedAt = isoNow$1();
682
+ state.updatedAt = isoNow();
257
683
  await writeJsonAtomic(this.resolveRunPath(runDir, LIVE_PROJECTION_PATH), createLiveState(state));
258
684
  await this.writeManifest(runDir, state);
259
685
  await this.appendTrace(runDir, state, event);
@@ -261,7 +687,7 @@ var FlowRunStore = class {
261
687
  async appendTrace(runDir, state, event) {
262
688
  const traceEvent = {
263
689
  seq: this.nextTraceSeq(runDir),
264
- at: isoNow$1(),
690
+ at: isoNow(),
265
691
  runId: state.runId,
266
692
  ...event
267
693
  };
@@ -340,7 +766,7 @@ var FlowRunStore = class {
340
766
  this.sessionSeqByBundle.set(sessionKey, seq);
341
767
  await this.appendJsonLine(this.resolveRunPath(runDir, path.posix.join(sessionDirPath(binding.bundleId), "events.ndjson")), {
342
768
  seq,
343
- at: isoNow$1(),
769
+ at: isoNow(),
344
770
  direction,
345
771
  message
346
772
  });
@@ -525,7 +951,7 @@ function normalizeArtifactExtension(extension) {
525
951
  function sessionDirPath(bundleId) {
526
952
  return path.posix.join(SESSIONS_DIR, bundleId);
527
953
  }
528
- function isoNow$1() {
954
+ function isoNow() {
529
955
  return (/* @__PURE__ */ new Date()).toISOString();
530
956
  }
531
957
  //#endregion
@@ -574,8 +1000,8 @@ var FlowRunner = class {
574
1000
  flowName: flow.name,
575
1001
  runTitle,
576
1002
  flowPath: options.flowPath,
577
- startedAt: isoNow(),
578
- updatedAt: isoNow(),
1003
+ startedAt: isoNow$1(),
1004
+ updatedAt: isoNow$1(),
579
1005
  status: "running",
580
1006
  input,
581
1007
  outputs: {},
@@ -602,15 +1028,15 @@ var FlowRunner = class {
602
1028
  const node = flow.nodes[current];
603
1029
  if (!node) throw new Error(`Unknown flow node: ${current}`);
604
1030
  const attemptId = nextAttemptId(attemptCounts, current);
605
- const startedAt = isoNow();
606
- const context = this.makeContext(state, input);
1031
+ const startedAt = isoNow$1();
1032
+ const context = makeFlowNodeContext(state, input, this.services);
607
1033
  let output;
608
1034
  let promptText = null;
609
1035
  let rawText = null;
610
1036
  let sessionInfo = null;
611
1037
  let agentInfo = null;
612
1038
  let trace = null;
613
- this.markNodeStarted(state, current, attemptId, node.nodeType, startedAt, node.statusDetail);
1039
+ markNodeStarted(state, current, attemptId, node.nodeType, startedAt, node.statusDetail);
614
1040
  await this.store.writeSnapshot(runDir, state, {
615
1041
  scope: "node",
616
1042
  type: "node_started",
@@ -626,27 +1052,27 @@ var FlowRunner = class {
626
1052
  let executionError;
627
1053
  try {
628
1054
  ({output, promptText, rawText, sessionInfo, agentInfo, trace} = await this.executeNode(runDir, state, flow, current, node, context));
629
- trace = await this.finalizeStepTrace(runDir, state, current, attemptId, output, trace);
1055
+ trace = await finalizeStepTrace(this.store, runDir, state, current, attemptId, output, trace);
630
1056
  nodeResult = createNodeResult({
631
1057
  attemptId,
632
1058
  nodeId: current,
633
1059
  nodeType: node.nodeType,
634
1060
  outcome: "ok",
635
1061
  startedAt,
636
- finishedAt: isoNow(),
1062
+ finishedAt: isoNow$1(),
637
1063
  output
638
1064
  });
639
1065
  } catch (error) {
640
1066
  executionError = error;
641
1067
  trace = extractAttachedStepTrace(error) ?? trace;
642
- trace = await this.finalizeStepTrace(runDir, state, current, attemptId, void 0, trace);
1068
+ trace = await finalizeStepTrace(this.store, runDir, state, current, attemptId, void 0, trace);
643
1069
  nodeResult = createNodeResult({
644
1070
  attemptId,
645
1071
  nodeId: current,
646
1072
  nodeType: node.nodeType,
647
1073
  outcome: outcomeForError(error),
648
1074
  startedAt,
649
- finishedAt: isoNow(),
1075
+ finishedAt: isoNow$1(),
650
1076
  error: error instanceof Error ? error.message : String(error)
651
1077
  });
652
1078
  }
@@ -654,9 +1080,9 @@ var FlowRunner = class {
654
1080
  if (nodeResult.outcome === "ok" && node.nodeType === "checkpoint") {
655
1081
  state.outputs[current] = output;
656
1082
  state.waitingOn = current;
657
- state.updatedAt = isoNow();
1083
+ state.updatedAt = isoNow$1();
658
1084
  state.status = "waiting";
659
- this.clearActiveNode(state, output?.summary ?? current);
1085
+ clearActiveNode(state, output?.summary ?? current);
660
1086
  state.steps.push({
661
1087
  attemptId,
662
1088
  nodeId: current,
@@ -684,8 +1110,8 @@ var FlowRunner = class {
684
1110
  };
685
1111
  }
686
1112
  if (nodeResult.outcome === "ok") state.outputs[current] = output;
687
- state.updatedAt = isoNow();
688
- this.clearActiveNode(state);
1113
+ state.updatedAt = isoNow$1();
1114
+ clearActiveNode(state);
689
1115
  state.steps.push({
690
1116
  attemptId,
691
1117
  nodeId: current,
@@ -720,9 +1146,9 @@ var FlowRunner = class {
720
1146
  throw executionError;
721
1147
  }
722
1148
  state.status = "completed";
723
- state.finishedAt = isoNow();
1149
+ state.finishedAt = isoNow$1();
724
1150
  state.updatedAt = state.finishedAt;
725
- this.clearActiveNode(state);
1151
+ clearActiveNode(state);
726
1152
  await this.store.writeSnapshot(runDir, state, {
727
1153
  scope: "run",
728
1154
  type: "run_completed",
@@ -733,41 +1159,16 @@ var FlowRunner = class {
733
1159
  state
734
1160
  };
735
1161
  } catch (error) {
736
- await this.persistRunFailure(runDir, state, error);
1162
+ await persistRunFailure(this.store, runDir, state, error);
737
1163
  throw error;
738
1164
  }
739
1165
  }, async () => {
740
- await this.persistRunFailure(runDir, state, new InterruptedError());
1166
+ await persistRunFailure(this.store, runDir, state, new InterruptedError());
741
1167
  });
742
1168
  } finally {
743
1169
  await this.closePendingPersistentSessionClients();
744
1170
  }
745
1171
  }
746
- async persistRunFailure(runDir, state, error) {
747
- if (state.finishedAt !== void 0 && (state.status === "failed" || state.status === "timed_out")) return;
748
- state.status = error instanceof TimeoutError ? "timed_out" : "failed";
749
- state.updatedAt = isoNow();
750
- state.finishedAt = state.updatedAt;
751
- state.error = error instanceof Error ? error.message : String(error);
752
- state.statusDetail = state.currentNode ? `Failed in ${state.currentNode}: ${state.error}` : state.error;
753
- await this.store.writeSnapshot(runDir, state, {
754
- scope: "run",
755
- type: "run_failed",
756
- payload: {
757
- status: state.status,
758
- error: state.error
759
- }
760
- });
761
- }
762
- makeContext(state, input) {
763
- return {
764
- input,
765
- outputs: state.outputs,
766
- results: state.results,
767
- state,
768
- services: this.services
769
- };
770
- }
771
1172
  async executeNode(runDir, state, flow, nodeId, node, context) {
772
1173
  switch (node.nodeType) {
773
1174
  case "compute": return await this.executeComputeNode(runDir, state, node, context);
@@ -805,7 +1206,7 @@ var FlowRunner = class {
805
1206
  cwd: resolveShellActionCwd(this.defaultCwd, execution.cwd),
806
1207
  timeoutMs: execution.timeoutMs ?? nodeTimeoutMs
807
1208
  };
808
- this.updateStatusDetail(state, formatShellActionSummary(effectiveExecution));
1209
+ updateStatusDetail(state, formatShellActionSummary(effectiveExecution));
809
1210
  await this.store.writeLive(runDir, state, {
810
1211
  scope: "node",
811
1212
  type: "node_heartbeat",
@@ -916,7 +1317,7 @@ var FlowRunner = class {
916
1317
  };
917
1318
  const prompt = normalizePromptInput(await Promise.resolve(node.prompt(context)));
918
1319
  const promptText = promptToDisplayText(prompt);
919
- this.updateStatusDetail(state, summarizePrompt(promptText, node.statusDetail));
1320
+ updateStatusDetail(state, summarizePrompt(promptText, node.statusDetail));
920
1321
  await this.store.writeLive(runDir, state, {
921
1322
  scope: "node",
922
1323
  type: "node_heartbeat",
@@ -934,9 +1335,9 @@ var FlowRunner = class {
934
1335
  const isolatedBinding = createIsolatedSessionBinding(flow.name, state.runId, state.currentAttemptId ?? randomUUID(), node.profile, agentInfo);
935
1336
  const initialIsolatedRecord = createSyntheticSessionRecord({
936
1337
  binding: isolatedBinding,
937
- createdAt: state.currentNodeStartedAt ?? isoNow(),
938
- updatedAt: state.currentNodeStartedAt ?? isoNow(),
939
- conversation: createSessionConversation(state.currentNodeStartedAt ?? isoNow()),
1338
+ createdAt: state.currentNodeStartedAt ?? isoNow$1(),
1339
+ updatedAt: state.currentNodeStartedAt ?? isoNow$1(),
1340
+ conversation: createSessionConversation(state.currentNodeStartedAt ?? isoNow$1()),
940
1341
  acpxState: void 0,
941
1342
  lastSeq: 0
942
1343
  });
@@ -1050,49 +1451,13 @@ var FlowRunner = class {
1050
1451
  await cancelSessionPrompt({ sessionId: boundSession.acpxRecordId });
1051
1452
  });
1052
1453
  }
1053
- markNodeStarted(state, nodeId, attemptId, nodeType, startedAt, detail) {
1054
- state.status = "running";
1055
- state.waitingOn = void 0;
1056
- state.currentNode = nodeId;
1057
- state.currentAttemptId = attemptId;
1058
- state.currentNodeType = nodeType;
1059
- state.currentNodeStartedAt = startedAt;
1060
- state.lastHeartbeatAt = startedAt;
1061
- state.statusDetail = detail ?? `Running ${nodeType} node ${nodeId}`;
1062
- }
1063
- clearActiveNode(state, detail) {
1064
- state.currentNode = void 0;
1065
- state.currentAttemptId = void 0;
1066
- state.currentNodeType = void 0;
1067
- state.currentNodeStartedAt = void 0;
1068
- state.lastHeartbeatAt = void 0;
1069
- state.statusDetail = detail;
1070
- }
1071
- updateStatusDetail(state, detail) {
1072
- if (!detail) return;
1073
- state.statusDetail = detail;
1074
- }
1075
- async finalizeStepTrace(runDir, state, nodeId, attemptId, output, baseTrace) {
1076
- const trace = baseTrace ? structuredClone(baseTrace) : {};
1077
- if (output !== void 0) {
1078
- const inlineOutput = toInlineOutput(output);
1079
- if (inlineOutput !== void 0) trace.outputInline = inlineOutput;
1080
- else trace.outputArtifact = await this.store.writeArtifact(runDir, state, output, {
1081
- mediaType: outputArtifactMediaType(output),
1082
- extension: outputArtifactExtension(output),
1083
- nodeId,
1084
- attemptId
1085
- });
1086
- }
1087
- return Object.keys(trace).length > 0 ? trace : null;
1088
- }
1089
1454
  async runWithHeartbeat(runDir, state, nodeId, node, timeoutMs, run, onTimeout) {
1090
1455
  const heartbeatMs = Math.max(0, Math.round(node.heartbeatMs ?? DEFAULT_FLOW_HEARTBEAT_MS));
1091
1456
  let timer;
1092
1457
  let active = true;
1093
1458
  const heartbeat = async () => {
1094
1459
  if (!active) return;
1095
- state.lastHeartbeatAt = isoNow();
1460
+ state.lastHeartbeatAt = isoNow$1();
1096
1461
  state.updatedAt = state.lastHeartbeatAt;
1097
1462
  await this.store.writeLive(runDir, state, {
1098
1463
  scope: "node",
@@ -1229,9 +1594,9 @@ var FlowRunner = class {
1229
1594
  }
1230
1595
  async runIsolatedPrompt(runDir, state, binding, agent, prompt, timeoutMs) {
1231
1596
  const capture = createQuietCaptureOutput();
1232
- const conversation = createSessionConversation(state.currentNodeStartedAt ?? isoNow());
1597
+ const conversation = createSessionConversation(state.currentNodeStartedAt ?? isoNow$1());
1233
1598
  let acpxState;
1234
- recordPromptSubmission(conversation, prompt, state.currentNodeStartedAt ?? isoNow());
1599
+ recordPromptSubmission(conversation, prompt, state.currentNodeStartedAt ?? isoNow$1());
1235
1600
  let eventStartSeq;
1236
1601
  let eventEndSeq;
1237
1602
  const pendingEventWrites = [];
@@ -1272,7 +1637,7 @@ var FlowRunner = class {
1272
1637
  await this.store.ensureSessionBundle(runDir, state, sessionInfo);
1273
1638
  const syntheticRecord = createSyntheticSessionRecord({
1274
1639
  binding: sessionInfo,
1275
- createdAt: state.currentNodeStartedAt ?? isoNow(),
1640
+ createdAt: state.currentNodeStartedAt ?? isoNow$1(),
1276
1641
  updatedAt: conversation.updated_at,
1277
1642
  conversation,
1278
1643
  acpxState: cloneSessionAcpxState(acpxState),
@@ -1296,181 +1661,6 @@ var FlowRunner = class {
1296
1661
  };
1297
1662
  }
1298
1663
  };
1299
- function normalizePromptInput(prompt) {
1300
- return typeof prompt === "string" ? textPrompt(prompt) : prompt;
1301
- }
1302
- async function resolveNodeCwd(defaultCwd, cwd, context) {
1303
- if (typeof cwd === "function") {
1304
- const resolved = await cwd(context) ?? defaultCwd;
1305
- return path.resolve(defaultCwd, resolved);
1306
- }
1307
- return path.resolve(defaultCwd, cwd ?? defaultCwd);
1308
- }
1309
- function resolveShellActionCwd(defaultCwd, cwd) {
1310
- return path.resolve(defaultCwd, cwd ?? defaultCwd);
1311
- }
1312
- function summarizePrompt(promptText, explicitDetail) {
1313
- if (explicitDetail) return explicitDetail;
1314
- const line = promptText.split("\n").map((candidate) => candidate.trim()).find((candidate) => candidate.length > 0);
1315
- if (!line) return "Running ACP prompt";
1316
- return `ACP: ${line.length > 120 ? `${line.slice(0, 117)}...` : line}`;
1317
- }
1318
- function createQuietCaptureOutput() {
1319
- const chunks = [];
1320
- return {
1321
- formatter: createOutputFormatter("quiet", { stdout: { write(chunk) {
1322
- chunks.push(chunk);
1323
- } } }),
1324
- read: () => chunks.join("").trim()
1325
- };
1326
- }
1327
- async function resolveFlowRunTitle(flow, input, flowPath) {
1328
- const titleDefinition = flow.run?.title;
1329
- if (titleDefinition === void 0) return;
1330
- return normalizeFlowRunTitle(typeof titleDefinition === "function" ? await Promise.resolve(titleDefinition({
1331
- input,
1332
- flowName: flow.name,
1333
- flowPath
1334
- })) : titleDefinition);
1335
- }
1336
- function normalizeFlowRunTitle(value) {
1337
- const trimmed = value?.trim();
1338
- return trimmed ? trimmed : void 0;
1339
- }
1340
- function createRunId(flowName) {
1341
- return `${isoNow().replaceAll(":", "").replaceAll(".", "")}-${flowName.replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "").toLowerCase()}-${randomUUID().slice(0, 8)}`;
1342
- }
1343
- function createSessionBindingKey(agentCommand, cwd, handle) {
1344
- return `${agentCommand}::${cwd}::${handle}`;
1345
- }
1346
- function createSessionName(flowName, handle, cwd, runId) {
1347
- return `${flowName}-${handle}-${stableShortHash(cwd)}-${runId.slice(-8)}`;
1348
- }
1349
- function createSessionBundleId(handle, key) {
1350
- return `${handle.replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "").toLowerCase() || "session"}-${stableShortHash(key)}`;
1351
- }
1352
- function createIsolatedSessionBinding(flowName, runId, attemptId, profile, agent) {
1353
- const key = `isolated::${attemptId}`;
1354
- const handle = "isolated";
1355
- return {
1356
- key,
1357
- handle,
1358
- bundleId: createSessionBundleId(`${handle}-${attemptId}`, `${key}::${agent.cwd}`),
1359
- name: `${flowName}-${attemptId}-${runId.slice(-8)}`,
1360
- profile,
1361
- agentName: agent.agentName,
1362
- agentCommand: agent.agentCommand,
1363
- cwd: agent.cwd,
1364
- acpxRecordId: key,
1365
- acpSessionId: key
1366
- };
1367
- }
1368
- function createSyntheticSessionRecord(options) {
1369
- return {
1370
- schema: SESSION_RECORD_SCHEMA,
1371
- acpxRecordId: options.binding.acpxRecordId,
1372
- acpSessionId: options.binding.acpSessionId,
1373
- agentSessionId: options.binding.agentSessionId,
1374
- agentCommand: options.binding.agentCommand,
1375
- cwd: options.binding.cwd,
1376
- name: options.binding.name,
1377
- createdAt: options.createdAt,
1378
- lastUsedAt: options.updatedAt,
1379
- lastSeq: options.lastSeq,
1380
- lastRequestId: void 0,
1381
- eventLog: defaultSessionEventLog(options.binding.acpxRecordId),
1382
- closed: true,
1383
- closedAt: options.updatedAt,
1384
- title: options.conversation.title,
1385
- messages: options.conversation.messages,
1386
- updated_at: options.conversation.updated_at,
1387
- cumulative_token_usage: options.conversation.cumulative_token_usage,
1388
- request_token_usage: options.conversation.request_token_usage,
1389
- acpx: options.acpxState
1390
- };
1391
- }
1392
- function createNodeResult(options) {
1393
- return {
1394
- attemptId: options.attemptId,
1395
- nodeId: options.nodeId,
1396
- nodeType: options.nodeType,
1397
- outcome: options.outcome,
1398
- startedAt: options.startedAt,
1399
- finishedAt: options.finishedAt,
1400
- durationMs: new Date(options.finishedAt).getTime() - new Date(options.startedAt).getTime(),
1401
- output: options.output,
1402
- error: options.error
1403
- };
1404
- }
1405
- function outcomeForError(error) {
1406
- if (error instanceof TimeoutError) return "timed_out";
1407
- if (error instanceof InterruptedError) return "cancelled";
1408
- return "failed";
1409
- }
1410
- function stableShortHash(value) {
1411
- return createHash("sha1").update(value).digest("hex").slice(0, 8);
1412
- }
1413
- function nextAttemptId(attemptCounts, nodeId) {
1414
- const next = (attemptCounts.get(nodeId) ?? 0) + 1;
1415
- attemptCounts.set(nodeId, next);
1416
- return `${nodeId}#${next}`;
1417
- }
1418
- function createNodeOutcomePayload(result, trace) {
1419
- return {
1420
- nodeType: result.nodeType,
1421
- outcome: result.outcome,
1422
- durationMs: result.durationMs,
1423
- error: result.error ?? null,
1424
- ...trace
1425
- };
1426
- }
1427
- function attachStepTrace(error, trace) {
1428
- const attached = error instanceof Error ? error : new Error(typeof error === "string" ? error : String(error));
1429
- attached.flowStepTrace = trace;
1430
- return attached;
1431
- }
1432
- function extractAttachedStepTrace(error) {
1433
- if (!(error instanceof Error)) return;
1434
- return error.flowStepTrace;
1435
- }
1436
- function toInlineOutput(value) {
1437
- if (value == null || typeof value === "number" || typeof value === "boolean") return value;
1438
- if (typeof value === "string") return value.length <= 200 && !value.includes("\n") ? value : void 0;
1439
- try {
1440
- const serialized = JSON.stringify(value);
1441
- if (serialized.length <= 200 && !serialized.includes("\n")) return value;
1442
- } catch {
1443
- return;
1444
- }
1445
- }
1446
- function outputArtifactMediaType(value) {
1447
- return typeof value === "string" ? "text/plain" : "application/json";
1448
- }
1449
- function outputArtifactExtension(value) {
1450
- return typeof value === "string" ? "txt" : "json";
1451
- }
1452
- function findConversationDeltaStart(before, after) {
1453
- const maxOverlap = Math.min(before.length, after.length);
1454
- for (let overlap = maxOverlap; overlap >= 0; overlap -= 1) {
1455
- let matches = true;
1456
- for (let index = 0; index < overlap; index += 1) {
1457
- const beforeMessage = before[before.length - overlap + index];
1458
- const afterMessage = after[index];
1459
- if (!deepEqualJson(beforeMessage, afterMessage)) {
1460
- matches = false;
1461
- break;
1462
- }
1463
- }
1464
- if (matches) return overlap;
1465
- }
1466
- return 0;
1467
- }
1468
- function deepEqualJson(left, right) {
1469
- return JSON.stringify(left) === JSON.stringify(right);
1470
- }
1471
- function isoNow() {
1472
- return (/* @__PURE__ */ new Date()).toISOString();
1473
- }
1474
1664
  //#endregion
1475
1665
  //#region src/flows/json.ts
1476
1666
  function parseJsonObject(text, options = {}) {
@@ -1546,6 +1736,6 @@ function scanBalanced(text, startIndex) {
1546
1736
  return null;
1547
1737
  }
1548
1738
  //#endregion
1549
- export { flowRunsBaseDir as a, checkpoint as c, shell as d, FlowRunner as i, compute as l, parseJsonObject as n, acp as o, parseStrictJsonObject as r, action as s, extractJsonObject as t, defineFlow as u };
1739
+ export { flowRunsBaseDir as a, action as c, defineFlow as d, shell as f, FlowRunner as i, checkpoint as l, parseJsonObject as n, validateFlowDefinition as o, isDefinedFlow as p, parseStrictJsonObject as r, acp as s, extractJsonObject as t, compute as u };
1550
1740
 
1551
- //# sourceMappingURL=flows-DnIYoHI1.js.map
1741
+ //# sourceMappingURL=flows-CR7xCmkR.js.map