acpx 0.7.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 (50) hide show
  1. package/README.md +14 -6
  2. package/dist/{cli-T-Z-9x6a.js → cli-Bf3yjqzE.js} +35 -10
  3. package/dist/cli-Bf3yjqzE.js.map +1 -0
  4. package/dist/cli.d.ts +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +724 -241
  7. package/dist/cli.js.map +1 -1
  8. package/dist/{client-COPilhO_.d.ts → client-BssohYqM.d.ts} +35 -4
  9. package/dist/client-BssohYqM.d.ts.map +1 -0
  10. package/dist/flags-C-rwARqg.js +260 -0
  11. package/dist/flags-C-rwARqg.js.map +1 -0
  12. package/dist/{flows-CF8w1rPI.js → flows-WLs26_5Y.js} +405 -337
  13. package/dist/flows-WLs26_5Y.js.map +1 -0
  14. package/dist/flows.d.ts +23 -2
  15. package/dist/flows.d.ts.map +1 -1
  16. package/dist/flows.js +1 -1
  17. package/dist/{prompt-turn-CVPMWdj1.js → live-checkpoint-D5d-K9s1.js} +2487 -609
  18. package/dist/live-checkpoint-D5d-K9s1.js.map +1 -0
  19. package/dist/output-DPg20dvn.js +4146 -0
  20. package/dist/output-DPg20dvn.js.map +1 -0
  21. package/dist/runtime.d.ts +56 -4
  22. package/dist/runtime.d.ts.map +1 -1
  23. package/dist/runtime.js +676 -393
  24. package/dist/runtime.js.map +1 -1
  25. package/dist/{types-CVBeQyi3.d.ts → session-options-CFudjdkU.d.ts} +62 -3
  26. package/dist/session-options-CFudjdkU.d.ts.map +1 -0
  27. package/package.json +30 -25
  28. package/skills/acpx/SKILL.md +211 -13
  29. package/dist/cli-T-Z-9x6a.js.map +0 -1
  30. package/dist/client-COPilhO_.d.ts.map +0 -1
  31. package/dist/flags-Dj-IXgo9.js +0 -163
  32. package/dist/flags-Dj-IXgo9.js.map +0 -1
  33. package/dist/flows-CF8w1rPI.js.map +0 -1
  34. package/dist/ipc-ABXlXzGP.js +0 -1290
  35. package/dist/ipc-ABXlXzGP.js.map +0 -1
  36. package/dist/jsonrpc-DSxh2w5R.js +0 -68
  37. package/dist/jsonrpc-DSxh2w5R.js.map +0 -1
  38. package/dist/output-DmHvT8vm.js +0 -807
  39. package/dist/output-DmHvT8vm.js.map +0 -1
  40. package/dist/perf-metrics-C2pXfxvR.js +0 -598
  41. package/dist/perf-metrics-C2pXfxvR.js.map +0 -1
  42. package/dist/prompt-turn-CVPMWdj1.js.map +0 -1
  43. package/dist/render-N5YwotCy.js +0 -172
  44. package/dist/render-N5YwotCy.js.map +0 -1
  45. package/dist/rolldown-runtime-CiIaOW0V.js +0 -13
  46. package/dist/session-CDaQe6BH.js +0 -1538
  47. package/dist/session-CDaQe6BH.js.map +0 -1
  48. package/dist/session-options-pCbHn_n7.d.ts +0 -13
  49. package/dist/session-options-pCbHn_n7.d.ts.map +0 -1
  50. package/dist/types-CVBeQyi3.d.ts.map +0 -1
@@ -1,7 +1,5 @@
1
- import { M as SESSION_RECORD_SCHEMA, g as textPrompt, h as promptToDisplayText, j as PERMISSION_MODES } from "./perf-metrics-C2pXfxvR.js";
2
- import { F as resolveSessionRecord, J as withInterrupt, K as InterruptedError, Y as withTimeout, b as recordPromptSubmission, g as cloneSessionAcpxState, q as TimeoutError, v as createSessionConversation, x as recordSessionUpdate, y as recordClientOperation, z as defaultSessionEventLog } from "./prompt-turn-CVPMWdj1.js";
3
- import { c as createSessionWithClient, i as sendSessionDirect, l as cancelSessionPrompt, r as runOnce } from "./session-CDaQe6BH.js";
4
- import { t as createOutputFormatter } from "./output-DmHvT8vm.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";
5
3
  import path from "node:path";
6
4
  import fs from "node:fs/promises";
7
5
  import os from "node:os";
