@skaile/workspaces 2.2.0 → 2.4.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 (207) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +35 -12
  3. package/dist/{asset-feeds-D2LOXTOS.js → asset-feeds-4AWAT5PW.js} +6 -6
  4. package/dist/{asset-feeds-D2LOXTOS.js.map → asset-feeds-4AWAT5PW.js.map} +1 -1
  5. package/dist/asset-manager/catalog-deployer.js +1 -1
  6. package/dist/asset-manager/index.js +5 -5
  7. package/dist/asset-manager/installer.js +2 -2
  8. package/dist/bridge/drivers/claude-sdk.js +5 -2
  9. package/dist/bridge/drivers/claude-sdk.js.map +1 -1
  10. package/dist/bridge/drivers/codex.js +1 -1
  11. package/dist/bridge/drivers/continue.js +2 -2
  12. package/dist/bridge/drivers/echo.js +1 -1
  13. package/dist/bridge/drivers/gemini.js +3 -3
  14. package/dist/bridge/drivers/goose.js +2 -2
  15. package/dist/bridge/drivers/omp.js +1 -1
  16. package/dist/bridge/drivers/opencode.js +2 -2
  17. package/dist/bridge/drivers/qwen.js +3 -3
  18. package/dist/bridge/index.js +2 -2
  19. package/dist/bridge/src/drivers/claude-sdk.d.ts.map +1 -1
  20. package/dist/{chunk-CERGXDGG.js → chunk-2OCYS5EP.js} +3 -3
  21. package/dist/{chunk-CERGXDGG.js.map → chunk-2OCYS5EP.js.map} +1 -1
  22. package/dist/{chunk-TQIU4HQG.js → chunk-2QVPCZTL.js} +101 -40
  23. package/dist/chunk-2QVPCZTL.js.map +1 -0
  24. package/dist/{chunk-A67FJ4YT.js → chunk-3HVASHBF.js} +3 -3
  25. package/dist/{chunk-A67FJ4YT.js.map → chunk-3HVASHBF.js.map} +1 -1
  26. package/dist/chunk-4O2RAXKJ.js +446 -0
  27. package/dist/chunk-4O2RAXKJ.js.map +1 -0
  28. package/dist/chunk-4QROZZ5Z.js +50 -0
  29. package/dist/chunk-4QROZZ5Z.js.map +1 -0
  30. package/dist/{chunk-VSSAJYFK.js → chunk-57RFYEVM.js} +3 -3
  31. package/dist/{chunk-VSSAJYFK.js.map → chunk-57RFYEVM.js.map} +1 -1
  32. package/dist/{chunk-SZJCUC52.js → chunk-6BV5NAPD.js} +156 -28
  33. package/dist/chunk-6BV5NAPD.js.map +1 -0
  34. package/dist/{chunk-MAIKGYFR.js → chunk-AKVAZ5CB.js} +2 -2
  35. package/dist/{chunk-MAIKGYFR.js.map → chunk-AKVAZ5CB.js.map} +1 -1
  36. package/dist/{chunk-BFIK6NPZ.js → chunk-AM5LFRXJ.js} +3 -3
  37. package/dist/{chunk-BFIK6NPZ.js.map → chunk-AM5LFRXJ.js.map} +1 -1
  38. package/dist/{chunk-6QSGCHE5.js → chunk-EEHOMCRU.js} +1071 -123
  39. package/dist/chunk-EEHOMCRU.js.map +1 -0
  40. package/dist/{chunk-6I5VRIX3.js → chunk-ENBTXMPL.js} +7 -7
  41. package/dist/{chunk-6I5VRIX3.js.map → chunk-ENBTXMPL.js.map} +1 -1
  42. package/dist/{chunk-Y662DNQQ.js → chunk-FKBQRV4J.js} +68 -24
  43. package/dist/chunk-FKBQRV4J.js.map +1 -0
  44. package/dist/{chunk-BUPFJFGP.js → chunk-G5V6YCHF.js} +3 -3
  45. package/dist/{chunk-BUPFJFGP.js.map → chunk-G5V6YCHF.js.map} +1 -1
  46. package/dist/{chunk-KGIVZOLU.js → chunk-GM2X24Q6.js} +2 -2
  47. package/dist/{chunk-KGIVZOLU.js.map → chunk-GM2X24Q6.js.map} +1 -1
  48. package/dist/{chunk-5BH6ZEGC.js → chunk-IHX2SN35.js} +9 -9
  49. package/dist/{chunk-5BH6ZEGC.js.map → chunk-IHX2SN35.js.map} +1 -1
  50. package/dist/{chunk-6HPTB6KT.js → chunk-JAFEMG7W.js} +4 -4
  51. package/dist/{chunk-6HPTB6KT.js.map → chunk-JAFEMG7W.js.map} +1 -1
  52. package/dist/{chunk-I5ESMJMW.js → chunk-MADO7PH5.js} +2 -2
  53. package/dist/{chunk-I5ESMJMW.js.map → chunk-MADO7PH5.js.map} +1 -1
  54. package/dist/{chunk-BCTU4TN7.js → chunk-MWFYXUFK.js} +3 -3
  55. package/dist/{chunk-BCTU4TN7.js.map → chunk-MWFYXUFK.js.map} +1 -1
  56. package/dist/{chunk-MAN7AGQJ.js → chunk-NU4E7C5I.js} +25 -6
  57. package/dist/chunk-NU4E7C5I.js.map +1 -0
  58. package/dist/{chunk-RKMXBOIO.js → chunk-O7ROIKTF.js} +3 -3
  59. package/dist/{chunk-RKMXBOIO.js.map → chunk-O7ROIKTF.js.map} +1 -1
  60. package/dist/chunk-OEGHKO5N.js +1236 -0
  61. package/dist/chunk-OEGHKO5N.js.map +1 -0
  62. package/dist/{chunk-63ZPKREA.js → chunk-P43HBWKK.js} +11 -4
  63. package/dist/chunk-P43HBWKK.js.map +1 -0
  64. package/dist/{chunk-IR6VPAVH.js → chunk-PJ6INYDD.js} +3 -3
  65. package/dist/{chunk-IR6VPAVH.js.map → chunk-PJ6INYDD.js.map} +1 -1
  66. package/dist/{chunk-DHGMYXOY.js → chunk-UGKOWIRP.js} +4 -4
  67. package/dist/{chunk-DHGMYXOY.js.map → chunk-UGKOWIRP.js.map} +1 -1
  68. package/dist/{chunk-WA7ADYSA.js → chunk-UZYTSIX6.js} +6 -6
  69. package/dist/{chunk-WA7ADYSA.js.map → chunk-UZYTSIX6.js.map} +1 -1
  70. package/dist/chunk-V4ZLZJFN.js +86 -0
  71. package/dist/chunk-V4ZLZJFN.js.map +1 -0
  72. package/dist/{chunk-GPMIXOJJ.js → chunk-WSARNOPP.js} +5 -5
  73. package/dist/{chunk-GPMIXOJJ.js.map → chunk-WSARNOPP.js.map} +1 -1
  74. package/dist/cli/index.js +49 -46
  75. package/dist/cli/index.js.map +1 -1
  76. package/dist/connectors/config.js +1 -1
  77. package/dist/connectors/index.js +6 -4
  78. package/dist/core/index.js +1 -1
  79. package/dist/core/src/subprocess.d.ts +5 -1
  80. package/dist/core/src/subprocess.d.ts.map +1 -1
  81. package/dist/deploy/index.js +1 -1
  82. package/dist/discovery/index.js +1 -1
  83. package/dist/discovery/src/requires-graph.d.ts +1 -1
  84. package/dist/discovery/src/requires-graph.d.ts.map +1 -1
  85. package/dist/{ensure-sources-6QMXRI6D.js → ensure-sources-QF72HVUM.js} +8 -8
  86. package/dist/{ensure-sources-6QMXRI6D.js.map → ensure-sources-QF72HVUM.js.map} +1 -1
  87. package/dist/factory-assets/connectors/box.js +6 -4
  88. package/dist/factory-assets/connectors/deploy.js +6 -4
  89. package/dist/factory-assets/connectors/devserver.js +6 -4
  90. package/dist/factory-assets/connectors/flow/CONNECTOR.md +63 -10
  91. package/dist/factory-assets/connectors/flow/adapter.d.ts +79 -36
  92. package/dist/factory-assets/connectors/flow/adapter.d.ts.map +1 -1
  93. package/dist/factory-assets/connectors/flow/adapter.js +6 -4
  94. package/dist/factory-assets/connectors/flow/contract/expression.d.ts +55 -0
  95. package/dist/factory-assets/connectors/flow/contract/expression.d.ts.map +1 -1
  96. package/dist/factory-assets/connectors/flow/contract/flow.v2.schema.json +61 -134
  97. package/dist/factory-assets/connectors/flow/contract/index.d.ts +1 -1
  98. package/dist/factory-assets/connectors/flow/contract/index.d.ts.map +1 -1
  99. package/dist/factory-assets/connectors/flow/contract/normalize.d.ts +4 -1
  100. package/dist/factory-assets/connectors/flow/contract/normalize.d.ts.map +1 -1
  101. package/dist/factory-assets/connectors/flow/contract/schema.d.ts +20 -8
  102. package/dist/factory-assets/connectors/flow/contract/schema.d.ts.map +1 -1
  103. package/dist/factory-assets/connectors/flow/contract.js +2 -1024
  104. package/dist/factory-assets/connectors/flow/contract.js.map +1 -1
  105. package/dist/factory-assets/connectors/flow/engine/bindings.d.ts +9 -0
  106. package/dist/factory-assets/connectors/flow/engine/bindings.d.ts.map +1 -0
  107. package/dist/factory-assets/connectors/flow/engine/engine.d.ts +11 -6
  108. package/dist/factory-assets/connectors/flow/engine/engine.d.ts.map +1 -1
  109. package/dist/factory-assets/connectors/flow/engine/flow-kind-provider.d.ts +1 -1
  110. package/dist/factory-assets/connectors/flow/engine/flow-kind-provider.d.ts.map +1 -1
  111. package/dist/factory-assets/connectors/flow/engine/index.d.ts +2 -0
  112. package/dist/factory-assets/connectors/flow/engine/index.d.ts.map +1 -1
  113. package/dist/factory-assets/connectors/flow/engine/loader.d.ts.map +1 -1
  114. package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts +42 -0
  115. package/dist/factory-assets/connectors/flow/engine/node-shape.d.ts.map +1 -0
  116. package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts +18 -0
  117. package/dist/factory-assets/connectors/flow/engine/output-schema.d.ts.map +1 -0
  118. package/dist/factory-assets/connectors/flow/engine/types.d.ts +67 -33
  119. package/dist/factory-assets/connectors/flow/engine/types.d.ts.map +1 -1
  120. package/dist/factory-assets/connectors/flow/engine.js +4 -2
  121. package/dist/factory-assets/connectors/flow/index.d.ts +3 -0
  122. package/dist/factory-assets/connectors/flow/index.d.ts.map +1 -1
  123. package/dist/factory-assets/connectors/flow/inline-sub-flow-host.d.ts +48 -0
  124. package/dist/factory-assets/connectors/flow/inline-sub-flow-host.d.ts.map +1 -0
  125. package/dist/factory-assets/connectors/flow/node-executor.d.ts +45 -0
  126. package/dist/factory-assets/connectors/flow/node-executor.d.ts.map +1 -0
  127. package/dist/factory-assets/connectors/flow/prompt-fragments.d.ts +5 -6
  128. package/dist/factory-assets/connectors/flow/prompt-fragments.d.ts.map +1 -1
  129. package/dist/factory-assets/connectors/flow/prompt-fragments.js +2 -1
  130. package/dist/factory-assets/connectors/flow/run-flow.d.ts +10 -9
  131. package/dist/factory-assets/connectors/flow/run-flow.d.ts.map +1 -1
  132. package/dist/factory-assets/connectors/flow/run-flow.js +7 -5
  133. package/dist/factory-assets/connectors/flow/sub-flow-runner.d.ts +73 -0
  134. package/dist/factory-assets/connectors/flow/sub-flow-runner.d.ts.map +1 -0
  135. package/dist/factory-assets/connectors/flow.js +6 -4
  136. package/dist/factory-assets/connectors/git.js +6 -4
  137. package/dist/factory-assets/connectors/gmail.js +6 -4
  138. package/dist/factory-assets/connectors/googledrive.js +6 -4
  139. package/dist/factory-assets/connectors/local.js +6 -4
  140. package/dist/factory-assets/connectors/mattermost.js +6 -4
  141. package/dist/factory-assets/connectors/memory.js +6 -4
  142. package/dist/factory-assets/connectors/minio.js +6 -4
  143. package/dist/factory-assets/connectors/postgres.js +6 -4
  144. package/dist/factory-assets/connectors/s3.js +6 -4
  145. package/dist/factory-assets/connectors/sharepoint.js +6 -4
  146. package/dist/factory-assets/connectors/sqlite.js +6 -4
  147. package/dist/factory-assets/connectors/static-server.js +6 -4
  148. package/dist/factory-assets/connectors/tunnel.js +6 -4
  149. package/dist/factory-assets/connectors/webdav.js +6 -4
  150. package/dist/factory-assets/connectors/xstate-store.js +6 -4
  151. package/dist/factory-assets/connectors/xstate.js +6 -4
  152. package/dist/{flows-TDDBCLNF.js → flows-YIW4HL3L.js} +4 -4
  153. package/dist/{flows-TDDBCLNF.js.map → flows-YIW4HL3L.js.map} +1 -1
  154. package/dist/{helpers-COQ7TDD4.js → helpers-LURCZHA4.js} +8 -8
  155. package/dist/{helpers-COQ7TDD4.js.map → helpers-LURCZHA4.js.map} +1 -1
  156. package/dist/library/index.js +3 -3
  157. package/dist/library/install.js +1 -1
  158. package/dist/{library-publish-SNPER6PO.js → library-publish-2GMJM3NZ.js} +14 -12
  159. package/dist/library-publish-2GMJM3NZ.js.map +1 -0
  160. package/dist/{open-library-VH2VEYQ7.js → open-library-POFOXZWH.js} +12 -10
  161. package/dist/{open-library-VH2VEYQ7.js.map → open-library-POFOXZWH.js.map} +1 -1
  162. package/dist/{paths-XZA22IB4.js → paths-ZLX4ITAQ.js} +6 -6
  163. package/dist/{paths-XZA22IB4.js.map → paths-ZLX4ITAQ.js.map} +1 -1
  164. package/dist/{plugin-store-CYSNVZIX.js → plugin-store-YIMNH2FH.js} +3 -3
  165. package/dist/{plugin-store-CYSNVZIX.js.map → plugin-store-YIMNH2FH.js.map} +1 -1
  166. package/dist/{project-FKL7PMJV.js → project-GVOPZEJZ.js} +9 -9
  167. package/dist/{project-FKL7PMJV.js.map → project-GVOPZEJZ.js.map} +1 -1
  168. package/dist/resolver/index.js +1 -1
  169. package/dist/runner/index.js +14 -8
  170. package/dist/runner/src/serve.d.ts +3 -1
  171. package/dist/runner/src/serve.d.ts.map +1 -1
  172. package/dist/sdk/asset-manager.js +5 -5
  173. package/dist/sdk/bridge.js +2 -2
  174. package/dist/sdk/core.js +1 -1
  175. package/dist/sdk/flow.js +4 -2
  176. package/dist/sdk/index.js +14 -8
  177. package/dist/sdk/index.js.map +1 -1
  178. package/dist/sdk/resolver.js +1 -1
  179. package/dist/sdk/runner.js +14 -8
  180. package/dist/{setup-BM6HMYJJ.js → setup-TJYLYT2A.js} +5 -5
  181. package/dist/{setup-BM6HMYJJ.js.map → setup-TJYLYT2A.js.map} +1 -1
  182. package/dist/{skills-LHS4TOKK.js → skills-HGVH6UTA.js} +5 -5
  183. package/dist/{skills-LHS4TOKK.js.map → skills-HGVH6UTA.js.map} +1 -1
  184. package/dist/{source-JIUBJC4F.js → source-FB3T4IHP.js} +13 -11
  185. package/dist/{source-JIUBJC4F.js.map → source-FB3T4IHP.js.map} +1 -1
  186. package/dist/{source-manifest-publish-TLH6UWWU.js → source-manifest-publish-XTRDVPSC.js} +15 -13
  187. package/dist/source-manifest-publish-XTRDVPSC.js.map +1 -0
  188. package/dist/tui/index.js +14 -8
  189. package/dist/tui/index.js.map +1 -1
  190. package/dist/types/src/flow.d.ts +26 -0
  191. package/dist/types/src/flow.d.ts.map +1 -1
  192. package/dist/types/src/index.d.ts +1 -1
  193. package/dist/types/src/index.d.ts.map +1 -1
  194. package/dist/workspace-plugin/adapters/mcp.js +4 -4
  195. package/dist/workspace-plugin/adapters/omp.js +7 -7
  196. package/dist/workspace-plugin/index.js +1 -1
  197. package/package.json +1 -1
  198. package/dist/chunk-63ZPKREA.js.map +0 -1
  199. package/dist/chunk-6QSGCHE5.js.map +0 -1
  200. package/dist/chunk-HPGV54O4.js +0 -301
  201. package/dist/chunk-HPGV54O4.js.map +0 -1
  202. package/dist/chunk-MAN7AGQJ.js.map +0 -1
  203. package/dist/chunk-SZJCUC52.js.map +0 -1
  204. package/dist/chunk-TQIU4HQG.js.map +0 -1
  205. package/dist/chunk-Y662DNQQ.js.map +0 -1
  206. package/dist/library-publish-SNPER6PO.js.map +0 -1
  207. package/dist/source-manifest-publish-TLH6UWWU.js.map +0 -1
