@osolmaz/pi-workflows 0.16.0 → 0.16.2

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 (175) hide show
  1. package/README.md +18 -2
  2. package/dist/channels/adapter-entry.d.ts +3 -0
  3. package/dist/channels/adapter-entry.js +203 -0
  4. package/dist/channels/adapter-entry.js.map +1 -0
  5. package/dist/channels/config.d.ts +43 -0
  6. package/dist/channels/config.js +234 -0
  7. package/dist/channels/config.js.map +1 -0
  8. package/dist/channels/protocol.d.ts +94 -0
  9. package/dist/channels/protocol.js +219 -0
  10. package/dist/channels/protocol.js.map +1 -0
  11. package/dist/channels/telegram.d.ts +46 -0
  12. package/dist/channels/telegram.js +285 -0
  13. package/dist/channels/telegram.js.map +1 -0
  14. package/dist/client/client.d.ts +1 -0
  15. package/dist/client/client.js +5 -1
  16. package/dist/client/client.js.map +1 -1
  17. package/dist/client/index.d.ts +1 -1
  18. package/dist/client/protocol.d.ts +1 -1
  19. package/dist/client/protocol.js +11 -5
  20. package/dist/client/protocol.js.map +1 -1
  21. package/dist/client/view.d.ts +48 -16
  22. package/dist/client/view.js +1 -0
  23. package/dist/client/view.js.map +1 -1
  24. package/dist/controllers/index.d.ts +1 -1
  25. package/dist/controllers/index.js.map +1 -1
  26. package/dist/controllers/sqlite.d.ts +20 -165
  27. package/dist/controllers/sqlite.js +178 -436
  28. package/dist/controllers/sqlite.js.map +1 -1
  29. package/dist/extension/index.d.ts +23 -2
  30. package/dist/extension/index.js +637 -534
  31. package/dist/extension/index.js.map +1 -1
  32. package/dist/extension/recorder.d.ts +95 -0
  33. package/dist/extension/recorder.js +530 -0
  34. package/dist/extension/recorder.js.map +1 -0
  35. package/dist/extension/remote-recorder-store.d.ts +25 -0
  36. package/dist/extension/remote-recorder-store.js +81 -0
  37. package/dist/extension/remote-recorder-store.js.map +1 -0
  38. package/dist/extension/session-events.d.ts +134 -0
  39. package/dist/extension/session-events.js +60 -0
  40. package/dist/extension/session-events.js.map +1 -0
  41. package/dist/extension/session-view.d.ts +2 -0
  42. package/dist/extension/session-view.js +60 -2
  43. package/dist/extension/session-view.js.map +1 -1
  44. package/dist/extension/step-message.d.ts +6 -4
  45. package/dist/extension/step-message.js +12 -6
  46. package/dist/extension/step-message.js.map +1 -1
  47. package/dist/extension/widget.d.ts +2 -2
  48. package/dist/extension/widget.js +28 -18
  49. package/dist/extension/widget.js.map +1 -1
  50. package/dist/extension/workflow-message-coordinator.d.ts +28 -0
  51. package/dist/extension/workflow-message-coordinator.js +346 -0
  52. package/dist/extension/workflow-message-coordinator.js.map +1 -0
  53. package/dist/host/channel-effects.d.ts +72 -0
  54. package/dist/host/channel-effects.js +271 -0
  55. package/dist/host/channel-effects.js.map +1 -0
  56. package/dist/host/channel-supervisor.d.ts +21 -0
  57. package/dist/host/channel-supervisor.js +54 -0
  58. package/dist/host/channel-supervisor.js.map +1 -0
  59. package/dist/host/runner.d.ts +50 -8
  60. package/dist/host/runner.js +1632 -341
  61. package/dist/host/runner.js.map +1 -1
  62. package/dist/host/state.d.ts +19 -16
  63. package/dist/host/state.js +251 -63
  64. package/dist/host/state.js.map +1 -1
  65. package/dist/host/view.d.ts +19 -9
  66. package/dist/host/view.js +227 -119
  67. package/dist/host/view.js.map +1 -1
  68. package/dist/host/worker-entry.d.ts +4 -1
  69. package/dist/host/worker-entry.js +23 -24
  70. package/dist/host/worker-entry.js.map +1 -1
  71. package/dist/host/worker-protocol.d.ts +15 -0
  72. package/dist/host/worker-protocol.js.map +1 -1
  73. package/dist/state/prune.js +3 -25
  74. package/dist/state/prune.js.map +1 -1
  75. package/dist/state/schema.js +76 -97
  76. package/dist/state/schema.js.map +1 -1
  77. package/dist/state/workflow-messages.d.ts +98 -0
  78. package/dist/state/workflow-messages.js +366 -0
  79. package/dist/state/workflow-messages.js.map +1 -0
  80. package/dist/viewer/render.js +1 -1
  81. package/dist/viewer/render.js.map +1 -1
  82. package/dist/viewer/tui.d.ts +1 -1
  83. package/dist/viewer/tui.js +65 -8
  84. package/dist/viewer/tui.js.map +1 -1
  85. package/dist/workflows/composition.js +0 -4
  86. package/dist/workflows/composition.js.map +1 -1
  87. package/dist/workflows/definition.js +0 -8
  88. package/dist/workflows/definition.js.map +1 -1
  89. package/dist/workflows/engine.js +4 -5
  90. package/dist/workflows/engine.js.map +1 -1
  91. package/dist/workflows/human-decision.d.ts +2 -0
  92. package/dist/workflows/human-decision.js +8 -2
  93. package/dist/workflows/human-decision.js.map +1 -1
  94. package/dist/workflows/index.d.ts +1 -1
  95. package/dist/workflows/index.js.map +1 -1
  96. package/dist/workflows/schema.js +0 -4
  97. package/dist/workflows/schema.js.map +1 -1
  98. package/dist/workflows/settings.d.ts +1 -8
  99. package/dist/workflows/settings.js.map +1 -1
  100. package/dist/workflows/store.d.ts +20 -19
  101. package/dist/workflows/store.js +163 -354
  102. package/dist/workflows/store.js.map +1 -1
  103. package/dist/workflows/tool-input.d.ts +21 -0
  104. package/dist/workflows/tool-input.js +23 -1
  105. package/dist/workflows/tool-input.js.map +1 -1
  106. package/dist/workflows/types.d.ts +6 -8
  107. package/dist/workflows/workflow-message-content.d.ts +38 -0
  108. package/dist/workflows/workflow-message-content.js +157 -0
  109. package/dist/workflows/workflow-message-content.js.map +1 -0
  110. package/docs/2026-08-18-herdr-piw-plan.md +2 -1
  111. package/docs/2026-08-20-durable-workflow-launch-plan.md +1 -1
  112. package/docs/2026-08-25-workflow-follow-ups.md +26 -50
  113. package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
  114. package/docs/2026-09-01-restore-session-delivery-controls-plan.md +4 -0
  115. package/docs/2026-09-01-unified-workflow-client-plan.md +5 -1
  116. package/docs/2026-09-02-installed-live-e2e-plan.md +15 -1
  117. package/docs/2026-09-02-unify-workflow-messages-plan.md +562 -0
  118. package/docs/2026-09-04-workflow-run-state-plan.md +363 -0
  119. package/docs/DEFERRED_TURNS.md +66 -294
  120. package/docs/HUMAN_DECISIONS.md +29 -30
  121. package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -11
  122. package/docs/SQLITE_STATE.md +25 -17
  123. package/docs/WORKFLOW_HOST.md +105 -31
  124. package/docs/WORKFLOW_STEP_MESSAGES.md +166 -75
  125. package/docs/live-replay-protocol.md +8 -4
  126. package/docs/tui-viewer.md +3 -1
  127. package/docs/workflows.md +44 -28
  128. package/herdr-plugin.toml +1 -1
  129. package/package.json +1 -1
  130. package/plugins/herdr/viewer.mjs +14 -5
  131. package/src/channels/adapter-entry.ts +220 -0
  132. package/src/channels/config.ts +296 -0
  133. package/src/channels/protocol.ts +333 -0
  134. package/src/channels/telegram.ts +335 -0
  135. package/src/client/client.ts +6 -2
  136. package/src/client/index.ts +2 -1
  137. package/src/client/protocol.ts +11 -5
  138. package/src/client/view.ts +51 -18
  139. package/src/controllers/index.ts +0 -1
  140. package/src/controllers/sqlite.ts +247 -845
  141. package/src/extension/index.ts +712 -647
  142. package/src/extension/recorder.ts +687 -0
  143. package/src/extension/remote-recorder-store.ts +126 -0
  144. package/src/extension/session-events.ts +119 -0
  145. package/src/extension/session-view.ts +86 -3
  146. package/src/extension/step-message.ts +19 -9
  147. package/src/extension/widget.ts +37 -17
  148. package/src/extension/workflow-message-coordinator.ts +398 -0
  149. package/src/host/channel-effects.ts +465 -0
  150. package/src/host/channel-supervisor.ts +73 -0
  151. package/src/host/runner.ts +2163 -525
  152. package/src/host/state.ts +333 -94
  153. package/src/host/view.ts +254 -122
  154. package/src/host/worker-entry.ts +38 -30
  155. package/src/host/worker-protocol.ts +11 -0
  156. package/src/state/prune.ts +3 -31
  157. package/src/state/schema.ts +76 -97
  158. package/src/state/workflow-messages.ts +599 -0
  159. package/src/viewer/render.ts +1 -5
  160. package/src/viewer/tui.ts +71 -6
  161. package/src/workflows/composition.ts +0 -5
  162. package/src/workflows/definition.ts +0 -8
  163. package/src/workflows/engine.ts +4 -6
  164. package/src/workflows/human-decision.ts +18 -2
  165. package/src/workflows/index.ts +0 -1
  166. package/src/workflows/schema.ts +0 -6
  167. package/src/workflows/settings.ts +1 -20
  168. package/src/workflows/store.ts +237 -489
  169. package/src/workflows/tool-input.ts +36 -1
  170. package/src/workflows/types.ts +6 -8
  171. package/src/workflows/workflow-message-content.ts +197 -0
  172. package/dist/extension/decision-channels.d.ts +0 -134
  173. package/dist/extension/decision-channels.js +0 -1307
  174. package/dist/extension/decision-channels.js.map +0 -1
  175. package/src/extension/decision-channels.ts +0 -1826
