@oomol-lab/open-flow 0.1.0-beta.69 → 0.1.0-beta.70

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 (49) hide show
  1. package/dist/browser/{ContentIcon-CsECY5id.js → ContentIcon-CUQHjQIw.js} +16 -8
  2. package/dist/browser/{FlowCanvas-C5X22xQ7.js → FlowCanvas-CqtUDPGj.js} +11 -26
  3. package/dist/browser/{createResourceDialog-Di5BBrQv.js → createResourceDialog-BAHYW9yu.js} +3 -3
  4. package/dist/browser/{dialog-Bjo-r9B0.js → dialog-DcGOzUP2.js} +162 -12
  5. package/dist/browser/{exportSvg-Cj6Spxx-.js → exportSvg-Dv5Qgrq7.js} +2 -2
  6. package/dist/browser/flow-authoring.js +95 -12
  7. package/dist/browser/flow-change-schema.d.ts +24 -1
  8. package/dist/browser/flow-change.d.ts +33 -4
  9. package/dist/browser/flow-change.js +96 -13
  10. package/dist/browser/{flowChanges-Dp-rcEJ6.js → flowChanges-CYyehKer.js} +2593 -2254
  11. package/dist/browser/{flowWorkspace-CB1QACku.js → flowWorkspace-qYUZPJHR.js} +1114 -392
  12. package/dist/browser/preview.css +225 -221
  13. package/dist/browser/preview.js +3781 -2655
  14. package/dist/browser/{tooltip-CJjYs_2I.js → tooltip-wZJRB9oP.js} +28 -1
  15. package/dist/browser/ui.css +8 -20
  16. package/dist/browser/workbench.css +225 -221
  17. package/dist/browser/workbench.js +31 -10
  18. package/dist/browser/{workbenchSelect-C3hxUcYm.js → workbenchSelect-D8_vP2p5.js} +2 -2
  19. package/dist/command/index.js +45 -45
  20. package/dist/common/authoring.d.ts +3 -3
  21. package/dist/common/authoringExamples.d.ts +3 -2
  22. package/dist/common/authoringSchema.d.ts +37 -374
  23. package/dist/common/control-api-conformance.js +151 -55
  24. package/dist/common/control-api.d.ts +12 -78
  25. package/dist/common/control-api.js +162 -39
  26. package/dist/common/control-requests.d.ts +3 -3
  27. package/dist/common/control-requests.js +221 -35
  28. package/dist/common/decision.js +85 -7
  29. package/dist/common/engine-contract.d.ts +4 -2
  30. package/dist/common/flow-encoding.js +110 -14
  31. package/dist/common/flow-graph.d.ts +3 -0
  32. package/dist/common/flow-semantics.js +273 -14
  33. package/dist/common/integration-trigger.js +85 -7
  34. package/dist/common/liveDetails.d.ts +15 -0
  35. package/dist/common/mcp.d.ts +3 -3
  36. package/dist/common/mcp.js +134 -38
  37. package/dist/common/openapi.js +85 -7
  38. package/dist/common/poll-trigger.js +85 -7
  39. package/dist/common/run-events.d.ts +2 -0
  40. package/dist/common/run-events.js +59 -2
  41. package/dist/common/runtime-contract.js +14 -4
  42. package/dist/common/scheduler.d.ts +6 -2
  43. package/dist/common/scheduler.js +178 -11
  44. package/dist/common/webhook-response.d.ts +43 -0
  45. package/dist/common/webhook-trigger.d.ts +12 -0
  46. package/dist/common/webhook-trigger.js +236 -9
  47. package/dist/common/workflow.js +85 -7
  48. package/package.json +1 -1
  49. package/skills/open-flow/references/flow-authoring.md +15 -10