@@ -238,6 +236,15 @@ function renderShellCommand(command, args) {
238
236
  const renderedArgs = args.map((arg) => JSON.stringify(arg)).join(" ");
239
237
  return renderedArgs.length > 0 ? `${command} ${renderedArgs}` : command;
240
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
+ }
241
248
  async function runShellAction(spec) {
242
249
  const cwd = spec.cwd ?? process.cwd();
243
250
  const args = spec.args ?? [];
@@ -282,12 +289,9 @@ async function runShellAction(spec) {
282
289
  signal,
283
290
  durationMs: Date.now() - startMs
284
291
  };
285
- if (timedOut) {
286
- reject(new TimeoutError(spec.timeoutMs ?? 0));
287
- return;
288
- }
289
- if (((exitCode ?? 0) !== 0 || signal != null) && spec.allowNonZeroExit !== true) {
290
- 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);
291
295
  return;
292
296
  }
293
297
  resolve(result);
@@ -314,16 +318,20 @@ function validateFlowDefinition(flow) {
314
318
  assertValidFlowDefinitionShape(flow);
315
319
  if (!flow.nodes[flow.startAt]) throw new Error(`Flow start node is missing: ${flow.startAt}`);
316
320
  const outgoingEdges = /* @__PURE__ */ new Set();
317
- for (const edge of flow.edges) {
318
- if (!flow.nodes[edge.from]) throw new Error(`Flow edge references unknown from-node: ${edge.from}`);
319
- if (outgoingEdges.has(edge.from)) throw new Error(`Flow node must not declare multiple outgoing edges: ${edge.from}`);
320
- outgoingEdges.add(edge.from);
321
- if ("to" in edge) {
322
- if (!flow.nodes[edge.to]) throw new Error(`Flow edge references unknown to-node: ${edge.to}`);
323
- continue;
324
- }
325
- 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;
326
333
  }
334
+ for (const target of Object.values(edge.switch.cases)) assertKnownFlowNode(flow, target, "Flow switch references unknown to-node");
327
335
  }
328
336
  function resolveNext(edges, from, output, result) {
329
337
  const edge = edges.find((candidate) => candidate.from === from);
@@ -586,14 +594,16 @@ function extractAttachedStepTrace(error) {
586
594
  }
587
595
  function toInlineOutput(value) {
588
596
  if (value == null || typeof value === "number" || typeof value === "boolean") return value;
589
- 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;
590
598
  try {
591
- const serialized = JSON.stringify(value);
592
- if (serialized.length <= 200 && !serialized.includes("\n")) return value;
599
+ if (isInlineSerializableText(JSON.stringify(value))) return value;
593
600
  } catch {
594
601
  return;
595
602
  }
596
603
  }
604
+ function isInlineSerializableText(value) {
605
+ return value.length <= 200 && !value.includes("\n");
606
+ }
597
607
  function outputArtifactMediaType(value) {
598
608
  return typeof value === "string" ? "text/plain" : "application/json";
599
609
  }
@@ -887,24 +897,9 @@ function createFlowDefinitionSnapshot(flow) {
887
897
  };
888
898
  }