package/src/viewer/tui.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { WorkflowClient } from "../client/client.js";
2
2
  import { materializeRunView } from "../client/materialize.js";
3
3
  import { sanitizeText } from "../render/ansi.js";
4
+ import { formatDuration } from "../render/format.js";
4
5
  import type { JsonValue } from "../state/json.js";
5
6
 
6
7
  const ALT_SCREEN_ON = "\u001b[?1049h\u001b[?25l";
@@ -17,6 +18,7 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
17
18
  let value: JsonValue = options.runId === undefined ? [] : null;
18
19
  let selected = 0;
19
20
  let scroll = 0;
21
+ let selectedStepIndex: number | null = null;
20
22
  let mode: "runs" | "run" = options.runId === undefined ? "runs" : "run";
21
23
  let unsubscribe: (() => Promise<void>) | undefined;
22
24
  let runMaterializationGeneration = 0;
@@ -30,6 +32,8 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
30
32
  height,
31
33
  mode === "runs" ? selected : undefined,
32
34
  scroll,
35
+ new Date(),
36
+ selectedStepIndex,
33
37
  );
34
38
  process.stdout.write(`${CLEAR}${lines.join("\n")}`);
35
39
  };
@@ -39,6 +43,7 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
39
43
  await unsubscribe?.();