@@ -1,3 +1,47 @@
1
+ //#region src/flow/common/webhookResponse.ts
2
+ function webhookResponseAllowsBody(status) {
3
+ return status !== 204 && status !== 205 && status !== 304;
4
+ }
5
+ var webhookHeadersPort = {
6
+ jsonSchema: {
7
+ type: "object",
8
+ additionalProperties: { type: [
9
+ "string",
10
+ "number",
11
+ "boolean"
12
+ ] }
13
+ },
14
+ nullable: true
15
+ };
16
+ /** Header names are case-insensitive; never silently combine duplicate business fields. */
17
+ function webhookHeaderNameIssue(name, names) {
18
+ if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) return "invalid";
19
+ if (names.some((candidate) => candidate.toLowerCase() === name.toLowerCase())) return "duplicate";
20
+ }
21
+ webhookHeadersPort.jsonSchema;
22
+ function validWebhookStatus(value) {
23
+ return typeof value === "number" && Number.isInteger(value) && value >= 200 && value <= 599 && webhookResponseAllowsBody(value);
24
+ }
25
+ /** Portable validation also runs in the isolated engine, which has no HTTP globals. */
26
+ function webhookHeaderValues(source) {
27
+ if (source == null || typeof source !== "object" || Array.isArray(source)) return;
28
+ const headers = {};
29
+ const names = [];
30
+ for (const [name, value] of Object.entries(source)) {
31
+ if (webhookHeaderNameIssue(name, names) != null) return;
32
+ if (typeof value !== "string" && typeof value !== "boolean" && !(typeof value === "number" && Number.isFinite(value))) return;
33
+ const text = String(value);
34
+ if ([...text].some((character) => character.charCodeAt(0) > 255 || /[\0\r\n]/.test(character))) return;
35
+ names.push(name);
36
+ Object.defineProperty(headers, name, {
37
+ value: text,
38
+ enumerable: true,
39
+ configurable: true
40
+ });
41
+ }
42
+ return headers;
43
+ }
44
+ //#endregion
1
45
  //#region src/execution/common/events.ts
2
46
  var encoder = new TextEncoder();
