acpx 0.8.0 → 0.9.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 (34) hide show
  1. package/README.md +7 -4
  2. package/dist/{cli-BGYGVo3b.js → cli-Bf3yjqzE.js} +4 -4
  3. package/dist/{cli-BGYGVo3b.js.map → cli-Bf3yjqzE.js.map} +1 -1
  4. package/dist/cli.d.ts +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +545 -247
  7. package/dist/cli.js.map +1 -1
  8. package/dist/{client-FzXPdgP7.d.ts → client-BssohYqM.d.ts} +30 -3
  9. package/dist/client-BssohYqM.d.ts.map +1 -0
  10. package/dist/{flags-D706STfk.js → flags-C-rwARqg.js} +96 -39
  11. package/dist/flags-C-rwARqg.js.map +1 -0
  12. package/dist/{flows-hcjHmU7P.js → flows-WLs26_5Y.js} +400 -335
  13. package/dist/flows-WLs26_5Y.js.map +1 -0
  14. package/dist/flows.d.ts +21 -1
  15. package/dist/flows.d.ts.map +1 -1
  16. package/dist/flows.js +1 -1
  17. package/dist/{live-checkpoint-B9ctAuqV.js → live-checkpoint-D5d-K9s1.js} +1355 -700
  18. package/dist/live-checkpoint-D5d-K9s1.js.map +1 -0
  19. package/dist/{output-BL9XRWzS.js → output-DPg20dvn.js} +1151 -717
  20. package/dist/output-DPg20dvn.js.map +1 -0
  21. package/dist/runtime.d.ts +30 -2
  22. package/dist/runtime.d.ts.map +1 -1
  23. package/dist/runtime.js +579 -425
  24. package/dist/runtime.js.map +1 -1
  25. package/dist/{session-options-BJyG6zEH.d.ts → session-options-CFudjdkU.d.ts} +7 -1
  26. package/dist/session-options-CFudjdkU.d.ts.map +1 -0
  27. package/package.json +15 -12
  28. package/skills/acpx/SKILL.md +11 -3
  29. package/dist/client-FzXPdgP7.d.ts.map +0 -1
  30. package/dist/flags-D706STfk.js.map +0 -1
  31. package/dist/flows-hcjHmU7P.js.map +0 -1
  32. package/dist/live-checkpoint-B9ctAuqV.js.map +0 -1
  33. package/dist/output-BL9XRWzS.js.map +0 -1
  34. package/dist/session-options-BJyG6zEH.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
- import { d as createSessionWithClient, f as cancelSessionPrompt, o as runOnce, s as sendSessionDirect, t as createOutputFormatter } from "./output-BL9XRWzS.js";
2
- import { Ft as PERMISSION_MODES, H as resolveSessionRecord, K as defaultSessionEventLog, Lt as SESSION_RECORD_SCHEMA, _t as withTimeout, b as recordPromptSubmission, ft as promptToDisplayText, g as cloneSessionAcpxState, gt as withInterrupt, ht as TimeoutError, mt as InterruptedError, pt as textPrompt, v as createSessionConversation, x as recordSessionUpdate, y as recordClientOperation } from "./live-checkpoint-B9ctAuqV.js";
1
+ import { d as createSessionWithClient, f as cancelSessionPrompt, o as runOnce, s as sendSessionDirect, t as createOutputFormatter } from "./output-DPg20dvn.js";
2
+ import { Ft as PERMISSION_MODES, H as resolveSessionRecord, K as defaultSessionEventLog, Lt as SESSION_RECORD_SCHEMA, _ as recordPromptSubmission, _t as withTimeout, ft as promptToDisplayText, g as recordClientOperation, gt as withInterrupt, h as createSessionConversation, ht as TimeoutError, mt as InterruptedError, p as cloneSessionAcpxState, pt as textPrompt, v as recordSessionUpdate } from "./live-checkpoint-D5d-K9s1.js";
3
3
  import path from "node:path";
4
4
  import fs from "node:fs/promises";
5
5
  import os from "node:os";
@@ -236,6 +236,15 @@ function renderShellCommand(command, args) {
236
236
  const renderedArgs = args.map((arg) => JSON.stringify(arg)).join(" ");
237
237
  return renderedArgs.length > 0 ? `${command} ${renderedArgs}` : command;
238
238
  }