40
44
  mode = "runs";
41
45
  scroll = 0;
46
+ selectedStepIndex = null;
42
47
  unsubscribe = await options.client.watchRuns((event) => {
43
48
  value = event.payload;
44
49
  selected = Math.min(selected, Math.max(0, arrayLength(value) - 1));
@@ -51,6 +56,7 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
51
56
  await unsubscribe?.();
52
57
  mode = "run";
53
58
  scroll = 0;
59
+ selectedStepIndex = null;
54
60
  unsubscribe = await options.client.watchRun(runId, (event) => {
55
61
  const generation = ++runMaterializationGeneration;
56
62
  value = event.payload;
@@ -75,6 +81,8 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
75
81
  draw();
76
82
  if (rawModeSupported) process.stdin.setRawMode(true);
77
83
  process.stdin.resume();
84
+ const redrawTimer = setInterval(draw, 1_000);
85
+ redrawTimer.unref?.();
78
86
  await new Promise<void>((resolve) => {
79
87
  const onKey = (data: Buffer) => {
80
88
  const key = data.toString("utf8");
@@ -101,6 +109,29 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
101
109
  } else if (key === "\u001b[6~") {
102
110
  scroll += page;
103
111
  draw();
112
+ } else if (key === "[") {
113
+ const total = runStepCount(value);
114
+ if (total > 0) {
115
+ selectedStepIndex = Math.max(0, (selectedStepIndex ?? total - 1) - 1);
116
+ scroll = 0;
117
+ draw();
118
+ }
119
+ } else if (key === "]") {
120
+ const total = runStepCount(value);
121
+ if (total > 0) {
122
+ const next = (selectedStepIndex ?? total - 1) + 1;
123
+ selectedStepIndex = next >= total - 1 ? null : next;
124
+ scroll = 0;
125
+ draw();
126
+ }
127
+ } else if (key === "\u001b[F" || key === "G" || key === "L") {
128
+ selectedStepIndex = null;
129
+ scroll = 0;
130
+ draw();
131
+ } else if (key === "\u001b[H" || key === "g") {
132
+ selectedStepIndex = runStepCount(value) > 0 ? 0 : null;
133
+ scroll = 0;
134
+ draw();
104
135
  }
105
136
  } else if (mode === "runs") {
106
137
  if (key === "\u001b[A" || key === "k") {
@@ -117,6 +148,7 @@ export async function runViewer(options: ViewerOptions): Promise<void> {
117
148
  };
118
149
  process.stdin.on("data", onKey);
119
150
  });
151
+ clearInterval(redrawTimer);
120
152
  } finally {
121
153
  runMaterializationGeneration += 1;
122
154
  await unsubscribe?.();
@@ -132,6 +164,8 @@ export function renderClientView(
132
164
  height: number,
133
165
  selected: number | undefined,
134
166
  scroll: number,
167
+ now: Date = new Date(),
168
+ selectedStepIndex: number | null = null,
135
169
  ): string[] {
136
170
  if (Array.isArray(value)) {
137
171
  if (value.length === 0) return ["No workflow runs found."];
@@ -146,12 +180,12 @@ export function renderClientView(
146
180
  return truncate(`${prefix}${summary(item)}`, width);
147
181
  });
148
182
  }
149
- const lines = renderRun(value);
183
+ const lines = renderRun(value, now, selectedStepIndex);
150
184
  const start = Math.min(scroll, Math.max(0, lines.length - height));
151
185
  return lines.slice(start, start + height).map((line) => truncate(line, width));
152
186
  }
153
187
 
154
- function renderRun(value: JsonValue): string[] {
188
+ function renderRun(value: JsonValue, now: Date, selectedStepIndex: number | null): string[] {
155
189
  if (!isRecord(value) || value.schema !== "pi-workflows.run-view.v1") {
156
190
  return JSON.stringify(value, null, 2).split("\n");
157
191
  }
@@ -166,20 +200,46 @@ function renderRun(value: JsonValue): string[] {
166
200
  : "unknown";
167
201
  const runId = typeof value.runId === "string" ? sanitizeText(value.runId) : "unknown";
168
202
  const status = typeof display.status === "string" ? display.status : "unknown";
203
+ const steps = Array.isArray(state.steps) ? state.steps : [];
204
+ const selected =
205
+ steps.length === 0
206
+ ? -1
207
+ : Math.min(Math.max(0, selectedStepIndex ?? steps.length - 1), steps.length - 1);
208
+ const startedAt = typeof manifest.startedAt === "string" ? Date.parse(manifest.startedAt) : NaN;
209
+ const finishedAt =
210
+ typeof manifest.finishedAt === "string" ? Date.parse(manifest.finishedAt) : NaN;
211
+ const elapsed = Number.isFinite(startedAt)
212
+ ? ` · elapsed ${formatDuration(Math.max(0, (Number.isFinite(finishedAt) ? finishedAt : now.getTime()) - startedAt))}`
213
+ : "";
214
+ const position =
215
+ selectedStepIndex === null || selected < 0 ? "" : ` · step ${selected + 1}/${steps.length}`;
169
216
  const lines = [
170
217
  `workflow ${workflowName}`,
171
- `${statusGlyph(status)} ${sanitizeText(status)} · run ${runId}`,
218
+ `${statusGlyph(status)} ${sanitizeText(status)} · run ${runId}${elapsed}${position}`,
172
219
  ];
173
220
  if (typeof display.reason === "string") lines.push(`reason: ${sanitizeText(display.reason)}`);
174
221
 
175
- const steps = Array.isArray(state.steps) ? state.steps : [];
176
222
  if (steps.length > 0) {
177
223
  lines.push("", "steps");
178
- for (const step of steps) {
224
+ for (const [index, step] of steps.entries()) {
179
225
  if (!isRecord(step)) continue;
180
226
  const nodeId = typeof step.nodeId === "string" ? sanitizeText(step.nodeId) : "unknown";
181
227
  const outcome = typeof step.outcome === "string" ? step.outcome : "unknown";
182
- lines.push(` ${stepGlyph(outcome)} ${nodeId} · ${sanitizeText(outcome)}`);
228
+ lines.push(
229
+ `${index === selected ? ">" : " "} ${stepGlyph(outcome)} ${nodeId} · ${sanitizeText(outcome)}`,
230
+ );
231
+ }
232
+ const inspected = steps[selected];
233
+ if (isRecord(inspected)) {
234
+ const nodeId =
235
+ typeof inspected.nodeId === "string" ? sanitizeText(inspected.nodeId) : "unknown";
236
+ const output = Object.hasOwn(inspected, "error") ? inspected.error : inspected.output;
237
+ lines.push("", `step output — ${nodeId}`);
238
+ lines.push(
239
+ ...JSON.stringify(output ?? null, null, 2)
240
+ .split("\n")
241
+ .map((line) => ` ${sanitizeText(line)}`),
242
+ );
183
243
  }
184
244
  }
185
245
  if (status === "completed" && Object.hasOwn(state, "finalOutput")) {
@@ -218,6 +278,11 @@ function selectedRunId(value: JsonValue, index: number): string | undefined {
218
278
  return isRecord(item) && typeof item.runId === "string" ? item.runId : undefined;
219
279
  }
220
280
 
281
+ function runStepCount(value: JsonValue): number {
282
+ if (!isRecord(value) || !isRecord(value.state) || !Array.isArray(value.state.steps)) return 0;
283
+ return value.state.steps.length;
284
+ }
285
+
221
286
  function arrayLength(value: JsonValue): number {
222
287
  return Array.isArray(value) ? value.length : 0;
223
288
  }
@@ -589,15 +589,10 @@ function wrappedEffect(
589
589
  project: (context: WorkflowNodeContext) => WorkflowNodeContext,
590
590
  ): WorkflowManagedEffect | undefined {
591
591
  if (node.effect === undefined) return undefined;
592
- const idempotencyKey = node.effect.idempotencyKey;
593
592
  const request = node.effect.request;
594
593
  return {
595
594
  type: node.effect.type,
596
595
  recovery: node.effect.recovery,
597
- idempotencyKey:
598
- typeof idempotencyKey === "function"
599
- ? (context: WorkflowNodeContext) => idempotencyKey(project(context))
600
- : idempotencyKey,
601
596
  request:
602
597
  typeof request === "function"
603
598
  ? (context: WorkflowNodeContext) => request(project(context))
@@ -191,14 +191,6 @@ function workflowEffect(
191
191
  return {
192
192
  type: normalized,
193
193
  recovery,
194
- idempotencyKey: ({ state }: WorkflowNodeContext) => {
195
- const nodeId = state.currentNode;
196
- if (nodeId === undefined) {
197
- throw new Error("Managed effect resolution requires an active workflow node");
198
- }
199
- const nodeInvocation = state.steps.filter((step) => step.nodeId === nodeId).length + 1;
200
- return `${state.runId}:${normalized}:${nodeId}:${nodeInvocation}`;
201
- },
202
194
  request: ({ input, outputs }: WorkflowNodeContext) => ({ input, outputs }),
203
195
  };
204
196
  }
@@ -1563,17 +1563,15 @@ export class WorkflowEngine {
1563
1563
  );
1564
1564
  }
1565
1565
  const effectType = node.effect.type.trim();
1566
- const idempotencyKey =
1567
- typeof node.effect.idempotencyKey === "function"
1568
- ? await node.effect.idempotencyKey(context)
1569
- : node.effect.idempotencyKey;
1566
+ const invocation = context.state.steps.filter((step) => step.nodeId === nodeId).length + 1;
1567
+ const idempotencyKey = `${context.state.runId}:${effectType}:${nodeId}:${invocation}`;
1570
1568
  const request =
1571
1569
  typeof node.effect.request === "function"
1572
1570
  ? await node.effect.request(context)
1573
1571
  : node.effect.request;
1574
1572
  /* istanbul ignore if -- definition validation and effect helpers reject empty fields */
1575
- if (effectType.length === 0 || idempotencyKey.trim().length === 0) {
1576
- throw new Error("Managed effect type and idempotency key must be nonempty text");
1573
+ if (effectType.length === 0) {
1574
+ throw new Error("Managed effect type must be nonempty text");
1577
1575
  }
1578
1576
  canonicalJson(request);
1579
1577
  const reservation = await this.store.reserveEffect({
@@ -468,6 +468,14 @@ export class HumanDecisionStore {
468
468
  channel: string,
469
469
  value: HumanDecisionDeliveryRecord,
470
470
  ): Promise<"created" | "adopted"> {
471
+ return this.recordDeliverySync(request, channel, value);
472
+ }
473
+
474
+ recordDeliverySync(
475
+ request: HumanDecisionRequest | HumanDecisionChannelRequest,
476
+ channel: string,
477
+ value: HumanDecisionDeliveryRecord,
478
+ ): "created" | "adopted" {
471
479
  return this.recordChannelMessage(request.decisionId, channel, "delivery", value);
472
480
  }
473
481
 
@@ -570,6 +578,14 @@ export class HumanDecisionStore {
570
578
  channel: string,
571
579
  value: HumanDecisionSettlementRecord,
572
580
  ): Promise<"created" | "adopted"> {
581
+ return this.recordSettlementSync(decisionId, channel, value);
582
+ }
583
+
584
+ recordSettlementSync(
585
+ decisionId: string,
586
+ channel: string,
587
+ value: HumanDecisionSettlementRecord,
588
+ ): "created" | "adopted" {
573
589
  return this.recordChannelMessage(decisionId, channel, "settlement", value);
574
590
  }
575
591
 
@@ -1066,7 +1082,7 @@ export class HumanDecisionStore {
1066
1082
  purpose: "delivery" | "settlement",
1067
1083
  value: T,
1068
1084
  ): "created" | "adopted" {
1069
- const safeChannel = requireSimpleId(channel, "Human decision channel");
1085
+ const safeChannel = requireChannelId(channel, "Human decision channel");
1070
1086
  assertId(value.attemptId, `${purpose} attempt id`);
1071
1087
  return this.state.transaction(() => {
1072
1088
  if (this.readRequestRow(decisionId) === undefined) {
@@ -1120,7 +1136,7 @@ export class HumanDecisionStore {
1120
1136
  channel: string,
1121
1137
  purpose: "delivery" | "settlement",
1122
1138
  ): T[] {
1123
- const channelId = channelIdFor(requireSimpleId(channel, "Human decision channel"));
1139
+ const channelId = channelIdFor(requireChannelId(channel, "Human decision channel"));
1124
1140
  const rows = this.state.connection
1125
1141
  .prepare(
1126
1142
  `SELECT content_hash AS contentHash
@@ -92,7 +92,6 @@ export {
92
92
  type WorkflowFollowUpQueueRecord,
93
93
  type WorkflowFollowUpRecord,
94
94
  type WorkflowFollowUpState,
95
- type WorkflowPresentationState,
96
95
  type WorkflowQueueFollowUpRequest,
97
96
  type WorkflowRemoveFollowUpRequest,
98
97
  type WorkflowSettingsChangeContext,
@@ -113,12 +113,6 @@ export function assertValidActionNode(node: ActionNodeDefinition, nodeId = "acti
113
113
  if (node.effect.recovery !== "idempotent" && node.effect.recovery !== "manual") {
114
114
  fail(`node ${nodeId} effect recovery must be idempotent or manual`);
115
115
  }
116
- if (
117
- typeof node.effect.idempotencyKey !== "string" &&
118
- typeof node.effect.idempotencyKey !== "function"
119
- ) {
120
- fail(`node ${nodeId} effect idempotencyKey must be text or a function`);
121
- }
122
116
  if (!("request" in node.effect)) {
123
117
  fail(`node ${nodeId} effect requires request data or a request function`);
124
118
  }
@@ -101,20 +101,7 @@ export type WorkflowSettingsChangeResult = {
101
101
  adopted: boolean;
102
102
  };
103
103
 
104
- export type WorkflowFollowUpState =
105
- | "queued"
106
- | "pending_presentation"
107
- | "ready"
108
- | "sent"
109
- | "removed"
110
- | "cancelled";
111
-
112
- export type WorkflowPresentationState =
113
- | "none"
114
- | "not-needed"
115
- | "pending"
116
- | "settled"
117
- | "unavailable";
104
+ export type WorkflowFollowUpState = "queued" | "removed" | "cancelled";
118
105
 
119
106
  export type WorkflowFollowUpRecord = {
120
107
  followUpId: string;
@@ -126,20 +113,14 @@ export type WorkflowFollowUpRecord = {
126
113
  source: string;
127
114
  prompt: string;
128
115
  state: WorkflowFollowUpState;
129
- sessionEntryId?: string;
130
116
  reason?: string;
131
117
  createdAt: string;
132
118
  updatedAt: string;
133
- sentAt?: string;
134
119
  };
135
120
 
136
121
  export type WorkflowFollowUpQueueRecord = {
137
122
  runId: string;
138
123
  originSessionId?: string;
139
- presentationState: WorkflowPresentationState;
140
- presentationEntryId?: string;
141
- presentationAssistantEntryId?: string;
142
- presentationReason?: string;
143
124
  followUps: WorkflowFollowUpRecord[];
144
125
  };
145
126