@@ -1,14 +1,16 @@
1
1
  import { resolveAuthRef } from './chunk-ZNIHTT22.js';
2
2
  import { pluginRegistry } from './chunk-6E6PKKAD.js';
3
3
  import { computeStimulus } from './chunk-IFGTRL2I.js';
4
- import { computeFlowStateFromSnapshots } from './chunk-HPGV54O4.js';
5
- import { renderStimulusPrompt } from './chunk-Y662DNQQ.js';
6
- import { portableSpawn } from './chunk-63ZPKREA.js';
4
+ import { getConsumedOutputSchema, validateConsumedNodeOutput, resolveNodeBindings, computeFlowStateFromSnapshots, interpolateNodeInstruction } from './chunk-4O2RAXKJ.js';
5
+ import { parseFlowDefinition } from './chunk-OEGHKO5N.js';
6
+ import { renderStimulusPrompt } from './chunk-FKBQRV4J.js';
7
+ import { flowDefaults, isAgentNode, isGateNode, nodeRequiresApproval, isSubFlowNode, isFunctionNode, nodeIsOptional, isActiveRuntimeNode, nodeLabel, nodeInstruction, nodeAssets } from './chunk-4QROZZ5Z.js';
8
+ import { portableSpawn } from './chunk-P43HBWKK.js';
7
9
  import { withFsDeadline, tryWithFsDeadline, FsDeadlineError, fsDeadlineMs } from './chunk-DYMWJAL4.js';
8
10
  import { createLogger } from './chunk-7GOCNYUV.js';
11
+ import { resolve, relative, sep, isAbsolute, join, dirname } from 'path';
9
12
  import { mkdir, rename, writeFile, readFile, stat, cp, readdir, chmod, realpath } from 'fs/promises';
10
- import { join, dirname, resolve, sep, relative } from 'path';
11
- import * as zNS from 'zod';
13
+ import * as z4 from 'zod';
12
14
  import { execFile } from 'child_process';
13
15
  import { promisify } from 'util';
14
16
  import { createReadStream, readFileSync, existsSync, mkdirSync, openSync, writeSync, closeSync, chmodSync, renameSync } from 'fs';
@@ -53,6 +55,326 @@ var AbstractConnector = class {
53
55
  };
54
56
  }
55
57
  };