239
+ function createShellFailureError(spec, args, exitCode, signal, stderr) {
240
+ const status = signal ? `signal ${signal}` : `exit ${String(exitCode)}`;
241
+ const details = stderr.length > 0 ? `\n${stderr.trim()}` : "";
242
+ return /* @__PURE__ */ new Error(`Shell action failed (${renderShellCommand(spec.command, args)}): ${status}${details}`);
243
+ }
244
+ function rejectIfShellFailed(spec, args, result, timedOut) {
245
+ if (timedOut) return new TimeoutError(spec.timeoutMs ?? 0);
246
+ if (((result.exitCode ?? 0) !== 0 || result.signal != null) && spec.allowNonZeroExit !== true) return createShellFailureError(spec, args, result.exitCode, result.signal, result.stderr);
247
+ }
239
248
  async function runShellAction(spec) {
240
249
  const cwd = spec.cwd ?? process.cwd();
241
250
  const args = spec.args ?? [];
@@ -280,12 +289,9 @@ async function runShellAction(spec) {
280
289
  signal,
281
290
  durationMs: Date.now() - startMs
282
291
  };
283
- if (timedOut) {
284
- reject(new TimeoutError(spec.timeoutMs ?? 0));
285
- return;
286
- }
287
- if (((exitCode ?? 0) !== 0 || signal != null) && spec.allowNonZeroExit !== true) {
288
- reject(/* @__PURE__ */ new Error(`Shell action failed (${renderShellCommand(spec.command, args)}): ${signal ? `signal ${signal}` : `exit ${String(exitCode)}`}${stderr.length > 0 ? `\n${stderr.trim()}` : ""}`));
292
+ const error = rejectIfShellFailed(spec, args, result, timedOut);
293
+ if (error) {
294
+ reject(error);
289
295
  return;
290
296
  }
291
297
  resolve(result);
@@ -312,16 +318,20 @@ function validateFlowDefinition(flow) {
312
318
  assertValidFlowDefinitionShape(flow);
313
319
  if (!flow.nodes[flow.startAt]) throw new Error(`Flow start node is missing: ${flow.startAt}`);
314
320
  const outgoingEdges = /* @__PURE__ */ new Set();
315
- for (const edge of flow.edges) {
316
- if (!flow.nodes[edge.from]) throw new Error(`Flow edge references unknown from-node: ${edge.from}`);
317
- if (outgoingEdges.has(edge.from)) throw new Error(`Flow node must not declare multiple outgoing edges: ${edge.from}`);
318
- outgoingEdges.add(edge.from);
319
- if ("to" in edge) {
320
- if (!flow.nodes[edge.to]) throw new Error(`Flow edge references unknown to-node: ${edge.to}`);
321
- continue;
322
- }
323
- for (const target of Object.values(edge.switch.cases)) if (!flow.nodes[target]) throw new Error(`Flow switch references unknown to-node: ${target}`);
321
+ for (const edge of flow.edges) validateFlowEdge(flow, edge, outgoingEdges);
322
+ }
323
+ function assertKnownFlowNode(flow, nodeId, description) {
324
+ if (!flow.nodes[nodeId]) throw new Error(`${description}: ${nodeId}`);
325
+ }
326
+ function validateFlowEdge(flow, edge, outgoingEdges) {
327
+ assertKnownFlowNode(flow, edge.from, "Flow edge references unknown from-node");
328
+ if (outgoingEdges.has(edge.from)) throw new Error(`Flow node must not declare multiple outgoing edges: ${edge.from}`);
329
+ outgoingEdges.add(edge.from);
330
+ if ("to" in edge) {
331
+ assertKnownFlowNode(flow, edge.to, "Flow edge references unknown to-node");
332
+ return;
324
333
  }
334
+ for (const target of Object.values(edge.switch.cases)) assertKnownFlowNode(flow, target, "Flow switch references unknown to-node");
325
335
  }
326
336
  function resolveNext(edges, from, output, result) {
327
337
  const edge = edges.find((candidate) => candidate.from === from);
@@ -584,14 +594,16 @@ function extractAttachedStepTrace(error) {
584
594
  }
585
595
  function toInlineOutput(value) {
586
596
  if (value == null || typeof value === "number" || typeof value === "boolean") return value;
587
- if (typeof value === "string") return value.length <= 200 && !value.includes("\n") ? value : void 0;
597
+ if (typeof value === "string") return isInlineSerializableText(value) ? value : void 0;
588
598
  try {
589
- const serialized = JSON.stringify(value);
590
- if (serialized.length <= 200 && !serialized.includes("\n")) return value;
599
+ if (isInlineSerializableText(JSON.stringify(value))) return value;
591
600
  } catch {
592
601
  return;
593
602
  }
594
603
  }
604
+ function isInlineSerializableText(value) {
605
+ return value.length <= 200 && !value.includes("\n");
606
+ }
595
607
  function outputArtifactMediaType(value) {
596
608
  return typeof value === "string" ? "text/plain" : "application/json";
597
609
  }
@@ -885,24 +897,9 @@ function createFlowDefinitionSnapshot(flow) {
885
897
  };
886
898
  }
887
899
  function snapshotNode(node) {
888
- const common = {
889
- nodeType: node.nodeType,
890
- ...node.timeoutMs !== void 0 ? { timeoutMs: node.timeoutMs } : {},
891
- ...node.heartbeatMs !== void 0 ? { heartbeatMs: node.heartbeatMs } : {},
892
- ...node.statusDetail ? { statusDetail: node.statusDetail } : {}
893
- };
900
+ const common = snapshotCommonNodeFields(node);
894
901
  switch (node.nodeType) {
895
- case "acp": return {
896
- ...common,
897
- ...node.profile ? { profile: node.profile } : {},
898
- session: {
899
- ...node.session?.handle ? { handle: node.session.handle } : {},
900
- ...node.session?.isolated ? { isolated: true } : {}
901
- },
902
- cwd: snapshotCwd(node.cwd),
903
- hasPrompt: true,
904
- hasParse: typeof node.parse === "function"
905
- };
902
+ case "acp": return snapshotAcpNode(node, common);
906
903
  case "compute": return {
907
904
  ...common,
908
905
  hasRun: true
@@ -925,6 +922,27 @@ function snapshotNode(node) {
925
922
  }
926
923
  throw new Error(`Unsupported flow node type: ${String(node)}`);
927
924
  }
925
+ function snapshotCommonNodeFields(node) {
926
+ return {
927
+ nodeType: node.nodeType,
928
+ ...node.timeoutMs !== void 0 ? { timeoutMs: node.timeoutMs } : {},
929
+ ...node.heartbeatMs !== void 0 ? { heartbeatMs: node.heartbeatMs } : {},
930
+ ...node.statusDetail ? { statusDetail: node.statusDetail } : {}
931
+ };
932
+ }
933
+ function snapshotAcpNode(node, common) {
934
+ return {
935
+ ...common,
936
+ ...node.profile ? { profile: node.profile } : {},
937
+ session: {
938
+ ...node.session?.handle ? { handle: node.session.handle } : {},
939
+ ...node.session?.isolated ? { isolated: true } : {}
940
+ },
941
+ cwd: snapshotCwd(node.cwd),
942
+ hasPrompt: true,
943
+ hasParse: typeof node.parse === "function"
944
+ };
945
+ }
928
946
  function snapshotCwd(cwd) {
929
947
  if (typeof cwd === "function") return { mode: "dynamic" };
930
948
  if (typeof cwd === "string") return {
@@ -1044,156 +1062,188 @@ var FlowRunner = class {
1044
1062
  state,
1045
1063
  inputArtifact
1046
1064
  });
1047
- let current = flow.startAt;
1048
- const attemptCounts = /* @__PURE__ */ new Map();
1049
1065
  try {
1050
- return await withInterrupt(async () => {
1051
- try {
1052
- while (current) {
1053
- const node = flow.nodes[current];
1054
- if (!node) throw new Error(`Unknown flow node: ${current}`);
1055
- const attemptId = nextAttemptId(attemptCounts, current);
1056
- const startedAt = isoNow$1();
1057
- const context = makeFlowNodeContext(state, input, this.services);
1058
- let output;
1059
- let promptText = null;
1060
- let rawText = null;
1061
- let sessionInfo = null;
1062
- let agentInfo = null;
1063
- let trace = null;
1064
- markNodeStarted(state, current, attemptId, node.nodeType, startedAt, node.statusDetail);
1065
- await this.store.writeSnapshot(runDir, state, {
1066
- scope: "node",
1067
- type: "node_started",
1068
- nodeId: current,
1069
- attemptId,
1070
- payload: {
1071
- nodeType: node.nodeType,
1072
- ...node.timeoutMs !== void 0 ? { timeoutMs: node.timeoutMs ?? this.defaultNodeTimeoutMs } : { timeoutMs: this.defaultNodeTimeoutMs },
1073
- ...state.statusDetail ? { statusDetail: state.statusDetail } : {}
1074
- }
1075
- });
1076
- let nodeResult;
1077
- let executionError;
1078
- try {
1079
- ({output, promptText, rawText, sessionInfo, agentInfo, trace} = await this.executeNode(runDir, state, flow, current, node, context));
1080
- trace = await finalizeStepTrace(this.store, runDir, state, current, attemptId, output, trace);
1081
- nodeResult = createNodeResult({
1082
- attemptId,
1083
- nodeId: current,
1084
- nodeType: node.nodeType,
1085
- outcome: "ok",
1086
- startedAt,
1087
- finishedAt: isoNow$1(),
1088
- output
1089
- });
1090
- } catch (error) {
1091
- executionError = error;
1092
- trace = extractAttachedStepTrace(error) ?? trace;
1093
- trace = await finalizeStepTrace(this.store, runDir, state, current, attemptId, void 0, trace);
1094
- nodeResult = createNodeResult({
1095
- attemptId,
1096
- nodeId: current,
1097
- nodeType: node.nodeType,
1098
- outcome: outcomeForError(error),
1099
- startedAt,
1100
- finishedAt: isoNow$1(),
1101
- error: error instanceof Error ? error.message : String(error)
1102
- });
1103
- }
1104
- state.results[current] = nodeResult;
1105
- if (nodeResult.outcome === "ok" && node.nodeType === "checkpoint") {
1106
- state.outputs[current] = output;
1107
- state.waitingOn = current;
1108
- state.updatedAt = isoNow$1();
1109
- state.status = "waiting";
1110
- clearActiveNode(state, output?.summary ?? current);
1111
- state.steps.push({
1112
- attemptId,
1113
- nodeId: current,
1114
- nodeType: node.nodeType,
1115
- outcome: nodeResult.outcome,
1116
- startedAt,
1117
- finishedAt: nodeResult.finishedAt,
1118
- promptText,
1119
- rawText,
1120
- output,
1121
- session: null,
1122
- agent: null,
1123
- ...trace ? { trace } : {}
1124
- });
1125
- await this.store.writeSnapshot(runDir, state, {
1126
- scope: "node",
1127
- type: "node_outcome",
1128
- nodeId: current,
1129
- attemptId,
1130
- payload: createNodeOutcomePayload(nodeResult, trace)
1131
- });
1132
- return {
1133
- runDir,
1134
- state
1135
- };
1136
- }
1137
- if (nodeResult.outcome === "ok") state.outputs[current] = output;
1138
- state.updatedAt = isoNow$1();
1139
- clearActiveNode(state);
1140
- state.steps.push({
1141
- attemptId,
1142
- nodeId: current,
1143
- nodeType: node.nodeType,
1144
- outcome: nodeResult.outcome,
1145
- startedAt,
1146
- finishedAt: nodeResult.finishedAt,
1147
- promptText,
1148
- rawText,
1149
- output,
1150
- error: nodeResult.error,
1151
- session: sessionInfo,
1152
- agent: agentInfo,
1153
- ...trace ? { trace } : {}
1154
- });
1155
- await this.store.writeSnapshot(runDir, state, {
1156
- scope: "node",
1157
- type: "node_outcome",
1158
- nodeId: current,
1159
- attemptId,
1160
- payload: createNodeOutcomePayload(nodeResult, trace)
1161
- });
1162
- if (nodeResult.outcome === "ok") {
1163
- current = resolveNext(flow.edges, current, output, nodeResult);
1164
- continue;
1165
- }
1166
- const next = resolveNextForOutcome(flow.edges, current, nodeResult);
1167
- if (next) {
1168
- current = next;
1169
- continue;
1170
- }
1171
- throw executionError;
1172
- }
1173
- state.status = "completed";
1174
- state.finishedAt = isoNow$1();
1175
- state.updatedAt = state.finishedAt;
1176
- clearActiveNode(state);
1177
- await this.store.writeSnapshot(runDir, state, {
1178
- scope: "run",
1179
- type: "run_completed",
1180
- payload: { status: state.status }
1181
- });
1182
- return {
1183
- runDir,
1184
- state
1185
- };
1186
- } catch (error) {
1187
- await persistRunFailure(this.store, runDir, state, error);
1188
- throw error;
1189
- }
1190
- }, async () => {
1066
+ return await withInterrupt(async () => await this.executeFlowRun(flow, input, runDir, state), async () => {
1191
1067
  await persistRunFailure(this.store, runDir, state, new InterruptedError());
1192
1068
  });
1193
1069
  } finally {
1194
1070
  await this.closePendingPersistentSessionClients();
1195
1071
  }
1196
1072
  }
1073
+ async executeFlowRun(flow, input, runDir, state) {
1074
+ let current = flow.startAt;
1075
+ const attemptCounts = /* @__PURE__ */ new Map();
1076
+ try {
1077
+ while (current) {
1078
+ const step = await this.executeFlowStep(flow, input, runDir, state, current, attemptCounts);
1079
+ const waiting = await this.maybeCompleteCheckpointStep(runDir, state, step);
1080
+ if (waiting) return waiting;
1081
+ await this.recordFlowStepOutcome(runDir, state, step);
1082
+ current = this.resolveNextNode(flow, step);
1083
+ }
1084
+ return await this.completeFlowRun(runDir, state);
1085
+ } catch (error) {
1086
+ await persistRunFailure(this.store, runDir, state, error);
1087
+ throw error;
1088
+ }
1089
+ }
1090
+ async executeFlowStep(flow, input, runDir, state, nodeId, attemptCounts) {
1091
+ const node = flow.nodes[nodeId];
1092
+ if (!node) throw new Error(`Unknown flow node: ${nodeId}`);
1093
+ const attemptId = nextAttemptId(attemptCounts, nodeId);
1094
+ const startedAt = isoNow$1();
1095
+ markNodeStarted(state, nodeId, attemptId, node.nodeType, startedAt, node.statusDetail);
1096
+ await this.writeNodeStartedSnapshot(runDir, state, nodeId, attemptId, node);
1097
+ return await this.executeStartedFlowStep({
1098
+ flow,
1099
+ input,
1100
+ runDir,
1101
+ state,
1102
+ nodeId,
1103
+ node,
1104
+ attemptId,
1105
+ startedAt
1106
+ });
1107
+ }
1108
+ async writeNodeStartedSnapshot(runDir, state, nodeId, attemptId, node) {
1109
+ await this.store.writeSnapshot(runDir, state, {
1110
+ scope: "node",
1111
+ type: "node_started",
1112
+ nodeId,
1113
+ attemptId,
1114
+ payload: {
1115
+ nodeType: node.nodeType,
1116
+ ...node.timeoutMs !== void 0 ? { timeoutMs: node.timeoutMs ?? this.defaultNodeTimeoutMs } : { timeoutMs: this.defaultNodeTimeoutMs },
1117
+ ...state.statusDetail ? { statusDetail: state.statusDetail } : {}
1118
+ }
1119
+ });
1120
+ }
1121
+ async executeStartedFlowStep(params) {
1122
+ const context = makeFlowNodeContext(params.state, params.input, this.services);
1123
+ try {
1124
+ const executed = await this.executeNode(params.runDir, params.state, params.flow, params.nodeId, params.node, context);
1125
+ return await this.createSuccessfulFlowStep(params, executed);
1126
+ } catch (error) {
1127
+ return await this.createFailedFlowStep(params, error);
1128
+ }
1129
+ }
1130
+ async createSuccessfulFlowStep(params, executed) {
1131
+ const trace = await finalizeStepTrace(this.store, params.runDir, params.state, params.nodeId, params.attemptId, executed.output, executed.trace);
1132
+ const nodeResult = createNodeResult({
1133
+ attemptId: params.attemptId,
1134
+ nodeId: params.nodeId,
1135
+ nodeType: params.node.nodeType,
1136
+ outcome: "ok",
1137
+ startedAt: params.startedAt,
1138
+ finishedAt: isoNow$1(),
1139
+ output: executed.output
1140
+ });
1141
+ params.state.results[params.nodeId] = nodeResult;
1142
+ return {
1143
+ ...executed,
1144
+ trace,
1145
+ nodeResult,
1146
+ attemptId: params.attemptId,
1147
+ nodeId: params.nodeId,
1148
+ node: params.node,
1149
+ startedAt: params.startedAt,
1150
+ state: params.state
1151
+ };
1152
+ }
1153
+ async createFailedFlowStep(params, error) {
1154
+ const trace = await finalizeStepTrace(this.store, params.runDir, params.state, params.nodeId, params.attemptId, void 0, extractAttachedStepTrace(error) ?? null);
1155
+ const nodeResult = createNodeResult({
1156
+ attemptId: params.attemptId,
1157
+ nodeId: params.nodeId,
1158
+ nodeType: params.node.nodeType,
1159
+ outcome: outcomeForError(error),
1160
+ startedAt: params.startedAt,
1161
+ finishedAt: isoNow$1(),
1162
+ error: error instanceof Error ? error.message : String(error)
1163
+ });
1164
+ params.state.results[params.nodeId] = nodeResult;
1165
+ return {
1166
+ output: void 0,
1167
+ promptText: null,
1168
+ rawText: null,
1169
+ sessionInfo: null,
1170
+ agentInfo: null,
1171
+ trace,
1172
+ nodeResult,
1173
+ executionError: error,
1174
+ attemptId: params.attemptId,
1175
+ nodeId: params.nodeId,
1176
+ node: params.node,
1177
+ startedAt: params.startedAt,
1178
+ state: params.state
1179
+ };
1180
+ }
1181
+ async maybeCompleteCheckpointStep(runDir, state, step) {
1182
+ if (step.nodeResult.outcome !== "ok" || step.node.nodeType !== "checkpoint") return;
1183
+ state.outputs[step.nodeId] = step.output;
1184
+ state.waitingOn = step.nodeId;
1185
+ state.updatedAt = isoNow$1();
1186
+ state.status = "waiting";
1187
+ await this.recordFlowStepOutcome(runDir, state, step, {
1188
+ sessionInfo: null,
1189
+ agentInfo: null,
1190
+ statusDetail: step.output?.summary ?? step.nodeId
1191
+ });
1192
+ return {
1193
+ runDir,
1194
+ state
1195
+ };
1196
+ }
1197
+ resolveNextNode(flow, step) {
1198
+ if (step.nodeResult.outcome === "ok") {
1199
+ step.state.outputs[step.nodeId] = step.output;
1200
+ return resolveNext(flow.edges, step.nodeId, step.output, step.nodeResult);
1201
+ }
1202
+ const next = resolveNextForOutcome(flow.edges, step.nodeId, step.nodeResult);
1203
+ if (next) return next;
1204
+ throw step.executionError;
1205
+ }
1206
+ async completeFlowRun(runDir, state) {
1207
+ state.status = "completed";
1208
+ state.finishedAt = isoNow$1();
1209
+ state.updatedAt = state.finishedAt;
1210
+ clearActiveNode(state);
1211
+ await this.store.writeSnapshot(runDir, state, {
1212
+ scope: "run",
1213
+ type: "run_completed",
1214
+ payload: { status: state.status }
1215
+ });
1216
+ return {
1217
+ runDir,
1218
+ state
1219
+ };
1220
+ }
1221
+ async recordFlowStepOutcome(runDir, state, step, overrides = {}) {
1222
+ state.updatedAt = isoNow$1();
1223
+ clearActiveNode(state, overrides.statusDetail);
1224
+ state.steps.push({
1225
+ attemptId: step.attemptId,
1226
+ nodeId: step.nodeId,
1227
+ nodeType: step.node.nodeType,
1228
+ outcome: step.nodeResult.outcome,
1229
+ startedAt: step.startedAt,
1230
+ finishedAt: step.nodeResult.finishedAt,
1231
+ promptText: step.promptText,
1232
+ rawText: step.rawText,
1233
+ output: step.output,
1234
+ error: step.nodeResult.error,
1235
+ session: overrides.sessionInfo ?? step.sessionInfo,
1236
+ agent: overrides.agentInfo ?? step.agentInfo,
1237
+ ...step.trace ? { trace: step.trace } : {}
1238
+ });
1239
+ await this.store.writeSnapshot(runDir, state, {
1240
+ scope: "node",
1241
+ type: "node_outcome",
1242
+ nodeId: step.nodeId,
1243
+ attemptId: step.attemptId,
1244
+ payload: createNodeOutcomePayload(step.nodeResult, step.trace)
1245
+ });
1246
+ }
1197
1247
  async executeNode(runDir, state, flow, nodeId, node, context) {
1198
1248
  switch (node.nodeType) {
1199
1249
  case "compute": return await this.executeComputeNode(runDir, state, node, context);
@@ -1335,147 +1385,132 @@ var FlowRunner = class {
1335
1385
  const nodeTimeoutMs = node.timeoutMs ?? this.defaultNodeTimeoutMs;
1336
1386
  let boundSession = null;
1337
1387
  return await this.runWithHeartbeat(runDir, state, state.currentNode ?? "", node, nodeTimeoutMs, async () => {
1338
- const resolvedAgent = this.resolveAgent(node.profile);
1339
- const agentInfo = {
1340
- ...resolvedAgent,
1341
- cwd: await resolveNodeCwd(resolvedAgent.cwd, node.cwd, context)
1342
- };
1343
- const prompt = normalizePromptInput(await Promise.resolve(node.prompt(context)));
1344
- const promptText = promptToDisplayText(prompt);
1345
- updateStatusDetail(state, summarizePrompt(promptText, node.statusDetail));
1346
- await this.store.writeLive(runDir, state, {
1347
- scope: "node",
1348
- type: "node_heartbeat",
1349
- nodeId: state.currentNode,
1350
- attemptId: state.currentAttemptId,
1351
- payload: { statusDetail: state.statusDetail }
1352
- });
1353
- const promptArtifact = await this.store.writeArtifact(runDir, state, promptText, {
1388
+ const prepared = await this.prepareAcpPrompt(runDir, state, node, context, nodeTimeoutMs);
1389
+ if (node.session?.isolated) return await this.executeIsolatedAcpPrompt(runDir, state, flow, node, context, prepared);
1390
+ boundSession = await this.ensureSessionBinding(runDir, state, flow, node, prepared.agentInfo, nodeTimeoutMs);
1391
+ return await this.executePersistentAcpPrompt(runDir, state, node, context, prepared, boundSession);
1392
+ }, async () => {
1393
+ if (!boundSession) return;
1394
+ await cancelSessionPrompt({ sessionId: boundSession.acpxRecordId });
1395
+ });
1396
+ }
1397
+ async prepareAcpPrompt(runDir, state, node, context, nodeTimeoutMs) {
1398
+ const resolvedAgent = this.resolveAgent(node.profile);
1399
+ const agentInfo = {
1400
+ ...resolvedAgent,
1401
+ cwd: await resolveNodeCwd(resolvedAgent.cwd, node.cwd, context)
1402
+ };
1403
+ const prompt = normalizePromptInput(await Promise.resolve(node.prompt(context)));
1404
+ const promptText = promptToDisplayText(prompt);
1405
+ updateStatusDetail(state, summarizePrompt(promptText, node.statusDetail));
1406
+ await this.writeAcpPromptHeartbeat(runDir, state);
1407
+ return {
1408
+ agentInfo,
1409
+ prompt,
1410
+ promptText,
1411
+ promptArtifact: await this.store.writeArtifact(runDir, state, promptText, {
1354
1412
  mediaType: "text/plain",
1355
1413
  extension: "txt",
1356
1414
  nodeId: state.currentNode,
1357
1415
  attemptId: state.currentAttemptId
1358
- });
1359
- if (node.session?.isolated) {
1360
- const isolatedBinding = createIsolatedSessionBinding(flow.name, state.runId, state.currentAttemptId ?? randomUUID(), node.profile, agentInfo);
1361
- const initialIsolatedRecord = createSyntheticSessionRecord({
1362
- binding: isolatedBinding,
1363
- createdAt: state.currentNodeStartedAt ?? isoNow$1(),
1364
- updatedAt: state.currentNodeStartedAt ?? isoNow$1(),
1365
- conversation: createSessionConversation(state.currentNodeStartedAt ?? isoNow$1()),
1366
- acpxState: void 0,
1367
- lastSeq: 0
1368
- });
1369
- await this.store.ensureSessionBundle(runDir, state, isolatedBinding, initialIsolatedRecord);
1370
- await this.store.appendTrace(runDir, state, {
1371
- scope: "acp",
1372
- type: "acp_prompt_prepared",
1373
- nodeId: state.currentNode,
1374
- attemptId: state.currentAttemptId,
1375
- sessionId: isolatedBinding.bundleId,
1376
- payload: {
1377
- sessionId: isolatedBinding.bundleId,
1378
- promptArtifact
1379
- }
1380
- });
1381
- const isolatedPrompt = await this.runIsolatedPrompt(runDir, state, isolatedBinding, agentInfo, prompt, nodeTimeoutMs);
1382
- const rawResponseArtifact = await this.store.writeArtifact(runDir, state, isolatedPrompt.rawText, {
1383
- mediaType: "text/plain",
1384
- extension: "txt",
1385
- nodeId: state.currentNode,
1386
- attemptId: state.currentAttemptId,
1387
- sessionId: isolatedBinding.bundleId
1388
- });
1389
- await this.store.appendTrace(runDir, state, {
1390
- scope: "acp",
1391
- type: "acp_response_parsed",
1392
- nodeId: state.currentNode,
1393
- attemptId: state.currentAttemptId,
1394
- sessionId: isolatedBinding.bundleId,
1395
- payload: {
1396
- sessionId: isolatedBinding.bundleId,
1397
- conversation: isolatedPrompt.conversation,
1398
- rawResponseArtifact
1399
- }
1400
- });
1401
- const trace = {
1402
- sessionId: isolatedBinding.bundleId,
1403
- promptArtifact,
1404
- rawResponseArtifact,
1405
- conversation: isolatedPrompt.conversation
1406
- };
1407
- let parsedOutput;
1408
- try {
1409
- parsedOutput = node.parse ? await node.parse(isolatedPrompt.rawText, context) : isolatedPrompt.rawText;
1410
- } catch (error) {
1411
- throw attachStepTrace(error, trace);
1412
- }
1413
- return {
1414
- output: parsedOutput,
1415
- promptText,
1416
- rawText: isolatedPrompt.rawText,
1417
- sessionInfo: isolatedBinding,
1418
- agentInfo,
1419
- trace
1420
- };
1416
+ }),
1417
+ nodeTimeoutMs
1418
+ };
1419
+ }
1420
+ async writeAcpPromptHeartbeat(runDir, state) {
1421
+ await this.store.writeLive(runDir, state, {
1422
+ scope: "node",
1423
+ type: "node_heartbeat",
1424
+ nodeId: state.currentNode,
1425
+ attemptId: state.currentAttemptId,
1426
+ payload: { statusDetail: state.statusDetail }
1427
+ });
1428
+ }
1429
+ async executeIsolatedAcpPrompt(runDir, state, flow, node, context, prepared) {
1430
+ const binding = createIsolatedSessionBinding(flow.name, state.runId, state.currentAttemptId ?? randomUUID(), node.profile, prepared.agentInfo);
1431
+ await this.initializeIsolatedSessionBundle(runDir, state, binding);
1432
+ await this.appendAcpPromptPreparedTrace(runDir, state, binding, prepared.promptArtifact);
1433
+ const prompt = await this.runIsolatedPrompt(runDir, state, binding, prepared.agentInfo, prepared.prompt, prepared.nodeTimeoutMs);
1434
+ return await this.finishAcpPrompt(runDir, state, node, context, prepared, prompt, binding);
1435
+ }
1436
+ async initializeIsolatedSessionBundle(runDir, state, binding) {
1437
+ const timestamp = state.currentNodeStartedAt ?? isoNow$1();
1438
+ const initialRecord = createSyntheticSessionRecord({
1439
+ binding,
1440
+ createdAt: timestamp,
1441
+ updatedAt: timestamp,
1442
+ conversation: createSessionConversation(timestamp),
1443
+ acpxState: void 0,
1444
+ lastSeq: 0
1445
+ });
1446
+ await this.store.ensureSessionBundle(runDir, state, binding, initialRecord);
1447
+ }
1448
+ async executePersistentAcpPrompt(runDir, state, node, context, prepared, binding) {
1449
+ await this.appendAcpPromptPreparedTrace(runDir, state, binding, prepared.promptArtifact);
1450
+ const prompt = await this.runPersistentPrompt(runDir, state, binding, prepared.prompt, prepared.nodeTimeoutMs);
1451
+ return await this.finishAcpPrompt(runDir, state, node, context, prepared, prompt, prompt.sessionInfo);
1452
+ }
1453
+ async appendAcpPromptPreparedTrace(runDir, state, binding, promptArtifact) {
1454
+ await this.store.appendTrace(runDir, state, {
1455
+ scope: "acp",
1456
+ type: "acp_prompt_prepared",
1457
+ nodeId: state.currentNode,
1458
+ attemptId: state.currentAttemptId,
1459
+ sessionId: binding.bundleId,
1460
+ payload: {
1461
+ sessionId: binding.bundleId,
1462
+ promptArtifact
1421
1463
  }
1422
- boundSession = await this.ensureSessionBinding(runDir, state, flow, node, agentInfo, nodeTimeoutMs);
1423
- await this.store.appendTrace(runDir, state, {
1424
- scope: "acp",
1425
- type: "acp_prompt_prepared",
1426
- nodeId: state.currentNode,
1427
- attemptId: state.currentAttemptId,
1428
- sessionId: boundSession.bundleId,
1429
- payload: {
1430
- sessionId: boundSession.bundleId,
1431
- promptArtifact
1432
- }
1433
- });
1434
- const persistentPrompt = await this.runPersistentPrompt(runDir, state, boundSession, prompt, nodeTimeoutMs);
1435
- const rawResponseArtifact = await this.store.writeArtifact(runDir, state, persistentPrompt.rawText, {
1436
- mediaType: "text/plain",
1437
- extension: "txt",
1438
- nodeId: state.currentNode,
1439
- attemptId: state.currentAttemptId,
1440
- sessionId: persistentPrompt.sessionInfo.bundleId
1441
- });
1442
- await this.store.appendTrace(runDir, state, {
1443
- scope: "acp",
1444
- type: "acp_response_parsed",
1445
- nodeId: state.currentNode,
1446
- attemptId: state.currentAttemptId,
1447
- sessionId: persistentPrompt.sessionInfo.bundleId,
1448
- payload: {
1449
- sessionId: persistentPrompt.sessionInfo.bundleId,
1450
- conversation: persistentPrompt.conversation,
1451
- rawResponseArtifact
1452
- }
1453
- });
1454
- const trace = {
1455
- sessionId: persistentPrompt.sessionInfo.bundleId,
1456
- promptArtifact,
1457
- rawResponseArtifact,
1458
- conversation: persistentPrompt.conversation
1459
- };
1460
- let parsedOutput;
1461
- try {
1462
- parsedOutput = node.parse ? await node.parse(persistentPrompt.rawText, context) : persistentPrompt.rawText;
1463
- } catch (error) {
1464
- throw attachStepTrace(error, trace);
1464
+ });
1465
+ }
1466
+ async finishAcpPrompt(runDir, state, node, context, prepared, prompt, sessionInfo) {
1467
+ const rawResponseArtifact = await this.writeAcpRawResponseArtifact(runDir, state, prompt, sessionInfo);
1468
+ await this.appendAcpResponseParsedTrace(runDir, state, prompt, sessionInfo, rawResponseArtifact);
1469
+ const trace = {
1470
+ sessionId: sessionInfo.bundleId,
1471
+ promptArtifact: prepared.promptArtifact,
1472
+ rawResponseArtifact,
1473
+ conversation: prompt.conversation
1474
+ };
1475
+ return {
1476
+ output: await this.parseAcpOutput(node, context, prompt.rawText, trace),
1477
+ promptText: prepared.promptText,
1478
+ rawText: prompt.rawText,
1479
+ sessionInfo,
1480
+ agentInfo: prepared.agentInfo,
1481
+ trace
1482
+ };
1483
+ }
1484
+ async writeAcpRawResponseArtifact(runDir, state, prompt, sessionInfo) {
1485
+ return await this.store.writeArtifact(runDir, state, prompt.rawText, {
1486
+ mediaType: "text/plain",
1487
+ extension: "txt",
1488
+ nodeId: state.currentNode,
1489
+ attemptId: state.currentAttemptId,
1490
+ sessionId: sessionInfo.bundleId
1491
+ });
1492
+ }
1493
+ async appendAcpResponseParsedTrace(runDir, state, prompt, sessionInfo, rawResponseArtifact) {
1494
+ await this.store.appendTrace(runDir, state, {
1495
+ scope: "acp",
1496
+ type: "acp_response_parsed",
1497
+ nodeId: state.currentNode,
1498
+ attemptId: state.currentAttemptId,
1499
+ sessionId: sessionInfo.bundleId,
1500
+ payload: {
1501
+ sessionId: sessionInfo.bundleId,
1502
+ conversation: prompt.conversation,
1503
+ rawResponseArtifact
1465
1504
  }
1466
- return {
1467
- output: parsedOutput,
1468
- promptText,
1469
- rawText: persistentPrompt.rawText,
1470
- sessionInfo: persistentPrompt.sessionInfo,
1471
- agentInfo,
1472
- trace
1473
- };
1474
- }, async () => {
1475
- if (!boundSession) return;
1476
- await cancelSessionPrompt({ sessionId: boundSession.acpxRecordId });
1477
1505
  });
1478
1506
  }
1507
+ async parseAcpOutput(node, context, rawText, trace) {
1508
+ try {
1509
+ return node.parse ? await node.parse(rawText, context) : rawText;
1510
+ } catch (error) {
1511
+ throw attachStepTrace(error, trace);
1512
+ }
1513
+ }
1479
1514
  async runWithHeartbeat(runDir, state, nodeId, node, timeoutMs, run, onTimeout) {
1480
1515
  const heartbeatMs = Math.max(0, Math.round(node.heartbeatMs ?? DEFAULT_FLOW_HEARTBEAT_MS));
1481
1516
  let timer;
@@ -1703,18 +1738,25 @@ function parseJsonObject(text, options = {}) {
1703
1738
  const mode = options.mode ?? "compat";
1704
1739
  const direct = tryParse(trimmed);
1705
1740
  if (direct.ok) return direct.value;
1706
- if (mode === "fenced" || mode === "compat") {
1707
- const fencedText = extractFencedJsonText(trimmed);
1708
- if (fencedText !== null) {
1709
- const fenced = tryParse(fencedText);
1710
- if (fenced.ok) return fenced.value;
1711
- }
1741
+ const fenced = parseFencedJsonIfAllowed(trimmed, mode);
1742
+ if (fenced.ok) return fenced.value;
1743
+ if (mode === "compat") {
1744
+ const balanced = parseBalancedJsonCandidate(trimmed);
1745
+ if (balanced.ok) return balanced.value;
1712
1746
  }
1713
- if (mode === "compat") for (const candidate of extractBalancedJsonCandidates(trimmed)) {
1747
+ throw new Error(`Could not parse JSON from assistant output:\n${trimmed}`);
1748
+ }
1749
+ function parseFencedJsonIfAllowed(text, mode) {
1750
+ if (mode !== "fenced" && mode !== "compat") return { ok: false };
1751
+ const fencedText = extractFencedJsonText(text);
1752
+ return fencedText === null ? { ok: false } : tryParse(fencedText);
1753
+ }
1754
+ function parseBalancedJsonCandidate(text) {
1755
+ for (const candidate of extractBalancedJsonCandidates(text)) {
1714
1756
  const parsed = tryParse(candidate);
1715
- if (parsed.ok) return parsed.value;
1757
+ if (parsed.ok) return parsed;
1716
1758
  }
1717
- throw new Error(`Could not parse JSON from assistant output:\n${trimmed}`);
1759
+ return { ok: false };
1718
1760
  }
1719
1761
  function parseStrictJsonObject(text) {
1720
1762
  return parseJsonObject(text, { mode: "strict" });
@@ -1761,27 +1803,50 @@ function scanBalanced(text, startIndex) {
1761
1803
  for (let index = startIndex; index < text.length; index += 1) {
1762
1804
  const char = text[index];
1763
1805
  if (inString) {
1764
- if (escaped) escaped = false;
1765
- else if (char === "\\") escaped = true;
1766
- else if (char === "\"") inString = false;
1806
+ const next = scanStringChar(char, escaped);
1807
+ escaped = next.escaped;
1808
+ inString = next.inString;
1767
1809
  continue;
1768
1810
  }
1769
1811
  if (char === "\"") {
1770
1812
  inString = true;
1771
1813
  continue;
1772
1814
  }
1773
- if (char === "{" || char === "[") {
1774
- stack.push(char);
1775
- continue;
1776
- }
1777
- if (char !== "}" && char !== "]") continue;
1778
- const last = stack.at(-1);
1779
- if (last === "{" && char !== "}" || last === "[" && char !== "]") return null;
1780
- stack.pop();
1781
- if (stack.length === 0) return text.slice(startIndex, index + 1);
1815
+ const result = scanBalancedToken(text, startIndex, index, char, stack);
1816
+ if (result !== SCAN_CONTINUE) return result;
1782
1817
  }
1783
1818
  return null;
1784
1819
  }
1820
+ const SCAN_CONTINUE = Symbol("scan-continue");
1821
+ function scanBalancedToken(text, startIndex, index, char, stack) {
1822
+ if (char === "{" || char === "[") {
1823
+ stack.push(char);
1824
+ return SCAN_CONTINUE;
1825
+ }
1826
+ if (char !== "}" && char !== "]") return SCAN_CONTINUE;
1827
+ if (!balancedClosingTokenMatches(stack.at(-1), char)) return null;
1828
+ stack.pop();
1829
+ return stack.length === 0 ? text.slice(startIndex, index + 1) : SCAN_CONTINUE;
1830
+ }
1831
+ function balancedClosingTokenMatches(open, close) {
1832
+ if (open === "{") return close === "}";
1833
+ if (open === "[") return close === "]";
1834
+ return false;
1835
+ }
1836
+ function scanStringChar(char, escaped) {
1837
+ if (escaped) return {
1838
+ escaped: false,
1839
+ inString: true
1840
+ };
1841
+ if (char === "\\") return {
1842
+ escaped: true,
1843
+ inString: true
1844
+ };
1845
+ return {
1846
+ escaped: false,
1847
+ inString: char !== "\""
1848
+ };
1849
+ }
1785
1850
  //#endregion
1786
1851
  //#region src/flows/decision.ts
1787
1852
  const DEFAULT_FIELD = "route";
@@ -1851,4 +1916,4 @@ function formatDecisionPrompt(question, choices, field) {
1851
1916
  //#endregion
1852
1917
  export { parseStrictJsonObject as a, validateFlowDefinition as c, checkpoint as d, compute as f, isDefinedFlow as h, parseJsonObject as i, acp as l, shell as m, decisionEdge as n, FlowRunner as o, defineFlow as p, extractJsonObject as r, flowRunsBaseDir as s, decision as t, action as u };
1853
1918
 
1854
- //# sourceMappingURL=flows-hcjHmU7P.js.map
1919
+ //# sourceMappingURL=flows-WLs26_5Y.js.map