3
47
  var nodeKinds = /* @__PURE__ */ new Set([
@@ -12,7 +56,8 @@ var nodeKinds = /* @__PURE__ */ new Set([
12
56
  "value",
13
57
  "wait",
14
58
  "outputs",
15
- "workflow"
59
+ "workflow",
60
+ "webhook-response"
16
61
  ]);
17
62
  function object(value, description) {
18
63
  if (value == null || typeof value != "object" || Array.isArray(value)) throw new TypeError(`${description} must be an object.`);
@@ -167,7 +212,8 @@ function createEventProjector(platformRunId, nodeFailureCodes) {
167
212
  case "node.completed": return {
168
213
  kind: type,
169
214
  payload,
170
- value: object(event.outputs, "Runtime node.completed outputs")
215
+ value: object(event.outputs, "Runtime node.completed outputs"),
216
+ ...event.response == null || context.rawRunId !== platformRunId ? {} : { response: responseSnapshot(event.response) }
171
217
  };
172
218
  case "node.started": {
173
219
  jobs.set(JSON.stringify([context.rawRunId, event.jobId]), context.nodeId);
@@ -234,5 +280,16 @@ function createEventProjector(platformRunId, nodeFailureCodes) {
234
280
  throw new TypeError(`Runtime event type ${JSON.stringify(type)} is not supported.`);
235
281
  };
236
282
  }
283
+ function responseSnapshot(value) {
284
+ const source = object(value, "Runtime response");
285
+ const headers = webhookHeaderValues(source.headers);
286
+ if (!validWebhookStatus(source.status) || typeof source.body !== "string" || !["text/plain;charset=UTF-8", "application/json"].includes(String(source.contentType)) || headers == null) throw new TypeError("Invalid runtime response.");
287
+ return {
288
+ status: source.status,
289
+ body: source.body,
290
+ contentType: String(source.contentType),
291
+ headers
292
+ };
293
+ }
237
294
  //#endregion
238
295
  export { createEventProjector, projectExecutionId, message as sanitizeRuntimeErrorMessage };
@@ -23,9 +23,11 @@ function dequal(foo, bar) {
23
23
  }
24
24
  //#endregion
25
25
  //#region src/execution/common/engineContract.ts
26
- var currentEngineContract = "open-flow-engine/v6";
27
- var nodejsEngineContract = "open-flow-engine/v6/nodejs-compat-v1";
26
+ var currentEngineContract = "open-flow-engine/v7";
27
+ var nodejsEngineContract = "open-flow-engine/v7/nodejs-compat-v1";
28
28
  var currentContract = {
29
+ waitAutoContinue: true,
30
+ webhookResponses: true,
29
31
  platformExports: /* @__PURE__ */ new Set(["engineContract", "identity"]),
30
32
  platformModule: "open-flow:platform",
31
33
  platformSource: `export const engineContract = ${JSON.stringify(currentEngineContract)}
@@ -65,8 +67,16 @@ export function identity(value) { return value }
65
67
  `
66
68
  };
67
69
  function findEngineContract(contract) {
68
- if (contract == "open-flow-engine/v6") return currentContract;
69
- if (contract == "open-flow-engine/v6/nodejs-compat-v1") return nodejsContract;
70
+ if (contract == "open-flow-engine/v7") return currentContract;
71
+ if (contract == "open-flow-engine/v7/nodejs-compat-v1") return nodejsContract;
72
+ if (contract == "open-flow-engine/v6" || contract == "open-flow-engine/v6/nodejs-compat-v1") return {
73
+ ...contract == "open-flow-engine/v6" ? currentContract : nodejsContract,
74
+ waitAutoContinue: false,
75
+ webhookResponses: false,
76
+ platformSource: `export const engineContract = ${JSON.stringify(contract)}
77
+ export function identity(value) { return value }
78
+ `
79
+ };
70
80
  }
71
81
  //#endregion
72
82
  //#region src/execution/common/runtime.ts
@@ -1,10 +1,12 @@
1
- import type { ConnectorCapability, JsonValue, WaitAction } from '../browser/flow-change.js';
1
+ import type { ConnectorCapability, JsonValue, WaitAction, WaitAutoContinue } from '../browser/flow-change.js';
2
2
  import type { PreparedFlow } from './flow-semantics.js';
3
+ import type { WebhookResponseSnapshot } from './webhook-response.js';
3
4
  import type { PreviousNode } from '../index.js';
4
5
  import type { AgentCheckpoint } from './runtime-contract.js';
5
6
  import * as Effect from 'effect/Effect';
6
7
  export declare const withWaitNotificationMessage: (value: JsonValue) => JsonValue;
7
8
  export declare const normalizeWaitComment: (value?: string | null) => string | null;
9
+ export declare const waitAutoContinueAt: (config: WaitAutoContinue, waitingSince: number) => number;
8
10
  export interface WaitResolution {
9
11
  readonly action: WaitAction;
10
12
  readonly resolvedAt: string;
@@ -28,7 +30,7 @@ export type SchedulerEvent = {
28
30
  readonly inputs: Readonly<Record<string, JsonValue>>;
29
31
  readonly jobId: string;
30
32
  readonly nodeId: string;
31
- readonly nodeKind: 'agent' | 'approval' | 'condition' | 'connector' | 'decision' | 'javascript' | 'openapi' | 'llm' | 'value' | 'wait' | 'outputs' | 'workflow';
33
+ readonly nodeKind: 'agent' | 'approval' | 'condition' | 'connector' | 'decision' | 'javascript' | 'openapi' | 'llm' | 'value' | 'wait' | 'outputs' | 'workflow' | 'webhook-response';
32
34
  readonly nodeTitle?: string;
33
35
  readonly runId: string;
34
36
  readonly type: 'node.started';
@@ -38,6 +40,7 @@ export type SchedulerEvent = {
38
40
  readonly outputs: Readonly<Record<string, JsonValue>>;
39
41
  readonly runId: string;
40
42
  readonly systemOutputs?: Readonly<Record<string, JsonValue>>;
43
+ readonly response?: WebhookResponseSnapshot;
41
44
  readonly type: 'node.completed';
42
45
  } | {
43
46
  readonly code: string;
@@ -86,6 +89,7 @@ export interface PendingWait {
86
89
  readonly pending?: JsonValue;
87
90
  }
88
91
  export interface WaitRequest {
92
+ readonly autoContinue?: WaitAutoContinue;
89
93
  readonly jobId: string;
90
94
  readonly nodeId: string;
91
95
  readonly value: JsonValue;
@@ -1035,7 +1035,7 @@ function timeSource(args) {
1035
1035
  function time$1(args) {
1036
1036
  return new RegExp(`^${timeSource(args)}$`);
1037
1037
  }
1038
- function datetime(args) {
1038
+ function datetime$1(args) {
1039
1039
  const opts = ["Z"];
1040
1040
  if (args.offset) opts.push(`([+-](?:[01]\\d|2[0-3]):[0-5]\\d)`);
1041
1041
  const qualified = `${timeSource({
@@ -1686,7 +1686,7 @@ var $ZodKSUID = /*@__PURE__*/ $constructor("$ZodKSUID", (inst, def) => {
1686
1686
  $ZodStringFormat.init(inst, def);
1687
1687
  });
1688
1688
  var $ZodISODateTime = /*@__PURE__*/ $constructor("$ZodISODateTime", (inst, def) => {
1689
- def.pattern ?? (def.pattern = datetime(def));
1689
+ def.pattern ?? (def.pattern = datetime$1(def));
1690
1690
  $ZodStringFormat.init(inst, def);
1691
1691
  });
1692
1692
  var $ZodISODate = /*@__PURE__*/ $constructor("$ZodISODate", (inst, def) => {
@@ -5683,6 +5683,11 @@ function preprocess(fn, schema) {
5683
5683
  });
5684
5684
  }
5685
5685
  //#endregion
5686
+ //#region ../../node_modules/.bun/zod@4.6.5/node_modules/zod/v4/classic/iso.js
5687
+ function datetime(params) {
5688
+ return /* @__PURE__ */ _isoDateTime(ZodISODateTime, params);
5689
+ }
5690
+ //#endregion
5686
5691
  //#region ../../node_modules/.bun/@cfworker+json-schema@4.1.1/node_modules/@cfworker/json-schema/dist/esm/deep-compare-strict.js
5687
5692
  function deepCompareStrict(a, b) {
5688
5693
  const typeofa = typeof a;
@@ -9506,6 +9511,39 @@ var triggerScheduleSchema = array(union([object({
9506
9511
  value: number()
9507
9512
  })]));
9508
9513
  //#endregion
9514
+ //#region src/localization/common/timeZone.ts
9515
+ /** Validate a named timezone using the same runtime data used for date conversion. */
9516
+ function isTimeZone(value) {
9517
+ try {
9518
+ Intl.DateTimeFormat("en", { timeZone: value });
9519
+ return true;
9520
+ } catch {
9521
+ return false;
9522
+ }
9523
+ }
9524
+ //#endregion
9525
+ //#region src/flow/common/wait.ts
9526
+ /** Drafts retain incomplete values; semantic validation gates publication and execution. */
9527
+ var waitAutoContinueDraftSchema = discriminatedUnion("kind", [strictObject({
9528
+ kind: literal("after"),
9529
+ durationMs: number().nullable()
9530
+ }), strictObject({
9531
+ kind: literal("at"),
9532
+ at: string$1(),
9533
+ timezone: string$1().optional()
9534
+ })]);
9535
+ /** Static business configuration; the deployment owns the allowed wait lifetime. */
9536
+ var waitAutoContinueSchema = discriminatedUnion("kind", [waitAutoContinueDraftSchema.options[0].extend({ durationMs: number().int().positive().max(Number.MAX_SAFE_INTEGER) }), waitAutoContinueDraftSchema.options[1].extend({
9537
+ at: datetime({ offset: true }),
9538
+ timezone: string$1().refine(isTimeZone, "Select a valid IANA timezone.").optional()
9539
+ })]);
9540
+ /** Resolve one registration's intent; the deployment separately enforces its lifetime. */
9541
+ function waitAutoContinueAt$1(config, waitingSince) {
9542
+ const timing = waitAutoContinueSchema.parse(config);
9543
+ const at = timing.kind == "after" ? waitingSince + timing.durationMs : Date.parse(timing.at);
9544
+ return Math.max(waitingSince, at);
9545
+ }
9546
+ //#endregion
9509
9547
  //#region src/flow/common/webhookMethod.ts
9510
9548
  var webhookMethods = [
9511
9549
  "GET",
@@ -9517,6 +9555,84 @@ var webhookMethods = [
9517
9555
  function webhookSupportsBody(method) {
9518
9556
  return method !== "GET";
9519
9557
  }
9558
+ //#endregion
9559
+ //#region src/flow/common/webhookResponse.ts
9560
+ var webhookResponseTimeout = {
9561
+ defaultMs: 3e4,
9562
+ minMs: 1e3,
9563
+ maxMs: 6e4
9564
+ };
9565
+ function webhookResponseAllowsBody(status) {
9566
+ return status !== 204 && status !== 205 && status !== 304;
9567
+ }
9568
+ var webhookHeadersPort = {
9569
+ jsonSchema: {
9570
+ type: "object",
9571
+ additionalProperties: { type: [
9572
+ "string",
9573
+ "number",
9574
+ "boolean"
9575
+ ] }
9576
+ },
9577
+ nullable: true
9578
+ };
9579
+ /** Header names are case-insensitive; never silently combine duplicate business fields. */
9580
+ function webhookHeaderNameIssue(name, names) {
9581
+ if (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name)) return "invalid";
9582
+ if (names.some((candidate) => candidate.toLowerCase() === name.toLowerCase())) return "duplicate";
9583
+ }
9584
+ /** Fixed inputs belong to the node, not author-supplied definitions. */
9585
+ var webhookResponseInputs = [
9586
+ {
9587
+ handle: "body",
9588
+ jsonSchema: {},
9589
+ nullable: true,
9590
+ value: ""
9591
+ },
9592
+ {
9593
+ handle: "statusCode",
9594
+ jsonSchema: { type: "number" },
9595
+ nullable: false,
9596
+ value: 200
9597
+ },
9598
+ {
9599
+ handle: "headers",
9600
+ jsonSchema: webhookHeadersPort.jsonSchema,
9601
+ nullable: false,
9602
+ value: {}
9603
+ }
9604
+ ];
9605
+ function validWebhookStatus(value) {
9606
+ return typeof value === "number" && Number.isInteger(value) && value >= 200 && value <= 599 && webhookResponseAllowsBody(value);
9607
+ }
9608
+ /** Portable validation also runs in the isolated engine, which has no HTTP globals. */
9609
+ function webhookHeaderValues(source) {
9610
+ if (source == null || typeof source !== "object" || Array.isArray(source)) return;
9611
+ const headers = {};
9612
+ const names = [];
9613
+ for (const [name, value] of Object.entries(source)) {
9614
+ if (webhookHeaderNameIssue(name, names) != null) return;
9615
+ if (typeof value !== "string" && typeof value !== "boolean" && !(typeof value === "number" && Number.isFinite(value))) return;
9616
+ const text = String(value);
9617
+ if ([...text].some((character) => character.charCodeAt(0) > 255 || /[\0\r\n]/.test(character))) return;
9618
+ names.push(name);
9619
+ Object.defineProperty(headers, name, {
9620
+ value: text,
9621
+ enumerable: true,
9622
+ configurable: true
9623
+ });
9624
+ }
9625
+ return headers;
9626
+ }
9627
+ function encodeWebhookBody(value) {
9628
+ return typeof value === "string" ? {
9629
+ body: value,
9630
+ contentType: "text/plain;charset=UTF-8"
9631
+ } : {
9632
+ body: JSON.stringify(value),
9633
+ contentType: "application/json"
9634
+ };
9635
+ }
9520
9636
  var text = string$1();
9521
9637
  var actionConnection = text.describe("Account selection: omit while unconfigured; use an account ID to select it; use \"\" to explicitly defer configuration. Deferred Actions fail if called; deferred Agent tools are excluded from the callable tool set. Not supported by Triggers.");
9522
9638
  var json = json$1();
@@ -9744,9 +9860,24 @@ var webhookOptions = object({
9744
9860
  allowedMethods: never().optional(),
9745
9861
  allowedOrigins: strings.optional(),
9746
9862
  noResponseBody: never().optional(),
9863
+ responseBody: discriminatedUnion("kind", [strictObject({
9864
+ kind: literal("workflow-outputs"),
9865
+ output: text.optional(),
9866
+ timeoutMs: number().int().min(webhookResponseTimeout.minMs).max(webhookResponseTimeout.maxMs).optional()
9867
+ }), strictObject({
9868
+ kind: literal("webhook-response"),
9869
+ nodeId: text.optional(),
9870
+ timeoutMs: number().int().min(webhookResponseTimeout.minMs).max(webhookResponseTimeout.maxMs).optional()
9871
+ })]).optional(),
9747
9872
  responseData: text.optional(),
9748
9873
  responseHeaders: record$1(text, text).optional(),
9749
- responseStatusCode: number().int().min(200).max(599).optional()
9874
+ responseHeadersSource: strictObject({
9875
+ kind: literal("workflow-outputs"),
9876
+ output: text.optional()
9877
+ }).optional(),
9878
+ responseHeaderOutputs: record$1(text, text).optional(),
9879
+ responseStatusCode: number().int().min(200).max(599).optional(),
9880
+ responseStatusCodeOutput: text.optional()
9750
9881
  });
9751
9882
  var webhook = {
9752
9883
  bodyFields: array(input),
@@ -9801,6 +9932,12 @@ var base = {
9801
9932
  onError: _enum(["stop", "error"]).optional()
9802
9933
  };
9803
9934
  var node = union([
9935
+ strictObject({
9936
+ ...base,
9937
+ kind: literal("webhook-response"),
9938
+ retry: never().optional(),
9939
+ onError: never().optional()
9940
+ }),
9804
9941
  strictObject({
9805
9942
  ...base,
9806
9943
  kind: literal("outputs"),
@@ -9833,7 +9970,8 @@ var node = union([
9833
9970
  strictObject({
9834
9971
  ...base,
9835
9972
  kind: literal("wait"),
9836
- ...wait
9973
+ ...wait,
9974
+ autoContinue: waitAutoContinueDraftSchema.optional()
9837
9975
  }).omit({ timeoutMs: true }),
9838
9976
  object({
9839
9977
  ...trigger,
@@ -9920,7 +10058,8 @@ object({
9920
10058
  literal(6),
9921
10059
  literal(7),
9922
10060
  literal(8),
9923
- literal(9)
10061
+ literal(9),
10062
+ literal(10)
9924
10063
  ]),
9925
10064
  document,
9926
10065
  modules: record$1(text, module)
@@ -10045,8 +10184,14 @@ var shapes = {
10045
10184
  },
10046
10185
  "graph.node.resolution.set": {
10047
10186
  ...at,
10048
- before: object(wait),
10049
- value: object(wait)
10187
+ before: strictObject({
10188
+ ...wait,
10189
+ autoContinue: waitAutoContinueDraftSchema.optional()
10190
+ }),
10191
+ value: strictObject({
10192
+ ...wait,
10193
+ autoContinue: waitAutoContinueDraftSchema.optional()
10194
+ })
10050
10195
  },
10051
10196
  "graph.node.webhook.set": {
10052
10197
  ...at,
@@ -10654,6 +10799,7 @@ function isResolutionNode(node) {
10654
10799
  }
10655
10800
  function nodeInputPorts(node) {
10656
10801
  switch (node.kind) {
10802
+ case "webhook-response": return portsByHandle(webhookResponseInputs);
10657
10803
  case "outputs": return portsByHandle(node.outputDefs);
10658
10804
  case "condition": return conditionInputPorts(node);
10659
10805
  case "value": return {};
@@ -10700,7 +10846,8 @@ function resolutionOutputPorts(node) {
10700
10846
  pattern: "\\S"
10701
10847
  },
10702
10848
  ...Object.fromEntries(actions.map((action) => [`${action}Url`, { type: "string" }])),
10703
- expiresAt: { type: "string" }
10849
+ expiresAt: { type: "string" },
10850
+ ...node.kind == "wait" && node.autoContinue != null ? { autoContinueAt: { type: "string" } } : {}
10704
10851
  }),
10705
10852
  ...Object.fromEntries(actions.map((action) => [action, objectPort({
10706
10853
  inputs,
@@ -10712,6 +10859,7 @@ function resolutionOutputPorts(node) {
10712
10859
  }
10713
10860
  function nodeOutputPorts(node) {
10714
10861
  switch (node.kind) {
10862
+ case "webhook-response":
10715
10863
  case "outputs":
10716
10864
  case "condition": return {};
10717
10865
  case "value": return portsByHandle(node.values);
@@ -11058,6 +11206,7 @@ function withWaitNotificationMessage$1(value) {
11058
11206
  pageUrl,
11059
11207
  message: source.message === void 0 ? [
11060
11208
  source.prompt,
11209
+ ...typeof source.autoContinueAt == "string" ? [`Automatically continues at: ${source.autoContinueAt}`] : [],
11061
11210
  `Expires at: ${source.expiresAt}`,
11062
11211
  `Review request: ${pageUrl}`
11063
11212
  ].join("\n\n") : source.message
@@ -11067,6 +11216,7 @@ function withWaitNotificationMessage$1(value) {
11067
11216
  //#region src/execution/common/scheduler.ts
11068
11217
  var withWaitNotificationMessage = withWaitNotificationMessage$1;
11069
11218
  var normalizeWaitComment = normalizeWaitComment$1;
11219
+ var waitAutoContinueAt = waitAutoContinueAt$1;
11070
11220
  var PortValidationError = class extends Error {};
11071
11221
  function usesPending(graph, nodeId) {
11072
11222
  return graph.edges.some((edge) => edge.source == nodeId && edge.sourceHandle == "pending") || Object.values(graph.nodes).some((node) => !isTriggerNode(node) && Object.values(nodeInputMappings(node)).some((mapping) => mapping.kind == "sources" && mapping.sources.some((source) => source.kind == "node" && source.nodeId == nodeId && source.output == "pending")));
@@ -11471,7 +11621,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11471
11621
  const systemFrame = dependencies.systemInputValues(nodeId);
11472
11622
  return Object.fromEntries(Object.entries(nodeInputPorts(node)).map(([handle, port]) => [handle, resolveInput(mappings[handle], port, launch[nodeId]?.[handle], `Node "${nodeId}" input "${handle}"`, frame, node.kind == "condition", systemFrame)]));
11473
11623
  };
11474
- const commit = (nodeId, jobId, outputs, releasedHandles = Object.keys(outputs), failure) => Effect.uninterruptible(Effect.gen(function* () {
11624
+ const commit = (nodeId, jobId, outputs, releasedHandles = Object.keys(outputs), failure, response) => Effect.uninterruptible(Effect.gen(function* () {
11475
11625
  const node = target.graph.nodes[nodeId];
11476
11626
  const systemOutputs = nodeSystemOutputPorts(node).error == null ? void 0 : { error: failure == null ? null : {
11477
11627
  code: failure.code,
@@ -11489,6 +11639,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11489
11639
  outputs,
11490
11640
  runId,
11491
11641
  ...system,
11642
+ ...response == null ? {} : { response },
11492
11643
  type: "node.completed"
11493
11644
  } : {
11494
11645
  ...failure,
@@ -11531,6 +11682,21 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11531
11682
  });
11532
11683
  let outputs;
11533
11684
  switch (node.kind) {
11685
+ case "webhook-response": {
11686
+ const status = nodeInputs.statusCode;
11687
+ if (!validWebhookStatus(status)) return yield* Effect.fail(new NodeValidationError("webhook.response-status-invalid", "Invalid HTTP response status."));
11688
+ const headers = webhookHeaderValues(nodeInputs.headers);
11689
+ if (headers == null) return yield* Effect.fail(new NodeValidationError("webhook.response-headers-invalid", "Invalid HTTP response headers."));
11690
+ return {
11691
+ kind: "completed",
11692
+ outputs: {},
11693
+ response: {
11694
+ ...encodeWebhookBody(nodeInputs.body),
11695
+ status,
11696
+ headers
11697
+ }
11698
+ };
11699
+ }
11534
11700
  case "outputs":
11535
11701
  outputs = nodeInputs;
11536
11702
  break;
@@ -11655,6 +11821,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11655
11821
  value,
11656
11822
  waitId,
11657
11823
  notify,
11824
+ ...node.kind == "wait" && node.autoContinue != null ? { autoContinue: node.autoContinue } : {},
11658
11825
  actions: isResolutionNode(node) ? resolutionActions(node) : ["approve", "reject"],
11659
11826
  prompt: isResolutionNode(node) ? node.prompt : JSON.stringify(value, null, 2),
11660
11827
  ...isResolutionNode(node) ? {} : { notification: agentNotice(node, agents[jobId].input) }
@@ -11686,7 +11853,7 @@ function runGraph(context, target, runId, launchInputs = {}, trigger, resume) {
11686
11853
  pendingCalls.delete(jobId);
11687
11854
  if (result.kind == "completed") {
11688
11855
  delete agents[jobId];
11689
- return commit(nodeId, jobId, result.outputs, result.releasedHandles);
11856
+ return commit(nodeId, jobId, result.outputs, result.releasedHandles, void 0, result.response);
11690
11857
  }
11691
11858
  agents[jobId] = result.agent;
11692
11859
  return createWait(nodeId, jobId, result.value);
@@ -11935,4 +12102,4 @@ function runFlow(prepared, options) {
11935
12102
  });
11936
12103
  }
11937
12104
  //#endregion
11938
- export { PortValidationError, decodeFlowRunCheckpoint, normalizeWaitComment, runFlow, waitRetentionMs, withWaitNotificationMessage };
12105
+ export { PortValidationError, decodeFlowRunCheckpoint, normalizeWaitComment, runFlow, waitAutoContinueAt, waitRetentionMs, withWaitNotificationMessage };
@@ -0,0 +1,43 @@
1
+ import type { InputPort, JsonValue, Port, PortDefinition } from '../browser/flow-change.js';
2
+ export declare const webhookResponseTimeout: {
3
+ readonly defaultMs: 30000;
4
+ readonly minMs: 1000;
5
+ readonly maxMs: 60000;
6
+ };
7
+ export declare const webhookStatusCodePort: {
8
+ readonly jsonSchema: {
9
+ readonly type: 'number';
10
+ };
11
+ readonly nullable: true;
12
+ };
13
+ export declare function webhookResponseAllowsBody(status: number): boolean;
14
+ export declare const webhookHeaderValuePort: {
15
+ readonly jsonSchema: {
16
+ readonly type: readonly ['string', 'number', 'boolean'];
17
+ };
18
+ readonly nullable: true;
19
+ };
20
+ export declare const webhookHeadersPort: {
21
+ readonly jsonSchema: {
22
+ readonly type: 'object';
23
+ readonly additionalProperties: {
24
+ readonly type: readonly ['string', 'number', 'boolean'];
25
+ };
26
+ };
27
+ readonly nullable: true;
28
+ };
29
+ export declare function workflowOutputsPort(outputs: readonly Port[]): PortDefinition;
30
+ /** Header names are case-insensitive; never silently combine duplicate business fields. */
31
+ export declare function webhookHeaderNameIssue(name: string, names: readonly string[]): 'invalid' | 'duplicate' | undefined;
32
+ /** Fixed inputs belong to the node, not author-supplied definitions. */
33
+ export declare const webhookResponseInputs: readonly InputPort[];
34
+ export interface WebhookResponseSnapshot {
35
+ readonly body: string;
36
+ readonly contentType: string;
37
+ readonly status: number;
38
+ readonly headers: Readonly<Record<string, string>>;
39
+ }
40
+ export declare function validWebhookStatus(value: unknown): value is number;
41
+ /** Portable validation also runs in the isolated engine, which has no HTTP globals. */
42
+ export declare function webhookHeaderValues(source: unknown): Readonly<Record<string, string>> | undefined;
43
+ export declare function encodeWebhookBody(value: JsonValue): Pick<WebhookResponseSnapshot, 'body' | 'contentType'>;
@@ -1,6 +1,18 @@
1
1
  import type { JsonValue, WebhookBodyField, WebhookMethod, WebhookOptions } from '../browser/flow-change.js';
2
2
  export { webhookSupportsBody } from './trigger-contract.js';
3
+ export { webhookResponseAllowsBody, webhookResponseTimeout } from './webhook-response.js';
4
+ export type { WebhookResponseSnapshot } from './webhook-response.js';
3
5
  export declare const maximumWebhookBodyBytes: number;
6
+ /** Serialize only declared workflow business outputs, never node logs or execution metadata. */
7
+ export declare function webhookResponseBody(result: unknown, source: Extract<NonNullable<WebhookOptions['responseBody']>, {
8
+ kind: 'workflow-outputs';
9
+ }>): {
10
+ readonly body: string;
11
+ readonly contentType: string;
12
+ } | undefined;
13
+ export declare function webhookResponseStatusCode(result: unknown, output: string): number | undefined;
14
+ /** Project business headers before the HTTP layer applies its deployment-owned header policy. */
15
+ export declare function webhookResponseHeaders(result: unknown, options: WebhookOptions): Readonly<Record<string, string>> | undefined;
4
16
  export declare function webhookEndpointId(url: URL): string | undefined;
5
17
  export declare function webhookOccurrenceId(endpointId: string, runtimeVersion: number, key: string | null): Promise<string | undefined>;
6
18
  export interface WebhookConformanceFixture {