58
+
59
+ // factory-assets/connectors/flow/inline-sub-flow-host.ts
60
+ var INLINE_SUB_FLOW_MAX_DEPTH = 16;
61
+ function isRecord(value) {
62
+ return value !== null && typeof value === "object" && !Array.isArray(value);
63
+ }
64
+ function childDefinition(request, authored) {
65
+ const childRunInput = authored.defaults?.run_input;
66
+ const parentRunInput = request.parentFlow.defaults?.run_input;
67
+ const hasBoundInputs = Object.keys(request.inputs).length > 0;
68
+ const hasParentContext = request.passContext && parentRunInput !== void 0;
69
+ if (!hasBoundInputs && !hasParentContext) return authored;
70
+ let runInput;
71
+ if ((childRunInput === void 0 || isRecord(childRunInput)) && (!hasParentContext || isRecord(parentRunInput))) {
72
+ runInput = {
73
+ ...isRecord(childRunInput) ? childRunInput : {},
74
+ ...hasParentContext && isRecord(parentRunInput) ? parentRunInput : {},
75
+ ...request.inputs
76
+ };
77
+ } else if (hasBoundInputs) {
78
+ runInput = request.inputs;
79
+ } else {
80
+ runInput = parentRunInput;
81
+ }
82
+ return {
83
+ ...authored,
84
+ defaults: {
85
+ ...authored.defaults,
86
+ run_input: runInput
87
+ }
88
+ };
89
+ }
90
+ function terminalError(execution) {
91
+ const failed = Object.values(execution.nodes).find((node) => node.status === "failed");
92
+ if (failed?.error) return failed.error;
93
+ return {
94
+ message: `Inline child flow ${execution.flowId} ended with status ${execution.status}`,
95
+ recoverable: false,
96
+ at: (/* @__PURE__ */ new Date()).toISOString()
97
+ };
98
+ }
99
+ function projection(session) {
100
+ const execution = session.adapter.getExecution(session.handle);
101
+ for (const definition of session.flow.nodes) {
102
+ const node = execution.nodes[definition.id];
103
+ if (node?.status === "awaiting_approval" && node.approval?.decision == null) {
104
+ return { status: "awaiting_approval", approval: structuredClone(node.approval) };
105
+ }
106
+ if (node?.status === "awaiting_input" && node.input?.providedAt == null) {
107
+ return { status: "awaiting_input", input: structuredClone(node.input) };
108
+ }
109
+ }
110
+ const approvals = Object.values(execution.nodes).flatMap((node) => node.approvalHistory).sort((left, right) => left.requestedAt.localeCompare(right.requestedAt));
111
+ const inputs = Object.values(execution.nodes).flatMap((node) => node.inputHistory).sort((left, right) => left.requestedAt.localeCompare(right.requestedAt));
112
+ return {
113
+ status: "running",
114
+ approval: approvals.at(-1) ? structuredClone(approvals.at(-1)) : void 0,
115
+ input: inputs.at(-1) ? structuredClone(inputs.at(-1)) : void 0
116
+ };
117
+ }
118
+ function currentInteractionNode(session, status) {
119
+ const execution = session.adapter.getExecution(session.handle);
120
+ const node = session.flow.nodes.find(
121
+ (definition) => execution.nodes[definition.id]?.status === status
122
+ );
123
+ if (!node) {
124
+ throw new Error(`Inline child ${session.flow.id} is not ${status.replaceAll("_", " ")}`);
125
+ }
126
+ return node.id;
127
+ }
128
+ var SessionInlineSubFlowRunner = class {
129
+ constructor(options) {
130
+ this.options = options;
131
+ }
132
+ options;
133
+ sessions = /* @__PURE__ */ new Map();
134
+ sessionKey(request) {
135
+ return `${request.parentExecution.runId}\0${request.node.id}`;
136
+ }
137
+ async dispose(session) {
138
+ if (session.closed) return;
139
+ session.closed = true;
140
+ this.sessions.delete(session.key);
141
+ session.request.routeOperationsTo(void 0);
142
+ await session.adapter.disconnect(session.handle);
143
+ }
144
+ async cancel(session) {
145
+ if (session.closed) return;
146
+ try {
147
+ await session.adapter.executeOp(session.handle, "cancel", {});
148
+ } finally {
149
+ await this.dispose(session);
150
+ }
151
+ }
152
+ async createSession(request) {
153
+ if (request.ancestry.includes(request.flowId)) {
154
+ throw new Error(
155
+ `Inline sub-flow cycle detected: ${[...request.ancestry, request.flowId].join(" -> ")}`
156
+ );
157
+ }
158
+ if (request.ancestry.length >= INLINE_SUB_FLOW_MAX_DEPTH) {
159
+ throw new Error(
160
+ `Inline sub-flow depth exceeds ${INLINE_SUB_FLOW_MAX_DEPTH}: ${[
161
+ ...request.ancestry,
162
+ request.flowId
163
+ ].join(" -> ")}`
164
+ );
165
+ }
166
+ const authored = await this.options.resolveFlow(request.flowId);
167
+ if (request.ancestry.includes(authored.id)) {
168
+ throw new Error(
169
+ `Inline sub-flow cycle detected after resolving ${request.flowId}: ${[
170
+ ...request.ancestry,
171
+ authored.id
172
+ ].join(" -> ")}`
173
+ );
174
+ }
175
+ const flow = childDefinition(request, authored);
176
+ const adapter = new FlowAdapter();
177
+ const handle = await adapter.connect(
178
+ {
179
+ id: `flow:${request.parentExecution.runId}:${request.node.id}`,
180
+ driver: "flow",
181
+ access: "read-write",
182
+ options: {
183
+ flow,
184
+ flowInputOrigins: request.inputOrigins,
185
+ inlineSubFlowAncestry: [...request.ancestry, flow.id],
186
+ inlineSubFlowRunner: this,
187
+ nodeExecutor: this.options.nodeExecutor,
188
+ seed: {
189
+ runId: `${request.parentExecution.runId}:${request.node.id}`,
190
+ startedBy: request.parentExecution.startedBy,
191
+ autonomousMode: request.parentExecution.autonomousMode
192
+ }
193
+ }
194
+ },
195
+ {}
196
+ );
197
+ const key = this.sessionKey(request);
198
+ let session;
199
+ const target = {
200
+ parentNodeId: request.node.id,
201
+ isAgentTurnActive: () => session.agentTurnActive || adapter.hasActiveInlineAgentTurn(handle),
202
+ executeAgentOp: (operation, args) => adapter.executeOp(handle, operation, args),
203
+ applyParentApproval: async (args) => {
204
+ const nodeId = currentInteractionNode(session, "awaiting_approval");
205
+ await adapter.executeOp(handle, "applyApproval", { ...args, nodeId });
206
+ session.stimulus = {
207
+ kind: "approval_received",
208
+ nodeId,
209
+ decision: args.decision === "rejected" ? "rejected" : "approved",
210
+ feedback: typeof args.feedback === "string" ? args.feedback : void 0,
211
+ decidedBy: String(args.decidedBy ?? "")
212
+ };
213
+ return projection(session);
214
+ },
215
+ applyParentInput: async (args) => {
216
+ const nodeId = currentInteractionNode(session, "awaiting_input");
217
+ await adapter.executeOp(handle, "applyInput", { ...args, nodeId });
218
+ session.stimulus = {
219
+ kind: "input_received",
220
+ nodeId,
221
+ response: args.response,
222
+ providedBy: String(args.providedBy ?? "")
223
+ };
224
+ return projection(session);
225
+ },
226
+ cancel: () => this.cancel(session),
227
+ getExecution: () => adapter.getAgentExecution(handle),
228
+ getMandatoryAgentApprovalNodeIds: () => adapter.getMandatoryAgentApprovalNodeIds(handle)
229
+ };
230
+ session = {
231
+ key,
232
+ request,
233
+ adapter,
234
+ handle,
235
+ flow,
236
+ target,
237
+ stimulus: { kind: "flow_started" },
238
+ changeVersion: 0,
239
+ agentTurnActive: false,
240
+ closed: false
241
+ };
242
+ adapter.onStateChange(handle, () => {
243
+ session.changeVersion += 1;
244
+ });
245
+ this.sessions.set(key, session);
246
+ request.routeOperationsTo(target);
247
+ return session;
248
+ }
249
+ async run(request) {
250
+ const key = this.sessionKey(request);
251
+ const session = this.sessions.get(key) ?? await this.createSession(request);
252
+ session.request = request;
253
+ request.routeOperationsTo(session.target);
254
+ try {
255
+ while (!session.closed) {
256
+ await session.adapter.drainRuntimeNodes(session.handle);
257
+ const execution = session.adapter.getExecution(session.handle);
258
+ if (execution.done || execution.status === "complete") {
259
+ await this.dispose(session);
260
+ return {
261
+ status: "complete",
262
+ output: {
263
+ summary: `Inline child flow ${session.flow.id} completed`,
264
+ artifacts: []
265
+ }
266
+ };
267
+ }
268
+ if (execution.status === "failed" || execution.status === "cancelled") {
269
+ const error = terminalError(execution);
270
+ await this.dispose(session);
271
+ return { status: "failed", error };
272
+ }
273
+ if (session.adapter.shouldDriveAgentTurn(session.handle, session.stimulus)) {
274
+ const versionBeforeTurn = session.changeVersion;
275
+ session.agentTurnActive = true;
276
+ try {
277
+ await this.options.driveAgentTurn({
278
+ adapter: session.adapter,
279
+ handle: session.handle,
280
+ flow: session.flow,
281
+ execution,
282
+ stimulus: session.stimulus
283
+ });
284
+ } finally {
285
+ session.agentTurnActive = false;
286
+ }
287
+ if (session.closed) {
288
+ return { status: "failed", error: terminalError(execution) };
289
+ }
290
+ session.stimulus = { kind: "state_changed" };
291
+ if (session.changeVersion === versionBeforeTurn) {
292
+ return { status: "paused", projection: projection(session) };
293
+ }
294
+ continue;
295
+ }
296
+ return { status: "paused", projection: projection(session) };
297
+ }
298
+ return {
299
+ status: "failed",
300
+ error: {
301
+ message: `Inline child flow ${request.flowId} was cancelled`,
302
+ recoverable: false,
303
+ at: (/* @__PURE__ */ new Date()).toISOString()
304
+ }
305
+ };
306
+ } catch (error) {
307
+ try {
308
+ await this.dispose(session);
309
+ } catch {
310
+ }
311
+ throw error;
312
+ }
313
+ }
314
+ };
315
+ function stableJsonStringify(value) {
316
+ return JSON.stringify(value, (_key, nested) => {
317
+ if (nested === null || Array.isArray(nested) || typeof nested !== "object") return nested;
318
+ return Object.fromEntries(
319
+ Object.entries(nested).sort(([left], [right]) => {
320
+ if (left < right) return -1;
321
+ if (left > right) return 1;
322
+ return 0;
323
+ })
324
+ );
325
+ });
326
+ }
327
+ function serializeEnvironmentInput(name, value) {
328
+ if (typeof value === "string") return value;
329
+ const serialized = stableJsonStringify(value);
330
+ if (serialized === void 0) {
331
+ throw new Error(`Function node input ${name} is not JSON-serializable`);
332
+ }
333
+ return serialized;
334
+ }
335
+ function commandFor(node) {
336
+ switch (node.run.runtime ?? "shell") {
337
+ case "shell":
338
+ return ["sh", "-c", node.run.command];
339
+ case "node":
340
+ return ["node", "-e", node.run.command];
341
+ case "python":
342
+ return ["python3", "-c", node.run.command];
343
+ }
344
+ }
345
+ var SessionNodeExecutor = class {
346
+ workspaceDir;
347
+ /** Creates an executor scoped to one already-materialized session workspace. */
348
+ constructor(options) {
349
+ this.workspaceDir = resolve(options.workspaceDir);
350
+ }
351
+ /** Runs one function node process with session environment and binding overrides. */
352
+ async execute(request) {
353
+ const cwd = resolve(this.workspaceDir, request.node.run.cwd ?? ".");
354
+ const workspaceRelative = relative(this.workspaceDir, cwd);
355
+ if (workspaceRelative === ".." || workspaceRelative.startsWith(`..${sep}`) || isAbsolute(workspaceRelative)) {
356
+ throw new Error(
357
+ `Function node ${request.node.id} cwd escapes the session workspace: ${request.node.run.cwd}`
358
+ );
359
+ }
360
+ const boundEnvironment = Object.fromEntries(
361
+ Object.entries(request.inputs).map(([name, value]) => [
362
+ name,
363
+ serializeEnvironmentInput(name, value)
364
+ ])
365
+ );
366
+ const process2 = portableSpawn(commandFor(request.node), {
367
+ cwd,
368
+ env: { ...globalThis.process.env, ...boundEnvironment }
369
+ });
370
+ const [stdout, stderr, exitStatus] = await Promise.all([
371
+ new Response(process2.stdout).text(),
372
+ new Response(process2.stderr).text(),
373
+ process2.exited
374
+ ]);
375
+ return { exitStatus, stdout, stderr };
376
+ }
377
+ };
56
378
  var execFileAsync = promisify(execFile);
57
379
  var GIT_REV_PARSE_TIMEOUT_MS = 15e3;
58
380
  var DeployConnector = class extends AbstractConnector {
@@ -229,7 +551,7 @@ var DeployConnector = class extends AbstractConnector {
229
551
  }
230
552
  }