889
899
  function snapshotNode(node) {
890
- const common = {
891
- nodeType: node.nodeType,
892
- ...node.timeoutMs !== void 0 ? { timeoutMs: node.timeoutMs } : {},
893
- ...node.heartbeatMs !== void 0 ? { heartbeatMs: node.heartbeatMs } : {},
894
- ...node.statusDetail ? { statusDetail: node.statusDetail } : {}
895
- };
900
+ const common = snapshotCommonNodeFields(node);
896
901
  switch (node.nodeType) {
897
- case "acp": return {
898
- ...common,
899
- ...node.profile ? { profile: node.profile } : {},
900
- session: {
901
- ...node.session?.handle ? { handle: node.session.handle } : {},
902
- ...node.session?.isolated ? { isolated: true } : {}
903
- },
904
- cwd: snapshotCwd(node.cwd),
905
- hasPrompt: true,
906
- hasParse: typeof node.parse === "function"
907
- };
902
+ case "acp": return snapshotAcpNode(node, common);
908
903
  case "compute": return {
909
904
  ...common,
910
905
  hasRun: true
@@ -927,6 +922,27 @@ function snapshotNode(node) {
927
922
  }
928
923
  throw new Error(`Unsupported flow node type: ${String(node)}`);
929
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
+ }
930
946
  function snapshotCwd(cwd) {
931
947
  if (typeof cwd === "function") return { mode: "dynamic" };
932
948
  if (typeof cwd === "string") return {
@@ -989,6 +1005,7 @@ var FlowRunner = class {
989
1005
  permissionMode;
990
1006
  mcpServers;
991
1007
  nonInteractivePermissions;
1008
+ permissionPolicy;
992
1009
  authCredentials;
993
1010
  authPolicy;
994
1011
  timeoutMs;
@@ -1005,6 +1022,7 @@ var FlowRunner = class {
1005
1022
  this.permissionMode = options.permissionMode;
1006
1023
  this.mcpServers = options.mcpServers;
1007
1024
  this.nonInteractivePermissions = options.nonInteractivePermissions;
1025
+ this.permissionPolicy = options.permissionPolicy;
1008
1026
  this.authCredentials = options.authCredentials;
1009
1027
  this.authPolicy = options.authPolicy;
1010
1028
  this.timeoutMs = options.timeoutMs;
@@ -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;
@@ -1521,6 +1556,7 @@ var FlowRunner = class {
1521
1556
  mcpServers: this.mcpServers,
1522
1557
  permissionMode: this.permissionMode,
1523
1558
  nonInteractivePermissions: this.nonInteractivePermissions,
1559
+ permissionPolicy: this.permissionPolicy,
1524
1560
  authCredentials: this.authCredentials,
1525
1561
  authPolicy: this.authPolicy,
1526
1562
  timeoutMs,
@@ -1569,6 +1605,7 @@ var FlowRunner = class {
1569
1605
  mcpServers: this.mcpServers,
1570
1606
  permissionMode: this.permissionMode,
1571
1607
  nonInteractivePermissions: this.nonInteractivePermissions,
1608
+ permissionPolicy: this.permissionPolicy,
1572
1609
  authCredentials: this.authCredentials,
1573
1610
  authPolicy: this.authPolicy,
1574
1611
  outputFormatter: capture.formatter,
@@ -1632,6 +1669,7 @@ var FlowRunner = class {
1632
1669
  mcpServers: this.mcpServers,
1633
1670
  permissionMode: this.permissionMode,
1634
1671
  nonInteractivePermissions: this.nonInteractivePermissions,
1672
+ permissionPolicy: this.permissionPolicy,
1635
1673
  authCredentials: this.authCredentials,
1636
1674
  authPolicy: this.authPolicy,
1637
1675
  outputFormatter: capture.formatter,
@@ -1700,18 +1738,25 @@ function parseJsonObject(text, options = {}) {
1700
1738
  const mode = options.mode ?? "compat";
1701
1739
  const direct = tryParse(trimmed);
1702
1740
  if (direct.ok) return direct.value;
1703
- if (mode === "fenced" || mode === "compat") {
1704
- const fencedText = extractFencedJsonText(trimmed);
1705
- if (fencedText !== null) {
1706
- const fenced = tryParse(fencedText);
1707
- if (fenced.ok) return fenced.value;
1708
- }
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;
1709
1746
  }
1710
- 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)) {
1711
1756
  const parsed = tryParse(candidate);
1712
- if (parsed.ok) return parsed.value;
1757
+ if (parsed.ok) return parsed;
1713
1758
  }
1714
- throw new Error(`Could not parse JSON from assistant output:\n${trimmed}`);
1759
+ return { ok: false };
1715
1760
  }
1716
1761
  function parseStrictJsonObject(text) {
1717
1762
  return parseJsonObject(text, { mode: "strict" });
@@ -1758,27 +1803,50 @@ function scanBalanced(text, startIndex) {
1758
1803
  for (let index = startIndex; index < text.length; index += 1) {
1759
1804
  const char = text[index];
1760
1805
  if (inString) {
1761
- if (escaped) escaped = false;
1762
- else if (char === "\\") escaped = true;
1763
- else if (char === "\"") inString = false;
1806
+ const next = scanStringChar(char, escaped);
1807
+ escaped = next.escaped;
1808
+ inString = next.inString;
1764
1809
  continue;
1765
1810
  }
1766
1811
  if (char === "\"") {
1767
1812
  inString = true;
1768
1813
  continue;
1769
1814
  }
1770
- if (char === "{" || char === "[") {
1771
- stack.push(char);
1772
- continue;
1773
- }
1774
- if (char !== "}" && char !== "]") continue;
1775
- const last = stack.at(-1);
1776
- if (last === "{" && char !== "}" || last === "[" && char !== "]") return null;
1777
- stack.pop();
1778
- 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;
1779
1817
  }
1780
1818
  return null;
1781
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
+ }
1782
1850
  //#endregion
1783
1851
  //#region src/flows/decision.ts
1784
1852
  const DEFAULT_FIELD = "route";
@@ -1848,4 +1916,4 @@ function formatDecisionPrompt(question, choices, field) {
1848
1916
  //#endregion
1849
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 };
1850
1918
 
1851
- //# sourceMappingURL=flows-CF8w1rPI.js.map
1919
+ //# sourceMappingURL=flows-WLs26_5Y.js.map