231
553
  _mockPush(state, opts) {
232
- const record = {
554
+ const record2 = {
233
555
  id: opts.deploymentId,
234
556
  url: `https://mock.deploy.example.com/${opts.deploymentId}`,
235
557
  ref: opts.ref,
@@ -238,8 +560,8 @@ var DeployConnector = class extends AbstractConnector {
238
560
  deployedAt: (/* @__PURE__ */ new Date()).toISOString(),
239
561
  status: "live"
240
562
  };
241
- state.deployments.set(opts.deploymentId, record);
242
- return JSON.stringify({ deployment_id: record.id, url: record.url, ref: record.ref ?? null });
563
+ state.deployments.set(opts.deploymentId, record2);
564
+ return JSON.stringify({ deployment_id: record2.id, url: record2.url, ref: record2.ref ?? null });
243
565
  }
244
566
  _opStatus(state, args) {
245
567
  const id = args.deployment ? String(args.deployment) : void 0;
@@ -3124,7 +3446,7 @@ var MinIOConnector = class extends AbstractConnector {
3124
3446
  const { client, bucket } = handle.state;
3125
3447
  const entries = [];
3126
3448
  const stream = client.listObjects(bucket, path ?? "", recursive);
3127
- return new Promise((resolve5, reject) => {
3449
+ return new Promise((resolve6, reject) => {
3128
3450
  stream.on("data", (obj) => {
3129
3451
  entries.push({
3130
3452
  name: obj.name,
@@ -3134,7 +3456,7 @@ var MinIOConnector = class extends AbstractConnector {
3134
3456
  modifiedAt: obj.lastModified
3135
3457
  });
3136
3458
  });
3137
- stream.on("end", () => resolve5(entries));
3459
+ stream.on("end", () => resolve6(entries));
3138
3460
  stream.on("error", reject);
3139
3461
  });
3140
3462
  }
@@ -3142,9 +3464,9 @@ var MinIOConnector = class extends AbstractConnector {
3142
3464
  const { client, bucket } = handle.state;
3143
3465
  const chunks = [];
3144
3466
  const stream = await client.getObject(bucket, path);
3145
- return new Promise((resolve5, reject) => {
3467
+ return new Promise((resolve6, reject) => {
3146
3468
  stream.on("data", (chunk) => chunks.push(chunk));
3147
- stream.on("end", () => resolve5({ data: Buffer.concat(chunks).toString() }));
3469
+ stream.on("end", () => resolve6({ data: Buffer.concat(chunks).toString() }));
3148
3470
  stream.on("error", reject);
3149
3471
  });
3150
3472
  }
@@ -4412,7 +4734,7 @@ async function checkCliExists(cli) {
4412
4734
  }
4413
4735
  }
4414
4736
  async function spawnCloudflaredTunnel(port) {
4415
- return new Promise((resolve5, reject) => {
4737
+ return new Promise((resolve6, reject) => {
4416
4738
  const proc = portableSpawn(["cloudflared", "tunnel", "--url", `http://localhost:${port}`]);
4417
4739
  const decoder = new TextDecoder();
4418
4740
  const reader = proc.stderr.getReader();
@@ -4423,7 +4745,7 @@ async function spawnCloudflaredTunnel(port) {
4423
4745
  const chunk = decoder.decode(value);
4424
4746
  const match = chunk.match(/https:\/\/[^\s"]+\.trycloudflare\.com/);
4425
4747
  if (match) {
4426
- resolve5({ url: match[0], process: proc });
4748
+ resolve6({ url: match[0], process: proc });
4427
4749
  return;
4428
4750
  }
4429
4751
  }
@@ -5126,7 +5448,7 @@ function connectorTargetFromFactory(id, factory) {
5126
5448
  id,
5127
5449
  displayName: id,
5128
5450
  apiVersion: 1,
5129
- configSchema: zNS.unknown(),
5451
+ configSchema: z4.unknown(),
5130
5452
  create: (_config, _ctx) => factory()
5131
5453
  };
5132
5454
  }
@@ -5423,7 +5745,7 @@ var ConnectorManager = class {
5423
5745
  id: driver,
5424
5746
  displayName: driver,
5425
5747
  apiVersion: 1,
5426
- configSchema: zNS.unknown(),
5748
+ configSchema: z4.unknown(),
5427
5749
  create: () => connector
5428
5750
  });
5429
5751
  }
@@ -6081,7 +6403,7 @@ var ConnectorManager = class {
6081
6403
  return buildSkillBody(descriptor, ops, shellAccess);
6082
6404
  }
6083
6405
  };
6084
- var zStatic = zNS.z ?? zNS.default ?? zNS;
6406
+ var zStatic = z4.z ?? z4.default ?? z4;
6085
6407
  async function buildSdkConnectorTools(manager, options) {
6086
6408
  const shellAccess = options?.shellAccess ?? true;
6087
6409
  let sdk = options?.sdk;
@@ -6092,7 +6414,7 @@ async function buildSdkConnectorTools(manager, options) {
6092
6414
  return null;
6093
6415
  }
6094
6416
  }
6095
- const z4 = options?.z ?? zStatic;
6417
+ const z5 = options?.z ?? zStatic;
6096
6418
  const tools = [];
6097
6419
  tools.push({
6098
6420
  name: "connector_list",
@@ -6114,7 +6436,7 @@ async function buildSdkConnectorTools(manager, options) {
6114
6436
  name: "connector_load_skill",
6115
6437
  description: "Load full instructions for a specific connector into context. Returns a markdown document describing the connector and its available CLI commands.",
6116
6438
  inputSchema: {
6117
- connector_id: z4.string().describe("Connector ID (use connector_list to enumerate)")
6439
+ connector_id: z5.string().describe("Connector ID (use connector_list to enumerate)")
6118
6440
  },
6119
6441
  handler: async ({ connector_id }) => {
6120
6442
  try {
@@ -6132,8 +6454,8 @@ async function buildSdkConnectorTools(manager, options) {
6132
6454
  name: "connector_read",
6133
6455
  description: 'Read content from a connector by path. For KV stores, path is the key. For databases, path is "table/pk".',
6134
6456
  inputSchema: {
6135
- connector_id: z4.string().describe("Connector ID"),
6136
- path: z4.string().describe("Path within the connector")
6457
+ connector_id: z5.string().describe("Connector ID"),
6458
+ path: z5.string().describe("Path within the connector")
6137
6459
  },
6138
6460
  handler: async ({ connector_id, path }) => {
6139
6461
  try {
@@ -6153,9 +6475,9 @@ async function buildSdkConnectorTools(manager, options) {
6153
6475
  name: "connector_write",
6154
6476
  description: "Write content to a connector at a path.",
6155
6477
  inputSchema: {
6156
- connector_id: z4.string().describe("Connector ID"),
6157
- path: z4.string().describe("Path within the connector"),
6158
- content: z4.string().describe("Content to write")
6478
+ connector_id: z5.string().describe("Connector ID"),
6479
+ path: z5.string().describe("Path within the connector"),
6480
+ content: z5.string().describe("Content to write")
6159
6481
  },
6160
6482
  handler: async ({
6161
6483
  connector_id,
@@ -6177,9 +6499,9 @@ async function buildSdkConnectorTools(manager, options) {
6177
6499
  name: "connector_list_entries",
6178
6500
  description: "List entries in a connector (files, keys, rows, etc).",
6179
6501
  inputSchema: {
6180
- connector_id: z4.string().describe("Connector ID"),
6181
- path: z4.string().optional().describe("Path prefix or directory"),
6182
- recursive: z4.boolean().optional().describe("Include nested entries")
6502
+ connector_id: z5.string().describe("Connector ID"),
6503
+ path: z5.string().optional().describe("Path prefix or directory"),
6504
+ recursive: z5.boolean().optional().describe("Include nested entries")
6183
6505
  },
6184
6506
  handler: async ({
6185
6507
  connector_id,
@@ -6201,8 +6523,8 @@ async function buildSdkConnectorTools(manager, options) {
6201
6523
  name: "connector_search",
6202
6524
  description: "Search within a connector for matching content.",
6203
6525
  inputSchema: {
6204
- connector_id: z4.string().describe("Connector ID"),
6205
- query: z4.string().describe("Search query")
6526
+ connector_id: z5.string().describe("Connector ID"),
6527
+ query: z5.string().describe("Search query")
6206
6528
  },
6207
6529
  handler: async ({ connector_id, query }) => {
6208
6530
  try {
@@ -6220,8 +6542,8 @@ async function buildSdkConnectorTools(manager, options) {
6220
6542
  name: "connector_delete",
6221
6543
  description: "Delete an entry from a connector.",
6222
6544
  inputSchema: {
6223
- connector_id: z4.string().describe("Connector ID"),
6224
- path: z4.string().describe("Path to delete")
6545
+ connector_id: z5.string().describe("Connector ID"),
6546
+ path: z5.string().describe("Path to delete")
6225
6547
  },
6226
6548
  handler: async ({ connector_id, path }) => {
6227
6549
  try {
@@ -6240,9 +6562,9 @@ async function buildSdkConnectorTools(manager, options) {
6240
6562
  name: "connector_exec",
6241
6563
  description: "Execute a custom connector operation by name. Use only when shell access is unavailable. Load the connector skill first to see available operations and their arguments.",
6242
6564
  inputSchema: {
6243
- connector_id: z4.string().describe("Connector ID"),
6244
- operation: z4.string().describe("Operation name (e.g. 'send_message', 'query')"),
6245
- args: z4.record(z4.unknown()).describe("Named arguments for the operation (without -- prefix)")
6565
+ connector_id: z5.string().describe("Connector ID"),
6566
+ operation: z5.string().describe("Operation name (e.g. 'send_message', 'query')"),
6567
+ args: z5.record(z5.unknown()).describe("Named arguments for the operation (without -- prefix)")
6246
6568
  },
6247
6569
  handler: async ({
6248
6570
  connector_id,
@@ -6285,7 +6607,7 @@ async function buildSdkFlowTools(manager, connectorId, options) {
6285
6607
  return null;
6286
6608
  }
6287
6609
  }
6288
- const z4 = options?.z ?? zStatic;
6610
+ const z5 = options?.z ?? zStatic;
6289
6611
  let ops;
6290
6612
  try {
6291
6613
  ops = manager.getOperations(connectorId);
@@ -6297,7 +6619,7 @@ async function buildSdkFlowTools(manager, connectorId, options) {
6297
6619
  const tools = agentOps.map((op) => ({
6298
6620
  name: op.name,
6299
6621
  description: op.description,
6300
- inputSchema: buildOperationInputSchema(op.args, z4),
6622
+ inputSchema: buildOperationInputSchema(op.args, z5),
6301
6623
  handler: async (input) => {
6302
6624
  try {
6303
6625
  const result = await manager.executeOp(connectorId, op.name, input ?? {});
@@ -6312,22 +6634,22 @@ async function buildSdkFlowTools(manager, connectorId, options) {
6312
6634
  }));
6313
6635
  return sdk.createSdkMcpServer({ name: "skaile-flow", tools });
6314
6636
  }
6315
- function buildOperationInputSchema(args, z4) {
6637
+ function buildOperationInputSchema(args, z5) {
6316
6638
  const shape = {};
6317
6639
  for (const arg of args) {
6318
6640
  let schema;
6319
6641
  switch (arg.type) {
6320
6642
  case "boolean":
6321
- schema = z4.boolean();
6643
+ schema = z5.boolean();
6322
6644
  break;
6323
6645
  case "number":
6324
- schema = z4.number();
6646
+ schema = z5.number();
6325
6647
  break;
6326
6648
  case "json":
6327
- schema = z4.union([z4.string(), z4.record(z4.unknown()), z4.array(z4.unknown())]);
6649
+ schema = z5.json();
6328
6650
  break;
6329
6651
  default:
6330
- schema = z4.string();
6652
+ schema = z5.string();
6331
6653
  }
6332
6654
  if (arg.description) schema = schema.describe(arg.description);
6333
6655
  if (!arg.required) schema = schema.optional();
@@ -6623,7 +6945,7 @@ async function ensureFleetMounted(dir, log, options) {
6623
6945
  return;
6624
6946
  } catch (err) {
6625
6947
  lastErr = err;
6626
- await new Promise((resolve5) => setTimeout(resolve5, pollIntervalMs));
6948
+ await new Promise((resolve6) => setTimeout(resolve6, pollIntervalMs));
6627
6949
  }
6628
6950
  }
6629
6951
  throw new Error(
@@ -6920,19 +7242,19 @@ var PortPool = class _PortPool {
6920
7242
  }
6921
7243
  /** Quick TCP connect test — returns true if nothing is listening. */
6922
7244
  static isPortFree(port, host = "127.0.0.1") {
6923
- return new Promise((resolve5) => {
7245
+ return new Promise((resolve6) => {
6924
7246
  const socket = createConnection({ port, host });
6925
7247
  socket.once("connect", () => {
6926
7248
  socket.destroy();
6927
- resolve5(false);
7249
+ resolve6(false);
6928
7250
  });
6929
7251
  socket.once("error", () => {
6930
7252
  socket.destroy();
6931
- resolve5(true);
7253
+ resolve6(true);
6932
7254
  });
6933
7255
  socket.setTimeout(500, () => {
6934
7256
  socket.destroy();
6935
- resolve5(true);
7257
+ resolve6(true);
6936
7258
  });
6937
7259
  });
6938
7260
  }
@@ -7151,9 +7473,34 @@ async function listWorktrees(repoRoot) {
7151
7473
  if (current.path) trees.push(current);
7152
7474
  return trees;
7153
7475
  }
7154
-
7155
- // factory-assets/connectors/flow/adapter.ts
7156
7476
  var now = () => (/* @__PURE__ */ new Date()).toISOString();
7477
+ var AGENT_INLINE_OPERATIONS = /* @__PURE__ */ new Set([
7478
+ "get_available",
7479
+ "get_state",
7480
+ "get_globals",
7481
+ "build_handoff",
7482
+ "validate_node",
7483
+ "start_node",
7484
+ "request_approval",
7485
+ "request_input",
7486
+ "complete_node",
7487
+ "skip_node",
7488
+ "fail_node"
7489
+ ]);
7490
+ function mergeInteractionHistory(history, interaction) {
7491
+ if (!interaction) return history;
7492
+ const next = [...history];
7493
+ const index = next.findIndex((entry) => entry.requestedAt === interaction.requestedAt);
7494
+ if (index >= 0) next[index] = interaction;
7495
+ else next.push(interaction);
7496
+ return next;
7497
+ }
7498
+ function normalizeRuntimeFlow(authored) {
7499
+ const parsed = parseFlowDefinition(authored);
7500
+ if (parsed.ok) return parsed.data;
7501
+ const details = parsed.errors.map((issue) => `${issue.path || "<root>"}: ${issue.message}`).join("; ");
7502
+ throw new Error(`FlowAdapter: invalid flow definition: ${details}`);
7503
+ }
7157
7504
  function emptyNode(nodeId) {
7158
7505
  return {
7159
7506
  id: nodeId,
@@ -7163,10 +7510,15 @@ function emptyNode(nodeId) {
7163
7510
  errorHistory: []
7164
7511
  };
7165
7512
  }
7513
+ function nodeExecutionRecord(entries = []) {
7514
+ const record2 = /* @__PURE__ */ Object.create(null);
7515
+ for (const [nodeId, execution] of entries) record2[nodeId] = execution;
7516
+ return record2;
7517
+ }
7166
7518
  function buildInitialExecution(flowDef, seed) {
7167
- const nodes = {};
7519
+ const nodes = nodeExecutionRecord();
7168
7520
  for (const n of flowDef.nodes) {
7169
- if (n.type === "skill") {
7521
+ if (isActiveRuntimeNode(n)) {
7170
7522
  nodes[n.id] = emptyNode(n.id);
7171
7523
  }
7172
7524
  }
@@ -7189,7 +7541,8 @@ function toSnapshot(exec) {
7189
7541
  approval: exec.approval,
7190
7542
  input: exec.input,
7191
7543
  output: exec.output,
7192
- error: exec.error
7544
+ error: exec.error,
7545
+ failedPreconditions: exec.failedPreconditions
7193
7546
  };
7194
7547
  }
7195
7548
  function snapshotMap(execution) {
@@ -7199,6 +7552,50 @@ function snapshotMap(execution) {
7199
7552
  }
7200
7553
  return map;
7201
7554
  }
7555
+ function prepareExecutionSnapshot(flowDef, execution) {
7556
+ let json2;
7557
+ try {
7558
+ json2 = JSON.stringify(execution);
7559
+ } catch {
7560
+ throw new Error("FlowAdapter: execution snapshot must be a JSON object");
7561
+ }
7562
+ const cloned = z4.json().safeParse(JSON.parse(json2));
7563
+ if (!cloned.success || typeof cloned.data !== "object" || Array.isArray(cloned.data)) {
7564
+ throw new Error("FlowAdapter: execution snapshot must be a JSON object");
7565
+ }
7566
+ const snapshot = cloned.data;
7567
+ for (const [nodeId, node] of Object.entries(snapshot.nodes ?? {})) {
7568
+ if (node.status !== "complete") continue;
7569
+ const validation = validateConsumedNodeOutput(flowDef, nodeId, node.output?.fields);
7570
+ if (!validation.valid) {
7571
+ throw new Error(
7572
+ `FlowAdapter: invalid execution output for node ${nodeId}: ${validation.issues.join("; ")}`
7573
+ );
7574
+ }
7575
+ }
7576
+ const nodes = nodeExecutionRecord(Object.entries(snapshot.nodes ?? {}));
7577
+ for (const node of flowDef.nodes) {
7578
+ const existing = nodes[node.id];
7579
+ if (isSubFlowNode(node) && existing && ["running", "awaiting_approval", "awaiting_input"].includes(existing.status)) {
7580
+ nodes[node.id] = {
7581
+ ...existing,
7582
+ status: "not_started",
7583
+ startedAt: void 0,
7584
+ completedAt: void 0,
7585
+ output: void 0,
7586
+ approval: void 0,
7587
+ input: void 0,
7588
+ error: void 0
7589
+ };
7590
+ continue;
7591
+ }
7592
+ if (isActiveRuntimeNode(node) && !Object.hasOwn(nodes, node.id)) {
7593
+ nodes[node.id] = emptyNode(node.id);
7594
+ }
7595
+ }
7596
+ snapshot.nodes = nodes;
7597
+ return snapshot;
7598
+ }
7202
7599
  function assertLegal(cond, message) {
7203
7600
  if (!cond) throw new Error(`FlowAdapter: ${message}`);
7204
7601
  }
@@ -7208,10 +7605,10 @@ function findNode(flowDef, nodeId) {
7208
7605
  return node;
7209
7606
  }
7210
7607
  function isOptional(flowDef, nodeId) {
7211
- return findNode(flowDef, nodeId).data?.optional === true;
7608
+ return nodeIsOptional(findNode(flowDef, nodeId));
7212
7609
  }
7213
- function isMandatoryGate(flowDef, nodeId) {
7214
- return findNode(flowDef, nodeId).data?.approval?.mandatory === true;
7610
+ function requiresAgentApproval(flowDef, nodeId) {
7611
+ return nodeRequiresApproval(findNode(flowDef, nodeId));
7215
7612
  }
7216
7613
  var FlowAdapter = class extends AbstractConnector {
7217
7614
  name = "flow";
@@ -7230,9 +7627,9 @@ var FlowAdapter = class extends AbstractConnector {
7230
7627
  // ── Lifecycle ──
7231
7628
  async connect(declaration, _ctx) {
7232
7629
  const options = declaration.options ?? {};
7233
- const flowDef = options.flow;
7234
- if (!flowDef) throw new Error("FlowAdapter requires options.flow (FlowDefinition)");
7235
- const execution = options.execution ?? buildInitialExecution(
7630
+ if (!options.flow) throw new Error("FlowAdapter requires options.flow (FlowDefinition)");
7631
+ const flowDef = normalizeRuntimeFlow(options.flow);
7632
+ const execution = options.execution ? prepareExecutionSnapshot(flowDef, options.execution) : buildInitialExecution(
7236
7633
  flowDef,
7237
7634
  options.seed ?? { runId: "local", startedBy: "system", autonomousMode: false }
7238
7635
  );
@@ -7241,14 +7638,46 @@ var FlowAdapter = class extends AbstractConnector {
7241
7638
  execution,
7242
7639
  stimulusBus: options.stimulusBus,
7243
7640
  connectorId: declaration.id,
7244
- useBusForStimulus: options.useBusForStimulus ?? false
7641
+ useBusForStimulus: options.useBusForStimulus ?? false,
7642
+ nodeExecutor: options.nodeExecutor,
7643
+ inlineSubFlowRunner: options.inlineSubFlowRunner,
7644
+ inlineOperationTargets: /* @__PURE__ */ new Map(),
7645
+ flowInputOrigins: options.flowInputOrigins,
7646
+ inlineSubFlowAncestry: options.inlineSubFlowAncestry ?? [flowDef.id],
7647
+ executionGeneration: 0
7245
7648
  };
7246
7649
  state.execution = this.recompute(state);
7247
7650
  return this.makeHandle(declaration, state);
7248
7651
  }
7249
7652
  async disconnect(handle) {
7653
+ const state = this.s(handle);
7654
+ state.executionGeneration += 1;
7655
+ await this.cancelInlineTargets(state);
7250
7656
  handle.status = "disconnected";
7251
7657
  }
7658
+ async cancelInlineTargets(s) {
7659
+ const targets = [...s.inlineOperationTargets.values()];
7660
+ s.inlineOperationTargets.clear();
7661
+ for (const target of targets) await target.cancel();
7662
+ }
7663
+ projectInlineInteraction(handle, parentNodeId, projection2) {
7664
+ this.mutate(
7665
+ handle,
7666
+ (s) => {
7667
+ const node = s.execution.nodes[parentNodeId];
7668
+ assertLegal(node, `inline projection: unknown parent node ${parentNodeId}`);
7669
+ s.execution.nodes[parentNodeId] = {
7670
+ ...node,
7671
+ status: projection2.status,
7672
+ approval: projection2.approval ?? node.approval,
7673
+ approvalHistory: mergeInteractionHistory(node.approvalHistory, projection2.approval),
7674
+ input: projection2.input ?? node.input,
7675
+ inputHistory: mergeInteractionHistory(node.inputHistory, projection2.input)
7676
+ };
7677
+ },
7678
+ false
7679
+ );
7680
+ }
7252
7681
  // ── State change subscription ──
7253
7682
  onStateChange(handle, cb) {
7254
7683
  this.s(handle).onChange = cb;
@@ -7256,13 +7685,21 @@ var FlowAdapter = class extends AbstractConnector {
7256
7685
  /**
7257
7686
  * Replace the current execution snapshot with a host-provided one.
7258
7687
  * Used on container rehydration via the extended `configure` command.
7259
- * Does NOT fire `onChange` the host already has the state; we don't
7260
- * want to loop it back on startup.
7688
+ * Publishes only when compatibility preparation changes the supplied snapshot,
7689
+ * such as seeding a node kind activated after the snapshot was persisted.
7261
7690
  */
7262
7691
  hydrate(handle, execution) {
7263
7692
  const s = this.s(handle);
7264
- s.execution = { ...execution };
7265
- s.execution = this.recompute(s);
7693
+ assertLegal(
7694
+ s.inlineOperationTargets.size === 0,
7695
+ "hydrate requires live inline children to be cancelled first"
7696
+ );
7697
+ const candidate = prepareExecutionSnapshot(s.flowDef, execution);
7698
+ const prepared = this.recompute({ ...s, execution: candidate });
7699
+ const changed = JSON.stringify(prepared) !== JSON.stringify(execution);
7700
+ s.executionGeneration += 1;
7701
+ s.execution = prepared;
7702
+ if (changed && s.onChange) s.onChange(s.execution);
7266
7703
  }
7267
7704
  // ── Base operations (private — wired into tools face in constructor) ──
7268
7705
  async read(handle, path) {
@@ -7323,7 +7760,7 @@ var FlowAdapter = class extends AbstractConnector {
7323
7760
  },
7324
7761
  {
7325
7762
  name: "build_handoff",
7326
- description: "Build curated context for a node from its completed flow-type predecessors (summaries + artifact refs).",
7763
+ description: "Build the selected agent node briefing: instruction, supplied bindings, required assets, and completed predecessor context.",
7327
7764
  accessLevel: "read",
7328
7765
  args: [
7329
7766
  {
@@ -7352,7 +7789,7 @@ var FlowAdapter = class extends AbstractConnector {
7352
7789
  ops.push(
7353
7790
  {
7354
7791
  name: "start_node",
7355
- description: "Mark an available node as running (records startedAt).",
7792
+ description: "Mark an available agent node as running (records startedAt).",
7356
7793
  accessLevel: "write",
7357
7794
  args: [
7358
7795
  { name: "nodeId", description: "Node ID to start", type: "string", required: true }
@@ -7360,7 +7797,7 @@ var FlowAdapter = class extends AbstractConnector {
7360
7797
  },
7361
7798
  {
7362
7799
  name: "request_approval",
7363
- description: "Request human approval for a running node. Ends your turn \u2014 the runner yields until the user approves or rejects.",
7800
+ description: "Request human approval for a running agent node, or replace a rejected first-class gate decision with a fresh pending request. Ends your turn \u2014 the runner yields until the user approves or rejects.",
7364
7801
  accessLevel: "write",
7365
7802
  args: [
7366
7803
  { name: "nodeId", description: "Node ID", type: "string", required: true },
@@ -7380,7 +7817,7 @@ var FlowAdapter = class extends AbstractConnector {
7380
7817
  },
7381
7818
  {
7382
7819
  name: "request_input",
7383
- description: "Request typed input from the user for a node. Ends your turn. Legal from `available`, `running`, or `awaiting_approval`.",
7820
+ description: "Request typed input for an agent node. Ends your turn. Legal from `available`, `running`, or `awaiting_approval`; never legal for a first-class gate.",
7384
7821
  accessLevel: "write",
7385
7822
  args: [
7386
7823
  { name: "nodeId", description: "Node ID", type: "string", required: true },
@@ -7400,7 +7837,7 @@ var FlowAdapter = class extends AbstractConnector {
7400
7837
  },
7401
7838
  {
7402
7839
  name: "complete_node",
7403
- description: "Mark a node as complete with structured output. Requires `awaiting_approval` with an `approved` decision, OR `running` when autonomous mode is on. Mandatory approval gates always require the approved decision \u2014 autonomous mode cannot skip them.",
7840
+ description: "Mark an agent node as complete with structured output. Consumed fields must match the declared output schema; an agent gets one corrective call before failure. Requires `awaiting_approval` with an `approved` decision, OR `running` when autonomous mode is on. Agent nodes with mandatory approval always require the approved decision. First-class gates complete directly through host approval and never use this operation.",
7404
7841
  accessLevel: "write",
7405
7842
  args: [
7406
7843
  { name: "nodeId", description: "Node ID to complete", type: "string", required: true },
@@ -7410,6 +7847,12 @@ var FlowAdapter = class extends AbstractConnector {
7410
7847
  type: "string",
7411
7848
  required: true
7412
7849
  },
7850
+ {
7851
+ name: "fields",
7852
+ description: "Named JSON object consumed by downstream nodes.",
7853
+ type: "json",
7854
+ required: false
7855
+ },
7413
7856
  {
7414
7857
  name: "artifacts",
7415
7858
  description: "JSON array of ArtifactRef objects produced by this node.",
@@ -7573,13 +8016,36 @@ var FlowAdapter = class extends AbstractConnector {
7573
8016
  }
7574
8017
  async executeOp(handle, operation, args) {
7575
8018
  const s = this.s(handle);
8019
+ const agentTarget = [...s.inlineOperationTargets.values()].find(
8020
+ (target) => target.isAgentTurnActive()
8021
+ );
8022
+ if (agentTarget && AGENT_INLINE_OPERATIONS.has(operation)) {
8023
+ return agentTarget.executeAgentOp(operation, args);
8024
+ }
8025
+ if (operation === "applyApproval" || operation === "applyInput") {
8026
+ const parentNodeId = String(args.nodeId ?? "");
8027
+ const target = s.inlineOperationTargets.get(parentNodeId);
8028
+ if (target) {
8029
+ const projection2 = operation === "applyApproval" ? await target.applyParentApproval(args) : await target.applyParentInput(args);
8030
+ this.projectInlineInteraction(handle, parentNodeId, projection2);
8031
+ return JSON.stringify(s.execution);
8032
+ }
8033
+ }
8034
+ if (operation === "cancel") {
8035
+ await this.cancelInlineTargets(s);
8036
+ this.cancel(handle);
8037
+ return JSON.stringify(s.execution);
8038
+ }
8039
+ if (operation === "hydrate" || operation === "start") {
8040
+ await this.cancelInlineTargets(s);
8041
+ }
7576
8042
  switch (operation) {
7577
8043
  case "get_available":
7578
8044
  return this.opGetAvailable(s);
7579
8045
  case "get_state":
7580
8046
  return JSON.stringify(s.execution);
7581
8047
  case "get_globals":
7582
- return JSON.stringify(s.flowDef.globals ?? {});
8048
+ return JSON.stringify(flowDefaults(s.flowDef));
7583
8049
  case "build_handoff":
7584
8050
  return this.buildHandoff(s, String(args.nodeId ?? ""));
7585
8051
  case "validate_node":
@@ -7605,8 +8071,7 @@ var FlowAdapter = class extends AbstractConnector {
7605
8071
  case "applyInput":
7606
8072
  return this.opApplyInput(handle, s, args);
7607
8073
  case "cancel":
7608
- this.cancel(handle);
7609
- return JSON.stringify(s.execution);
8074
+ throw new Error("FlowAdapter: cancel routing invariant violated");
7610
8075
  case "setAutonomousMode":
7611
8076
  return this.opSetAutonomousMode(handle, s, args);
7612
8077
  case "retryNode":
@@ -7621,14 +8086,18 @@ var FlowAdapter = class extends AbstractConnector {
7621
8086
  }
7622
8087
  // ── executeOp branch handlers (private dispatch helpers) ─────────────────
7623
8088
  opGetAvailable(s) {
8089
+ const agentNodeIds = new Set(s.flowDef.nodes.filter(isAgentNode).map((node) => node.id));
7624
8090
  const nodes = Object.values(s.execution.nodes);
7625
- const available = nodes.filter((n) => n.status === "available").map((n) => n.id);
8091
+ const available = nodes.filter((n) => agentNodeIds.has(n.id) && n.status === "available").map((n) => n.id);
7626
8092
  return JSON.stringify(available);
7627
8093
  }
7628
8094
  opValidateNode(s, args) {
7629
8095
  const nodeId = String(args.nodeId ?? "");
7630
8096
  const node = s.execution.nodes[nodeId];
7631
8097
  if (!node) return JSON.stringify({ valid: false, reason: "Node not found" });
8098
+ if (!isAgentNode(findNode(s.flowDef, nodeId))) {
8099
+ return JSON.stringify({ valid: false, reason: "Node is not an active agent node" });
8100
+ }
7632
8101
  const predsMet = flowPredecessors(s.flowDef, nodeId).every((pid) => {
7633
8102
  const predStatus = s.execution.nodes[pid]?.status ?? "not_started";
7634
8103
  return predStatus === "complete" || predStatus === "skipped";
@@ -7636,7 +8105,8 @@ var FlowAdapter = class extends AbstractConnector {
7636
8105
  return JSON.stringify({
7637
8106
  valid: node.status === "available",
7638
8107
  status: node.status,
7639
- predecessorsReady: predsMet
8108
+ predecessorsReady: predsMet,
8109
+ failedPreconditions: node.failedPreconditions ?? []
7640
8110
  });
7641
8111
  }
7642
8112
  opStartNode(handle, args) {
@@ -7657,12 +8127,14 @@ var FlowAdapter = class extends AbstractConnector {
7657
8127
  }
7658
8128
  opCompleteNode(handle, args) {
7659
8129
  const nodeId = String(args.nodeId ?? "");
8130
+ const parsedFields = args.fields === void 0 ? { issues: [] } : safeParseFields(args.fields);
7660
8131
  const output = {
7661
8132
  summary: String(args.summary ?? "Completed"),
7662
8133
  artifacts: safeParseArtifacts(args.artifacts),
7663
- concerns: safeParseStringArray(args.concerns)
8134
+ concerns: safeParseStringArray(args.concerns),
8135
+ ...parsedFields.fields ? { fields: parsedFields.fields } : {}
7664
8136
  };
7665
- return this.doCompleteNode(handle, nodeId, output);
8137
+ return this.doCompleteNode(handle, nodeId, output, parsedFields.issues);
7666
8138
  }
7667
8139
  opSkipNode(handle, args) {
7668
8140
  const nodeId = String(args.nodeId ?? "");
@@ -7709,15 +8181,17 @@ var FlowAdapter = class extends AbstractConnector {
7709
8181
  return JSON.stringify(s.execution);
7710
8182
  }
7711
8183
  opStart(handle, s, args) {
7712
- const flow = args.flow;
7713
- if (!flow || typeof flow !== "object") {
8184
+ const authoredFlow = args.flow;
8185
+ if (!authoredFlow || typeof authoredFlow !== "object") {
7714
8186
  throw new Error("FlowAdapter: start requires args.flow (FlowDefinition)");
7715
8187
  }
8188
+ const flow = normalizeRuntimeFlow(authoredFlow);
7716
8189
  const seed = args.seed ?? {};
7717
8190
  if (!seed.runId || !seed.startedBy) {
7718
8191
  throw new Error("FlowAdapter: start requires args.seed.runId + args.seed.startedBy");
7719
8192
  }
7720
8193
  s.flowDef = flow;
8194
+ s.executionGeneration += 1;
7721
8195
  s.execution = buildInitialExecution(flow, {
7722
8196
  runId: seed.runId,
7723
8197
  startedBy: seed.startedBy,
@@ -7730,6 +8204,10 @@ var FlowAdapter = class extends AbstractConnector {
7730
8204
  // ── Agent-side mutations (tool calls) ─────────────────────────────────────
7731
8205
  doStartNode(handle, nodeId) {
7732
8206
  return this.mutate(handle, (s) => {
8207
+ assertLegal(
8208
+ isAgentNode(findNode(s.flowDef, nodeId)),
8209
+ `node is not an active agent node: ${nodeId}`
8210
+ );
7733
8211
  const node = s.execution.nodes[nodeId];
7734
8212
  assertLegal(node, `node not in execution: ${nodeId}`);
7735
8213
  assertLegal(node.status === "available", `cannot start ${nodeId}: status is ${node.status}`);
@@ -7743,11 +8221,17 @@ var FlowAdapter = class extends AbstractConnector {
7743
8221
  }
7744
8222
  doRequestApproval(handle, nodeId, summary, artifacts) {
7745
8223
  return this.mutate(handle, (s) => {
8224
+ const definition = findNode(s.flowDef, nodeId);
7746
8225
  const node = s.execution.nodes[nodeId];
7747
8226
  assertLegal(node, `node not in execution: ${nodeId}`);
8227
+ const rejectedGateRerequest = isGateNode(definition) && node.status === "awaiting_approval" && node.approval?.decision === "rejected";
8228
+ assertLegal(
8229
+ isAgentNode(definition) || rejectedGateRerequest,
8230
+ `node is not an active agent node or rejected gate: ${nodeId}`
8231
+ );
7748
8232
  assertLegal(
7749
- node.status === "running",
7750
- `request_approval requires running, got ${node.status}`
8233
+ isAgentNode(definition) && node.status === "running" || rejectedGateRerequest,
8234
+ `request_approval requires a running agent node or rejected gate, got ${node.status}`
7751
8235
  );
7752
8236
  const approval = { requestedAt: now(), summary };
7753
8237
  const history = [...node.approvalHistory, approval];
@@ -7765,6 +8249,10 @@ var FlowAdapter = class extends AbstractConnector {
7765
8249
  }
7766
8250
  doRequestInput(handle, nodeId, prompt, schema) {
7767
8251
  return this.mutate(handle, (s) => {
8252
+ assertLegal(
8253
+ isAgentNode(findNode(s.flowDef, nodeId)),
8254
+ `node is not an active agent node: ${nodeId}`
8255
+ );
7768
8256
  const node = s.execution.nodes[nodeId];
7769
8257
  assertLegal(node, `node not in execution: ${nodeId}`);
7770
8258
  assertLegal(
@@ -7787,20 +8275,64 @@ var FlowAdapter = class extends AbstractConnector {
7787
8275
  return JSON.stringify({ ok: true, status: "awaiting_input" });
7788
8276
  });
7789
8277
  }
7790
- doCompleteNode(handle, nodeId, output) {
7791
- return this.mutate(handle, (s) => {
7792
- const node = s.execution.nodes[nodeId];
7793
- assertLegal(node, `node not in execution: ${nodeId}`);
7794
- const autonomous = s.execution.autonomousMode;
7795
- const mandatory = isMandatoryGate(s.flowDef, nodeId);
7796
- const approvedPrior = node.status === "awaiting_approval" && node.approval?.decision === "approved";
7797
- const runningAutonomous = node.status === "running" && autonomous && !mandatory;
7798
- assertLegal(
7799
- approvedPrior || runningAutonomous,
7800
- mandatory && node.status === "running" ? `complete_node blocked: node ${nodeId} is a mandatory approval gate \u2014 call request_approval and wait for an approved decision (autonomous mode cannot skip it)` : `complete_node requires awaiting_approval+approved OR running+autonomous; node is ${node.status}${node.approval?.decision ? ` with approval ${node.approval.decision}` : ""}, autonomous=${autonomous}`
8278
+ doCompleteNode(handle, nodeId, output, fieldIssues = []) {
8279
+ const state = this.s(handle);
8280
+ assertLegal(
8281
+ isAgentNode(findNode(state.flowDef, nodeId)),
8282
+ `node is not an active agent node: ${nodeId}`
8283
+ );
8284
+ const node = state.execution.nodes[nodeId];
8285
+ assertLegal(node, `node not in execution: ${nodeId}`);
8286
+ const autonomous = state.execution.autonomousMode;
8287
+ const mandatory = requiresAgentApproval(state.flowDef, nodeId);
8288
+ const approvedPrior = node.status === "awaiting_approval" && node.approval?.decision === "approved";
8289
+ const runningAutonomous = node.status === "running" && autonomous && !mandatory;
8290
+ assertLegal(
8291
+ approvedPrior || runningAutonomous,
8292
+ mandatory && node.status === "running" ? `complete_node blocked: node ${nodeId} is a mandatory approval gate \u2014 call request_approval and wait for an approved decision (autonomous mode cannot skip it)` : `complete_node requires awaiting_approval+approved OR running+autonomous; node is ${node.status}${node.approval?.decision ? ` with approval ${node.approval.decision}` : ""}, autonomous=${autonomous}`
8293
+ );
8294
+ const validation = fieldIssues.length > 0 && getConsumedOutputSchema(state.flowDef, nodeId) ? { valid: false, issues: fieldIssues } : validateConsumedNodeOutput(state.flowDef, nodeId, output.fields);
8295
+ if (!validation.valid) {
8296
+ const issueText = validation.issues.join("; ");
8297
+ if (!node.outputCorrection) {
8298
+ const correction = {
8299
+ requestedAt: now(),
8300
+ issues: validation.issues
8301
+ };
8302
+ this.mutate(handle, (current) => {
8303
+ const execution = current.execution.nodes[nodeId];
8304
+ assertLegal(execution, `node not in execution: ${nodeId}`);
8305
+ current.execution.nodes[nodeId] = { ...execution, outputCorrection: correction };
8306
+ });
8307
+ throw new Error(
8308
+ `FlowAdapter: output for node ${nodeId} does not match its consumed schema. Submit one corrected complete_node call. ${issueText}`
8309
+ );
8310
+ }
8311
+ this.mutate(handle, (current) => {
8312
+ const execution = current.execution.nodes[nodeId];
8313
+ assertLegal(execution, `node not in execution: ${nodeId}`);
8314
+ const error = {
8315
+ message: `Node ${nodeId} output failed schema validation after one correction: ${issueText}`,
8316
+ recoverable: false,
8317
+ at: now()
8318
+ };
8319
+ current.execution.nodes[nodeId] = {
8320
+ ...execution,
8321
+ status: "failed",
8322
+ completedAt: now(),
8323
+ error,
8324
+ errorHistory: [...execution.errorHistory, error]
8325
+ };
8326
+ });
8327
+ throw new Error(
8328
+ `FlowAdapter: node ${nodeId} failed after one output correction. ${issueText}`
7801
8329
  );
7802
- s.execution.nodes[nodeId] = {
7803
- ...node,
8330
+ }
8331
+ return this.mutate(handle, (current) => {
8332
+ const execution = current.execution.nodes[nodeId];
8333
+ assertLegal(execution, `node not in execution: ${nodeId}`);
8334
+ current.execution.nodes[nodeId] = {
8335
+ ...execution,
7804
8336
  status: "complete",
7805
8337
  completedAt: now(),
7806
8338
  output
@@ -7810,10 +8342,14 @@ var FlowAdapter = class extends AbstractConnector {
7810
8342
  }
7811
8343
  doSkipNode(handle, nodeId) {
7812
8344
  return this.mutate(handle, (s) => {
8345
+ assertLegal(
8346
+ isAgentNode(findNode(s.flowDef, nodeId)),
8347
+ `node is not an active agent node: ${nodeId}`
8348
+ );
7813
8349
  const node = s.execution.nodes[nodeId];
7814
8350
  assertLegal(node, `node not in execution: ${nodeId}`);
7815
8351
  assertLegal(
7816
- !isMandatoryGate(s.flowDef, nodeId),
8352
+ !requiresAgentApproval(s.flowDef, nodeId),
7817
8353
  `cannot skip node ${nodeId}: it is a mandatory approval gate \u2014 call request_approval and wait for an approved decision`
7818
8354
  );
7819
8355
  assertLegal(isOptional(s.flowDef, nodeId), `cannot skip required node ${nodeId}`);
@@ -7827,6 +8363,10 @@ var FlowAdapter = class extends AbstractConnector {
7827
8363
  }
7828
8364
  doFailNode(handle, nodeId, message, recoverable) {
7829
8365
  return this.mutate(handle, (s) => {
8366
+ assertLegal(
8367
+ isAgentNode(findNode(s.flowDef, nodeId)),
8368
+ `node is not an active agent node: ${nodeId}`
8369
+ );
7830
8370
  const node = s.execution.nodes[nodeId];
7831
8371
  assertLegal(node, `node not in execution: ${nodeId}`);
7832
8372
  const error = { message, recoverable, at: now() };
@@ -7852,18 +8392,24 @@ var FlowAdapter = class extends AbstractConnector {
7852
8392
  // ── Host-side mutations (not exposed as agent tools) ─────────────────────
7853
8393
  /**
7854
8394
  * Record an approval decision on a node currently `awaiting_approval`.
7855
- * `approved` leaves the node in `awaiting_approval` the agent
7856
- * transitions it to `complete` on the next turn. `rejected` drops the
7857
- * node back to `available` so the agent can retry.
8395
+ * A first-class gate completes directly when approved and remains parked when rejected.
8396
+ * Agent-node approvals keep their existing resume behavior.
7858
8397
  */
7859
8398
  applyApproval(handle, nodeId, decision, feedback, decidedBy) {
7860
8399
  this.mutate(handle, (s) => {
8400
+ const definition = findNode(s.flowDef, nodeId);
7861
8401
  const node = s.execution.nodes[nodeId];
7862
8402
  assertLegal(node, `applyApproval: unknown node ${nodeId}`);
7863
8403
  assertLegal(
7864
8404
  node.status === "awaiting_approval",
7865
8405
  `applyApproval: node ${nodeId} is ${node.status}, not awaiting_approval`
7866
8406
  );
8407
+ if (isGateNode(definition)) {
8408
+ assertLegal(
8409
+ node.approval?.decidedAt == null && node.approval?.decision == null,
8410
+ `applyApproval: gate ${nodeId} approval attempt is already decided; request approval again before applying another decision`
8411
+ );
8412
+ }
7867
8413
  const base = node.approval ?? { requestedAt: now(), summary: "" };
7868
8414
  const approval = {
7869
8415
  ...base,
@@ -7875,7 +8421,22 @@ var FlowAdapter = class extends AbstractConnector {
7875
8421
  const history = [...node.approvalHistory];
7876
8422
  if (history.length > 0) history[history.length - 1] = approval;
7877
8423
  else history.push(approval);
7878
- if (decision === "approved") {
8424
+ if (isGateNode(definition) && decision === "approved") {
8425
+ s.execution.nodes[nodeId] = {
8426
+ ...node,
8427
+ status: "complete",
8428
+ completedAt: now(),
8429
+ approval,
8430
+ approvalHistory: history
8431
+ };
8432
+ } else if (isGateNode(definition)) {
8433
+ s.execution.nodes[nodeId] = {
8434
+ ...node,
8435
+ status: "awaiting_approval",
8436
+ approval,
8437
+ approvalHistory: history
8438
+ };
8439
+ } else if (decision === "approved") {
7879
8440
  s.execution.nodes[nodeId] = { ...node, approval, approvalHistory: history };
7880
8441
  } else {
7881
8442
  s.execution.nodes[nodeId] = {
@@ -7959,9 +8520,296 @@ var FlowAdapter = class extends AbstractConnector {
7959
8520
  getExecution(handle) {
7960
8521
  return this.s(handle).execution;
7961
8522
  }
7962
- /** Node IDs marked as mandatory approval gates (`data.approval.mandatory`). */
8523
+ /** @internal Whether a nested inline descendant owns the current agent turn. */
8524
+ hasActiveInlineAgentTurn(handle) {
8525
+ return [...this.s(handle).inlineOperationTargets.values()].some(
8526
+ (target) => target.isAgentTurnActive()
8527
+ );
8528
+ }
8529
+ /** Snapshot currently visible through agent-facing tools, including a live inline child. */
8530
+ getAgentExecution(handle) {
8531
+ const state = this.s(handle);
8532
+ return [...state.inlineOperationTargets.values()].find((target) => target.isAgentTurnActive())?.getExecution() ?? state.execution;
8533
+ }
8534
+ /** Agent node IDs whose approval policy remains mandatory in autonomous mode. */
8535
+ getMandatoryAgentApprovalNodeIds(handle) {
8536
+ const state = this.s(handle);
8537
+ const target = [...state.inlineOperationTargets.values()].find(
8538
+ (candidate) => candidate.isAgentTurnActive()
8539
+ );
8540
+ if (target) return target.getMandatoryAgentApprovalNodeIds();
8541
+ return state.flowDef.nodes.filter(nodeRequiresApproval).map((n) => n.id);
8542
+ }
8543
+ /** @deprecated Use {@link getMandatoryAgentApprovalNodeIds}. */
7963
8544
  getMandatoryGates(handle) {
7964
- return this.s(handle).flowDef.nodes.filter((n) => n.data?.approval?.mandatory === true).map((n) => n.id);
8545
+ return this.getMandatoryAgentApprovalNodeIds(handle);
8546
+ }
8547
+ /**
8548
+ * Decide whether the current state and stimulus require the session agent's conversation.
8549
+ * Runtime-owned gates stay dark while pending; a rejected gate is the exception because the
8550
+ * rejection starts the revision conversation promised by the gate lifecycle.
8551
+ */
8552
+ shouldDriveAgentTurn(handle, stimulus) {
8553
+ const state = this.s(handle);
8554
+ const execution = state.execution;
8555
+ if (execution.done || execution.status === "failed" || execution.status === "cancelled") {
8556
+ return false;
8557
+ }
8558
+ if (stimulus.kind === "user_message") return true;
8559
+ for (const node of state.flowDef.nodes) {
8560
+ const current = execution.nodes[node.id];
8561
+ if (isGateNode(node) && current?.status === "awaiting_approval" && current.approval?.decision === "rejected") {
8562
+ return true;
8563
+ }
8564
+ }
8565
+ for (const node of state.flowDef.nodes) {
8566
+ if (!isAgentNode(node)) continue;
8567
+ const current = execution.nodes[node.id];
8568
+ if (!current) continue;
8569
+ if (current.status === "available" || current.status === "running") return true;
8570
+ if (current.status === "awaiting_approval" && current.approval?.decision === "approved") {
8571
+ return true;
8572
+ }
8573
+ }
8574
+ return false;
8575
+ }
8576
+ /**
8577
+ * Drain runtime-owned nodes in definition order until only agent work or a human decision
8578
+ * remains. Function nodes use NodeExecutor, gates park, and sub-flows delegate to the host.
8579
+ */
8580
+ async drainRuntimeNodes(handle) {
8581
+ return this.drainAvailableRuntimeNodes(handle, true);
8582
+ }
8583
+ /**
8584
+ * Run every currently available function node through the configured
8585
+ * NodeExecutor. Newly-unblocked function nodes are drained in definition
8586
+ * order before returning control to the runner's agent-turn decision.
8587
+ */
8588
+ async executeAvailableFunctionNodes(handle) {
8589
+ return this.drainAvailableRuntimeNodes(handle, false);
8590
+ }
8591
+ async drainAvailableRuntimeNodes(handle, includeGates) {
8592
+ const advancedSubFlows = /* @__PURE__ */ new Set();
8593
+ while (true) {
8594
+ const state = this.s(handle);
8595
+ if (state.execution.status === "cancelled" || state.execution.status === "failed") break;
8596
+ const node = state.flowDef.nodes.find((candidate) => {
8597
+ const status = state.execution.nodes[candidate.id]?.status;
8598
+ if (isSubFlowNode(candidate) && !advancedSubFlows.has(candidate.id) && (status === "available" || status === "running")) {
8599
+ return true;
8600
+ }
8601
+ return (isFunctionNode(candidate) || includeGates && isGateNode(candidate)) && status === "available";
8602
+ });
8603
+ if (!node) break;
8604
+ if (isGateNode(node)) {
8605
+ this.mutate(
8606
+ handle,
8607
+ (current) => {
8608
+ const execution = current.execution.nodes[node.id];
8609
+ assertLegal(execution, `node not in execution: ${node.id}`);
8610
+ assertLegal(
8611
+ execution.status === "available",
8612
+ `cannot park gate node ${node.id}: status is ${execution.status}`
8613
+ );
8614
+ const approval = { requestedAt: now(), summary: node.run.prompt };
8615
+ current.execution.nodes[node.id] = {
8616
+ ...execution,
8617
+ status: "awaiting_approval",
8618
+ approval,
8619
+ approvalHistory: [...execution.approvalHistory, approval]
8620
+ };
8621
+ },
8622
+ false
8623
+ );
8624
+ continue;
8625
+ }
8626
+ if (isSubFlowNode(node)) {
8627
+ advancedSubFlows.add(node.id);
8628
+ const execution = state.execution.nodes[node.id];
8629
+ assertLegal(execution, `node not in execution: ${node.id}`);
8630
+ if (execution.status === "available") {
8631
+ this.mutate(
8632
+ handle,
8633
+ (current) => {
8634
+ const currentExecution = current.execution.nodes[node.id];
8635
+ assertLegal(currentExecution, `node not in execution: ${node.id}`);
8636
+ assertLegal(
8637
+ currentExecution.status === "available",
8638
+ `cannot execute sub-flow node ${node.id}: status is ${currentExecution.status}`
8639
+ );
8640
+ current.execution.nodes[node.id] = {
8641
+ ...currentExecution,
8642
+ status: "running",
8643
+ startedAt: now()
8644
+ };
8645
+ },
8646
+ false
8647
+ );
8648
+ }
8649
+ const executionGeneration = state.executionGeneration;
8650
+ let result;
8651
+ try {
8652
+ const runner = state.inlineSubFlowRunner;
8653
+ if (!runner) {
8654
+ throw new Error(`no inline sub-flow runner configured for node ${node.id}`);
8655
+ }
8656
+ const resolved = resolveNodeBindings(
8657
+ state.flowDef,
8658
+ node,
8659
+ snapshotMap(state.execution),
8660
+ state.flowInputOrigins
8661
+ );
8662
+ result = await runner.run({
8663
+ node,
8664
+ flowId: node.run.flow,
8665
+ inputs: resolved.values,
8666
+ inputOrigins: {
8667
+ ...node.run.passContext ? state.flowInputOrigins : {},
8668
+ ...resolved.origins
8669
+ },
8670
+ passContext: node.run.passContext ?? false,
8671
+ parentFlow: state.flowDef,
8672
+ parentExecution: state.execution,
8673
+ ancestry: state.inlineSubFlowAncestry,
8674
+ routeOperationsTo: (target) => {
8675
+ if (target) state.inlineOperationTargets.set(node.id, target);
8676
+ else state.inlineOperationTargets.delete(node.id);
8677
+ }
8678
+ });
8679
+ } catch (error) {
8680
+ result = {
8681
+ status: "failed",
8682
+ error: {
8683
+ message: `Sub-flow node ${node.id} failed: ${error instanceof Error ? error.message : String(error)}`,
8684
+ recoverable: false,
8685
+ at: now()
8686
+ }
8687
+ };
8688
+ }
8689
+ if (this.s(handle).executionGeneration !== executionGeneration) break;
8690
+ if (result.status === "paused") {
8691
+ this.projectInlineInteraction(handle, node.id, result.projection);
8692
+ continue;
8693
+ }
8694
+ if (this.s(handle).execution.status === "cancelled") break;
8695
+ this.mutate(
8696
+ handle,
8697
+ (current) => {
8698
+ const execution2 = current.execution.nodes[node.id];
8699
+ assertLegal(execution2, `node not in execution: ${node.id}`);
8700
+ if (result.status === "complete") {
8701
+ current.execution.nodes[node.id] = {
8702
+ ...execution2,
8703
+ status: "complete",
8704
+ completedAt: now(),
8705
+ output: result.output
8706
+ };
8707
+ return;
8708
+ }
8709
+ current.execution.nodes[node.id] = {
8710
+ ...execution2,
8711
+ status: nodeIsOptional(node) ? "skipped" : "failed",
8712
+ completedAt: now(),
8713
+ output: result.output,
8714
+ error: result.error,
8715
+ errorHistory: [...execution2.errorHistory, result.error]
8716
+ };
8717
+ },
8718
+ false
8719
+ );
8720
+ continue;
8721
+ }
8722
+ if (!isFunctionNode(node)) continue;
8723
+ this.mutate(
8724
+ handle,
8725
+ (current) => {
8726
+ const execution = current.execution.nodes[node.id];
8727
+ assertLegal(execution, `node not in execution: ${node.id}`);
8728
+ assertLegal(
8729
+ execution.status === "available",
8730
+ `cannot execute function node ${node.id}: status is ${execution.status}`
8731
+ );
8732
+ current.execution.nodes[node.id] = {
8733
+ ...execution,
8734
+ status: "running",
8735
+ startedAt: now()
8736
+ };
8737
+ },
8738
+ false
8739
+ );
8740
+ let capturedOutput;
8741
+ try {
8742
+ const executor = state.nodeExecutor;
8743
+ if (!executor) throw new Error(`no NodeExecutor configured for function node ${node.id}`);
8744
+ const resolved = resolveNodeBindings(
8745
+ state.flowDef,
8746
+ node,
8747
+ snapshotMap(state.execution),
8748
+ state.flowInputOrigins
8749
+ );
8750
+ const result = await executor.execute({
8751
+ node,
8752
+ inputs: resolved.values,
8753
+ inputOrigins: resolved.origins
8754
+ });
8755
+ capturedOutput = functionNodeOutput(node, result);
8756
+ const successfulStatuses = node.run.successExit ?? [0];
8757
+ if (!successfulStatuses.includes(result.exitStatus)) {
8758
+ const detail = result.stderr.trim() || result.stdout.trim();
8759
+ throw new Error(
8760
+ `Function node ${node.id} exited with unsuccessful exit status ${result.exitStatus}${detail ? `: ${detail}` : ""}`
8761
+ );
8762
+ }
8763
+ const validation = validateConsumedNodeOutput(
8764
+ state.flowDef,
8765
+ node.id,
8766
+ capturedOutput.fields
8767
+ );
8768
+ if (!validation.valid) {
8769
+ throw new Error(
8770
+ `output does not match its consumed schema: ${validation.issues.join("; ")}`
8771
+ );
8772
+ }
8773
+ this.mutate(
8774
+ handle,
8775
+ (current) => {
8776
+ const execution = current.execution.nodes[node.id];
8777
+ assertLegal(execution, `node not in execution: ${node.id}`);
8778
+ current.execution.nodes[node.id] = {
8779
+ ...execution,
8780
+ status: "complete",
8781
+ completedAt: now(),
8782
+ output: capturedOutput
8783
+ };
8784
+ },
8785
+ false
8786
+ );
8787
+ } catch (error) {
8788
+ const message = error instanceof Error ? error.message : String(error);
8789
+ this.mutate(
8790
+ handle,
8791
+ (current) => {
8792
+ const execution = current.execution.nodes[node.id];
8793
+ assertLegal(execution, `node not in execution: ${node.id}`);
8794
+ const nodeError = {
8795
+ message: `Function node ${node.id} failed: ${message}`,
8796
+ recoverable: false,
8797
+ at: now()
8798
+ };
8799
+ current.execution.nodes[node.id] = {
8800
+ ...execution,
8801
+ status: "failed",
8802
+ completedAt: now(),
8803
+ output: capturedOutput,
8804
+ error: nodeError,
8805
+ errorHistory: [...execution.errorHistory, nodeError]
8806
+ };
8807
+ },
8808
+ false
8809
+ );
8810
+ }
8811
+ }
8812
+ return this.getExecution(handle);
7965
8813
  }
7966
8814
  // ── Private helpers ──
7967
8815
  s(handle) {
@@ -7970,23 +8818,21 @@ var FlowAdapter = class extends AbstractConnector {
7970
8818
  /**
7971
8819
  * Central mutation pipeline: runs the mutator, recomputes derived
7972
8820
  * fields, updates the flow-level `status` if the mutation completed
7973
- * or failed the flow, fires `onChange` once, and (Phase 3, 2026-05-08)
7974
- * optionally signals the {@link FlowAdapterStimulusBus} so the runner
7975
- * can kick a turn outside the legacy orchestrator path.
7976
- *
7977
- * The bus signal path is gated by the `useBusForStimulus` flag set at
7978
- * connect time — Phase 3 builds the path; Phase 4 flips the default to
7979
- * true globally and removes the orchestrator's onStateChange
7980
- * subscription.
8821
+ * or failed the flow, fires `onChange` once, and optionally signals the
8822
+ * {@link FlowAdapterStimulusBus}. The signal path is gated by the
8823
+ * `useBusForStimulus` flag set at connect time.
7981
8824
  */
7982
- mutate(handle, fn) {
8825
+ mutate(handle, fn, signalStimulus = true) {
7983
8826
  const s = this.s(handle);
7984
8827
  const previousExecution = s.lastSignaledExecution;
7985
- s.execution = { ...s.execution, nodes: { ...s.execution.nodes } };
8828
+ s.execution = {
8829
+ ...s.execution,
8830
+ nodes: nodeExecutionRecord(Object.entries(s.execution.nodes))
8831
+ };
7986
8832
  const result = fn(s);
7987
8833
  s.execution = this.recompute(s);
7988
8834
  if (s.onChange) s.onChange(s.execution);
7989
- if (s.useBusForStimulus && s.stimulusBus) {
8835
+ if (signalStimulus && s.useBusForStimulus && s.stimulusBus) {
7990
8836
  const stim = computeStimulus(previousExecution, s.execution);
7991
8837
  if (stim) {
7992
8838
  const fragment = renderStimulusPrompt(stim);
@@ -8009,17 +8855,23 @@ var FlowAdapter = class extends AbstractConnector {
8009
8855
  recompute(s) {
8010
8856
  const snapshots = snapshotMap(s.execution);
8011
8857
  const computed = computeFlowStateFromSnapshots(s.flowDef, snapshots);
8012
- const nodes = { ...s.execution.nodes };
8858
+ const nodes = nodeExecutionRecord(Object.entries(s.execution.nodes));
8013
8859
  for (const ns of computed.nodes) {
8014
8860
  const existing = nodes[ns.nodeId];
8015
8861
  if (!existing) continue;
8016
- if (existing.status === "not_started" && ns.status === "available") {
8017
- nodes[ns.nodeId] = { ...existing, status: "available" };
8862
+ if (existing.status === "not_started" || existing.status === "available") {
8863
+ const hasFailedPreconditions = (ns.failedPreconditions?.length ?? 0) > 0;
8864
+ const status = existing.status === "available" && ns.status === "not_started" && !hasFailedPreconditions ? "available" : ns.status;
8865
+ nodes[ns.nodeId] = {
8866
+ ...existing,
8867
+ status,
8868
+ failedPreconditions: hasFailedPreconditions ? ns.failedPreconditions : void 0
8869
+ };
8018
8870
  }
8019
8871
  }
8020
8872
  let flowStatus = s.execution.status;
8021
8873
  if (flowStatus !== "cancelled") {
8022
- const anyRequiredFailed = s.flowDef.nodes.filter((n) => n.type === "skill" && !(n.data?.optional ?? false)).some((n) => nodes[n.id]?.status === "failed");
8874
+ const anyRequiredFailed = s.flowDef.nodes.filter((n) => isActiveRuntimeNode(n) && !nodeIsOptional(n)).some((n) => nodes[n.id]?.status === "failed");
8023
8875
  if (anyRequiredFailed) {
8024
8876
  flowStatus = "failed";
8025
8877
  } else if (computed.done) {
@@ -8037,13 +8889,62 @@ var FlowAdapter = class extends AbstractConnector {
8037
8889
  };
8038
8890
  }
8039
8891
  /**
8040
- * Build curated context for a node by collecting the summaries and
8041
- * artifact refs produced by its upstream flow-type predecessors.
8892
+ * Build the selected agent node's complete turn briefing plus upstream context.
8042
8893
  */
8043
8894
  buildHandoff(s, nodeId) {
8895
+ const target = findNode(s.flowDef, nodeId);
8896
+ assertLegal(isAgentNode(target), `node is not an active agent node: ${nodeId}`);
8897
+ const targetExecution = s.execution.nodes[nodeId];
8898
+ assertLegal(targetExecution, `node not in execution: ${nodeId}`);
8899
+ assertLegal(
8900
+ targetExecution.status === "available" || targetExecution.status === "running",
8901
+ `cannot build handoff for ${nodeId}: status is ${targetExecution.status}`
8902
+ );
8903
+ const sections = [`# Agent node: ${nodeLabel(target)}`, `Node ID: ${target.id}`];
8904
+ const resolved = resolveNodeBindings(
8905
+ s.flowDef,
8906
+ target,
8907
+ snapshotMap(s.execution),
8908
+ s.flowInputOrigins
8909
+ );
8910
+ const instruction = nodeInstruction(target);
8911
+ if (instruction) {
8912
+ sections.push(
8913
+ `## Instruction
8914
+
8915
+ ${interpolateNodeInstruction(instruction, resolved.values)}`
8916
+ );
8917
+ }
8918
+ if (Object.keys(resolved.values).length > 0) {
8919
+ sections.push(
8920
+ `## Supplied bindings
8921
+
8922
+ \`\`\`json
8923
+ ${JSON.stringify(resolved.values, null, 2)}
8924
+ \`\`\``,
8925
+ `## Binding origins
8926
+
8927
+ \`\`\`json
8928
+ ${JSON.stringify(resolved.origins, null, 2)}
8929
+ \`\`\``
8930
+ );
8931
+ }
8932
+ if (targetExecution.outputCorrection) {
8933
+ sections.push(
8934
+ `## Output correction required
8935
+
8936
+ This node has one corrective output call available. Fix every issue before calling \`complete_node\` again:
8937
+ ${targetExecution.outputCorrection.issues.map((issue) => `- ${issue}`).join("\n")}`
8938
+ );
8939
+ }
8940
+ const assets = nodeAssets(target);
8941
+ if (assets.length > 0) {
8942
+ sections.push(`## Required assets
8943
+
8944
+ ${assets.map((asset) => `- ${asset}`).join("\n")}`);
8945
+ }
8044
8946
  const deps = flowPredecessors(s.flowDef, nodeId);
8045
- if (deps.length === 0) return "(no prior context)";
8046
- const sections = [];
8947
+ const handoff = [];
8047
8948
  for (const depId of deps) {
8048
8949
  const exec = s.execution.nodes[depId];
8049
8950
  const output = exec?.output;
@@ -8052,16 +8953,42 @@ var FlowAdapter = class extends AbstractConnector {
8052
8953
  Artifacts: ${output.artifacts.map((a) => a.uri).join(", ")}` : "";
8053
8954
  const concerns = output.concerns && output.concerns.length > 0 ? `
8054
8955
  Concerns: ${output.concerns.join("; ")}` : "";
8055
- const label = s.flowDef.nodes.find((n) => n.id === depId)?.data?.label ?? depId;
8056
- sections.push(`### ${label} (${exec?.status})
8956
+ const predecessor = s.flowDef.nodes.find((n) => n.id === depId);
8957
+ const label = predecessor ? nodeLabel(predecessor) : depId;
8958
+ handoff.push(`### ${label} (${exec?.status})
8057
8959
  ${output.summary}${artifacts}${concerns}`);
8058
8960
  }
8059
- return sections.length > 0 ? sections.join("\n\n") : "(no prior context)";
8961
+ sections.push(
8962
+ `## Upstream handoff
8963
+
8964
+ ${handoff.length > 0 ? handoff.join("\n\n") : "(no prior context)"}`
8965
+ );
8966
+ return sections.join("\n\n");
8060
8967
  }
8061
8968
  };
8062
8969
  function flowPredecessors(flowDef, nodeId) {
8063
8970
  return flowDef.edges.filter((e) => e.target === nodeId && e.type === "flow").map((e) => e.source);
8064
8971
  }
8972
+ function functionNodeOutput(node, result) {
8973
+ const capturedFields = [];
8974
+ for (const channel of ["stdout", "stderr"]) {
8975
+ const destination = node.run.capture?.[channel];
8976
+ if (!destination) continue;
8977
+ const match = /^fields\.([^.]+)$/.exec(destination);
8978
+ if (!match?.[1]) {
8979
+ throw new Error(
8980
+ `Function node ${node.id} run.capture.${channel} destination "${destination}" is invalid; expected fields.<name>`
8981
+ );
8982
+ }
8983
+ capturedFields.push([match[1], result[channel]]);
8984
+ }
8985
+ const fields = Object.fromEntries(capturedFields);
8986
+ return {
8987
+ summary: `Function node ${node.id} exited with status ${result.exitStatus}.`,
8988
+ artifacts: [],
8989
+ ...Object.keys(fields).length > 0 ? { fields } : {}
8990
+ };
8991
+ }
8065
8992
  function safeParseStringArray(value) {
8066
8993
  if (!value) return [];
8067
8994
  if (Array.isArray(value)) return value.map(String);
@@ -8075,6 +9002,27 @@ function safeParseStringArray(value) {
8075
9002
  }
8076
9003
  return [];
8077
9004
  }
9005
+ var JsonFieldsSchema = z4.record(z4.string(), z4.json());
9006
+ function safeParseFields(value) {
9007
+ let parsed = value;
9008
+ if (typeof value === "string") {
9009
+ try {
9010
+ parsed = JSON.parse(value);
9011
+ } catch {
9012
+ return { issues: ["fields: malformed JSON; expected an object"] };
9013
+ }
9014
+ }
9015
+ const result = JsonFieldsSchema.safeParse(parsed);
9016
+ if (!result.success) {
9017
+ return {
9018
+ issues: result.error.issues.map((issue) => {
9019
+ const suffix = issue.path.map(String).join(".");
9020
+ return `fields${suffix ? `.${suffix}` : ""}: ${issue.message}`;
9021
+ })
9022
+ };
9023
+ }
9024
+ return { fields: result.data, issues: [] };
9025
+ }
8078
9026
  function safeParseArtifacts(value) {
8079
9027
  if (!value) return [];
8080
9028
  const raw = typeof value === "string" ? safeJsonArray(value) : Array.isArray(value) ? value : [];
@@ -8109,6 +9057,6 @@ function createConnector() {
8109
9057
  return new FlowAdapter();
8110
9058
  }
8111
9059
 
8112
- export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, BoxConnector, ConnectorFieldMissingError, ConnectorManager, ConnectorStartupError, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, PortPool, PostgresConnector, S3Connector, SQLiteConnector, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, __test, atomicReplaceCredential, buildConnectorPromptSection, buildSdkConnectorTools, buildSdkFlowTools, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector20, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createFsWatcher, createWorktree, defaultSpawn, ensureDirMode, ensureFleetMounted, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, readGitSyncStatus, registerBuiltinConnectors, removeMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock };
8113
- //# sourceMappingURL=chunk-6QSGCHE5.js.map
8114
- //# sourceMappingURL=chunk-6QSGCHE5.js.map
9060
+ export { AbstractConnector, BUILTIN_CONNECTOR_CATALOG, BoxConnector, ConnectorFieldMissingError, ConnectorManager, ConnectorStartupError, DeployConnector, DevServerConnector, FlowAdapter, GitConnector, GmailConnector, GoogleDriveConnector, LocalConnector, LogBuffer, ManagedGitconfigCollisionError, MattermostConnector, MemoryConnector, MinIOConnector, PortPool, PostgresConnector, S3Connector, SQLiteConnector, SessionInlineSubFlowRunner, SessionNodeExecutor, SharePointConnector, StaticServerConnector, TunnelConnector, WebDAVConnector, XStateConnector, XStateStoreConnector, __test, atomicReplaceCredential, buildConnectorPromptSection, buildSdkConnectorTools, buildSdkFlowTools, createConnector, createConnector10, createConnector11, createConnector12, createConnector13, createConnector14, createConnector15, createConnector16, createConnector17, createConnector18, createConnector19, createConnector2, createConnector20, createConnector3, createConnector4, createConnector5, createConnector6, createConnector7, createConnector8, createConnector9, createFsWatcher, createWorktree, defaultSpawn, ensureDirMode, ensureFleetMounted, findGitRoot, findMissingPackages, getConnector, installNpmPackages, isFleetManaged, isPackageResolvable, listConnectors, listWorktrees, readGitSyncStatus, registerBuiltinConnectors, removeMountBlock, renderCredentialHelperScript, tryGetConnector, worktreeRegistry, writeHelperScript, writeMountBlock };
9061
+ //# sourceMappingURL=chunk-EEHOMCRU.js.map
9062
+ //# sourceMappingURL=chunk-EEHOMCRU.js.map