@osolmaz/pi-workflows 0.16.0 → 0.16.1

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 (154) 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 +42 -16
  22. package/dist/client/view.js.map +1 -1
  23. package/dist/controllers/index.d.ts +1 -1
  24. package/dist/controllers/index.js.map +1 -1
  25. package/dist/controllers/sqlite.d.ts +12 -165
  26. package/dist/controllers/sqlite.js +124 -435
  27. package/dist/controllers/sqlite.js.map +1 -1
  28. package/dist/extension/index.d.ts +23 -2
  29. package/dist/extension/index.js +619 -535
  30. package/dist/extension/index.js.map +1 -1
  31. package/dist/extension/recorder.d.ts +95 -0
  32. package/dist/extension/recorder.js +530 -0
  33. package/dist/extension/recorder.js.map +1 -0
  34. package/dist/extension/remote-recorder-store.d.ts +25 -0
  35. package/dist/extension/remote-recorder-store.js +81 -0
  36. package/dist/extension/remote-recorder-store.js.map +1 -0
  37. package/dist/extension/session-events.d.ts +134 -0
  38. package/dist/extension/session-events.js +60 -0
  39. package/dist/extension/session-events.js.map +1 -0
  40. package/dist/extension/session-view.d.ts +2 -0
  41. package/dist/extension/session-view.js +60 -2
  42. package/dist/extension/session-view.js.map +1 -1
  43. package/dist/extension/step-message.d.ts +6 -4
  44. package/dist/extension/step-message.js +12 -6
  45. package/dist/extension/step-message.js.map +1 -1
  46. package/dist/extension/widget.d.ts +2 -2
  47. package/dist/extension/widget.js +28 -18
  48. package/dist/extension/widget.js.map +1 -1
  49. package/dist/extension/workflow-message-coordinator.d.ts +27 -0
  50. package/dist/extension/workflow-message-coordinator.js +294 -0
  51. package/dist/extension/workflow-message-coordinator.js.map +1 -0
  52. package/dist/host/channel-effects.d.ts +72 -0
  53. package/dist/host/channel-effects.js +271 -0
  54. package/dist/host/channel-effects.js.map +1 -0
  55. package/dist/host/channel-supervisor.d.ts +21 -0
  56. package/dist/host/channel-supervisor.js +54 -0
  57. package/dist/host/channel-supervisor.js.map +1 -0
  58. package/dist/host/runner.d.ts +43 -8
  59. package/dist/host/runner.js +1567 -331
  60. package/dist/host/runner.js.map +1 -1
  61. package/dist/host/state.d.ts +19 -16
  62. package/dist/host/state.js +251 -63
  63. package/dist/host/state.js.map +1 -1
  64. package/dist/host/view.d.ts +19 -9
  65. package/dist/host/view.js +222 -118
  66. package/dist/host/view.js.map +1 -1
  67. package/dist/state/prune.js +3 -25
  68. package/dist/state/prune.js.map +1 -1
  69. package/dist/state/schema.js +76 -97
  70. package/dist/state/schema.js.map +1 -1
  71. package/dist/state/workflow-messages.d.ts +96 -0
  72. package/dist/state/workflow-messages.js +335 -0
  73. package/dist/state/workflow-messages.js.map +1 -0
  74. package/dist/viewer/render.js +1 -1
  75. package/dist/viewer/render.js.map +1 -1
  76. package/dist/viewer/tui.d.ts +1 -1
  77. package/dist/viewer/tui.js +65 -8
  78. package/dist/viewer/tui.js.map +1 -1
  79. package/dist/workflows/human-decision.d.ts +2 -0
  80. package/dist/workflows/human-decision.js +8 -2
  81. package/dist/workflows/human-decision.js.map +1 -1
  82. package/dist/workflows/index.d.ts +1 -1
  83. package/dist/workflows/index.js.map +1 -1
  84. package/dist/workflows/settings.d.ts +1 -8
  85. package/dist/workflows/settings.js.map +1 -1
  86. package/dist/workflows/store.d.ts +5 -19
  87. package/dist/workflows/store.js +121 -354
  88. package/dist/workflows/store.js.map +1 -1
  89. package/dist/workflows/tool-input.d.ts +21 -0
  90. package/dist/workflows/tool-input.js +23 -1
  91. package/dist/workflows/tool-input.js.map +1 -1
  92. package/dist/workflows/types.d.ts +5 -5
  93. package/dist/workflows/workflow-message-content.d.ts +38 -0
  94. package/dist/workflows/workflow-message-content.js +157 -0
  95. package/dist/workflows/workflow-message-content.js.map +1 -0
  96. package/docs/2026-08-18-herdr-piw-plan.md +2 -1
  97. package/docs/2026-08-20-durable-workflow-launch-plan.md +1 -1
  98. package/docs/2026-08-25-workflow-follow-ups.md +26 -50
  99. package/docs/2026-08-30-out-of-process-workflow-host-plan.md +3 -3
  100. package/docs/2026-09-01-restore-session-delivery-controls-plan.md +4 -0
  101. package/docs/2026-09-01-unified-workflow-client-plan.md +5 -1
  102. package/docs/2026-09-02-installed-live-e2e-plan.md +15 -1
  103. package/docs/2026-09-02-unify-workflow-messages-plan.md +562 -0
  104. package/docs/DEFERRED_TURNS.md +66 -294
  105. package/docs/HUMAN_DECISIONS.md +29 -30
  106. package/docs/HUMAN_DECISION_PRESENTATIONS.md +4 -11
  107. package/docs/SQLITE_STATE.md +19 -15
  108. package/docs/WORKFLOW_HOST.md +93 -28
  109. package/docs/WORKFLOW_STEP_MESSAGES.md +166 -75
  110. package/docs/live-replay-protocol.md +8 -4
  111. package/docs/tui-viewer.md +3 -1
  112. package/docs/workflows.md +34 -27
  113. package/herdr-plugin.toml +1 -1
  114. package/package.json +1 -1
  115. package/plugins/herdr/viewer.mjs +14 -5
  116. package/src/channels/adapter-entry.ts +220 -0
  117. package/src/channels/config.ts +296 -0
  118. package/src/channels/protocol.ts +333 -0
  119. package/src/channels/telegram.ts +335 -0
  120. package/src/client/client.ts +6 -2
  121. package/src/client/index.ts +2 -1
  122. package/src/client/protocol.ts +11 -5
  123. package/src/client/view.ts +43 -18
  124. package/src/controllers/index.ts +0 -1
  125. package/src/controllers/sqlite.ts +164 -844
  126. package/src/extension/index.ts +693 -646
  127. package/src/extension/recorder.ts +687 -0
  128. package/src/extension/remote-recorder-store.ts +126 -0
  129. package/src/extension/session-events.ts +119 -0
  130. package/src/extension/session-view.ts +86 -3
  131. package/src/extension/step-message.ts +19 -9
  132. package/src/extension/widget.ts +37 -17
  133. package/src/extension/workflow-message-coordinator.ts +334 -0
  134. package/src/host/channel-effects.ts +465 -0
  135. package/src/host/channel-supervisor.ts +73 -0
  136. package/src/host/runner.ts +2120 -531
  137. package/src/host/state.ts +333 -94
  138. package/src/host/view.ts +249 -121
  139. package/src/state/prune.ts +3 -31
  140. package/src/state/schema.ts +76 -97
  141. package/src/state/workflow-messages.ts +548 -0
  142. package/src/viewer/render.ts +1 -5
  143. package/src/viewer/tui.ts +71 -6
  144. package/src/workflows/human-decision.ts +18 -2
  145. package/src/workflows/index.ts +0 -1
  146. package/src/workflows/settings.ts +1 -20
  147. package/src/workflows/store.ts +173 -489
  148. package/src/workflows/tool-input.ts +36 -1
  149. package/src/workflows/types.ts +5 -5
  150. package/src/workflows/workflow-message-content.ts +197 -0
  151. package/dist/extension/decision-channels.d.ts +0 -134
  152. package/dist/extension/decision-channels.js +0 -1307
  153. package/dist/extension/decision-channels.js.map +0 -1
  154. package/src/extension/decision-channels.ts +0 -1826
@@ -3,6 +3,7 @@ import { StateDatabase, workflowStatePath } from "../state/database.js";
3
3
  import { canonicalJson, parseJson } from "../state/json.js";
4
4
  import { StateMutationStore, StaleResourceError, resourceIdFor, tokenHash, } from "../state/mutation.js";
5
5
  import { initializeViewerRun, recordViewerDeltas, viewerTailPatch, VIEWER_PAGE_SIZE, } from "../state/viewer.js";
6
+ import { WorkflowMessageStore, workflowMessageIdFor } from "../state/workflow-messages.js";
6
7
  import { compositionMetadata } from "./composition.js";
7
8
  import { ClaimLostError } from "./errors.js";
8
9
  import { HumanDecisionStore } from "./human-decision.js";
@@ -10,6 +11,7 @@ import { applyJsonPatch, validateJsonPatch } from "./json-patch.js";
10
11
  import { reduceSessionEvents, reduceSessionEventsFromCheckpoint, } from "./session-reducer.js";
11
12
  import { applyWorkflowSettingsPatch, workflowSettingsScopeId, } from "./settings.js";
12
13
  import { MAX_CURRENT_UPDATES, createUpdateId, updateProjection } from "./updates.js";
14
+ import { followUpWorkflowMessageContent } from "./workflow-message-content.js";
13
15
  export const RUN_STATE_SCHEMA = "pi-workflows.run-state.v1";
14
16
  export const DEFINITION_SNAPSHOT_SCHEMA = "pi-workflows.definition-snapshot.v1";
15
17
  export const SESSION_BINDING_SCHEMA = "pi-workflows.session-binding.v1";
@@ -47,14 +49,11 @@ const STEP_ROW_SELECT = `
47
49
  ON last_link.attempt_id = a.attempt_id AND last_link.role = 'last'`;
48
50
  const FOLLOW_UP_ROW_SELECT = `
49
51
  SELECT f.follow_up_id AS followUpId, f.resource_id AS resourceId,
50
- f.queue_resource_id AS queueResourceId, f.run_id AS runId,
51
- f.request_id AS requestId, f.order_number AS orderNumber,
52
+ f.run_id AS runId, f.request_id AS requestId, f.order_number AS orderNumber,
52
53
  f.target_session_id AS targetSessionId, f.actor_type AS actorType,
53
54
  f.actor_id AS actorId, f.source_type AS sourceType,
54
- f.prompt_hash AS promptHash, f.status,
55
- f.session_entry_id AS sessionEntryId, f.reason_hash AS reasonHash,
56
- f.created_at AS createdAt, f.updated_at AS updatedAt, f.sent_at AS sentAt,
57
- r.revision
55
+ f.prompt_hash AS promptHash, f.status, f.reason_hash AS reasonHash,
56
+ f.created_at AS createdAt, f.updated_at AS updatedAt, r.revision
58
57
  FROM workflow_follow_ups f
59
58
  JOIN resources r ON r.resource_id = f.resource_id`;
60
59
  export function workflowStateDatabasePath(homeDir) {
@@ -69,12 +68,15 @@ export class WorkflowRunStore {
69
68
  state;
70
69
  authorityProvider;
71
70
  snapshotLifecycle;
71
+ allowHostSessionRecording;
72
72
  contexts = new Map();
73
73
  ownsState;
74
74
  mutations;
75
+ workflowMessages;
75
76
  constructor(databasePath = workflowStatePath(), options = {}) {
76
77
  this.authorityProvider = options.authorityProvider;
77
78
  this.snapshotLifecycle = options.snapshotLifecycle;
79
+ this.allowHostSessionRecording = options.allowHostSessionRecording === true;
78
80
  this.ownsState = options.state === undefined;
79
81
  this.state =
80
82
  options.state ??
@@ -85,6 +87,7 @@ export class WorkflowRunStore {
85
87
  });
86
88
  this.databasePath = this.state.filePath;
87
89
  this.mutations = new StateMutationStore(this.state);
90
+ this.workflowMessages = new WorkflowMessageStore(this.state);
88
91
  }
89
92
  close() {
90
93
  if (this.ownsState)
@@ -333,6 +336,15 @@ export class WorkflowRunStore {
333
336
  return;
334
337
  }
335
338
  const definitionHash = this.state.putJson(snapshot, now);
339
+ const parentLineage = state.parentRunId === undefined
340
+ ? undefined
341
+ : this.state.connection
342
+ .prepare(`SELECT root_run_id AS rootRunId, restart_number AS restartNumber
343
+ FROM runs WHERE run_id = ?`)
344
+ .get(state.parentRunId);
345
+ if (state.parentRunId !== undefined && !isRunLineageRow(parentLineage)) {
346
+ throw new Error(`Workflow parent run is missing: ${state.parentRunId}`);
347
+ }
336
348
  this.state.connection
337
349
  .prepare(`INSERT INTO workflow_definitions(
338
350
  definition_digest, workflow_name, definition_hash, created_at
@@ -355,12 +367,13 @@ export class WorkflowRunStore {
355
367
  const errorHash = state.error === undefined ? null : this.state.putText(state.error, now);
356
368
  this.state.connection
357
369
  .prepare(`INSERT INTO runs(
358
- run_id, resource_id, project_id, parent_run_id, definition_digest,
370
+ run_id, resource_id, project_id, parent_run_id, root_run_id, lineage_kind,
371
+ restart_number, parent_terminal_fingerprint, definition_digest,
359
372
  workflow_ref, launch_options_hash, title, status, paused,
360
373
  status_detail, input_hash, final_output_hash, error_hash,
361
374
  created_at, updated_at, finished_at
362
- ) VALUES (?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
363
- .run(state.runId, resourceId, state.parentRunId ?? null, definitionDigest, state.workflowName, launchOptionsHash, state.runTitle ?? null, state.status, state.paused === true ? 1 : 0, state.statusDetail ?? null, inputHash, finalOutputHash, errorHash, Date.parse(state.startedAt), now, state.finishedAt === undefined ? null : Date.parse(state.finishedAt));
375
+ ) VALUES (?, ?, NULL, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
376
+ .run(state.runId, resourceId, state.parentRunId ?? null, isRunLineageRow(parentLineage) ? parentLineage.rootRunId : state.runId, state.parentRunId === undefined ? null : "continuation", isRunLineageRow(parentLineage) ? parentLineage.restartNumber : 0, definitionDigest, state.workflowName, launchOptionsHash, state.runTitle ?? null, state.status, state.paused === true ? 1 : 0, state.statusDetail ?? null, inputHash, finalOutputHash, errorHash, Date.parse(state.startedAt), now, state.finishedAt === undefined ? null : Date.parse(state.finishedAt));
364
377
  initializeViewerRun(this.state, state.runId, now);
365
378
  this.insertRunSources(state.runId, source, state.workflowSources ?? []);
366
379
  this.syncContinuationIdentity(state);
@@ -585,59 +598,49 @@ export class WorkflowRunStore {
585
598
  if (Buffer.byteLength(request.prompt, "utf8") > 64 * 1024) {
586
599
  throw new Error("Workflow follow-up prompt cannot exceed 65536 bytes");
587
600
  }
588
- for (let attempt = 0; attempt < 8; attempt += 1) {
601
+ return this.state.transaction(() => {
589
602
  const prior = this.followUpRowByRequest(request.runId, request.requestId);
590
603
  if (prior !== undefined) {
591
604
  this.assertSameFollowUp(prior, request);
592
605
  return { followUp: this.followUpRecord(prior), adopted: true };
593
606
  }
594
607
  this.requireRunAcceptsSettings(request.runId, true);
595
- const queue = this.requireFollowUpQueueRow(request.runId);
596
- try {
597
- const result = this.mutations.mutate({
598
- resourceId: queue.resourceId,
599
- operation: "follow-up.queue",
600
- actor: request.actor,
601
- expectedRevision: queue.revision,
602
- }, "follow-up.queued", ({ database, now }) => {
603
- this.requireRunAcceptsSettings(request.runId, true);
604
- const currentQueue = this.requireFollowUpQueueRow(request.runId);
605
- if (currentQueue.originSessionId !== null &&
606
- currentQueue.originSessionId !== request.targetSessionId) {
607
- throw new Error("Workflow follow-ups must target the run's origin Pi session");
608
- }
609
- if (currentQueue.originSessionId === null) {
610
- database.connection
611
- .prepare(`UPDATE workflow_follow_up_queues
612
- SET origin_session_id = ?, updated_at = ? WHERE run_id = ?`)
613
- .run(request.targetSessionId, now, request.runId);
614
- }
615
- const order = this.nextFollowUpOrder(request.runId);
616
- const followUpId = followUpIdFor(request.runId, request.requestId);
617
- const resourceId = this.mutations.ensureResource("follow_up", followUpId, now);
618
- const promptHash = database.putText(request.prompt, now);
619
- database.connection
620
- .prepare(`INSERT INTO workflow_follow_ups(
621
- follow_up_id, resource_id, queue_resource_id, run_id,
622
- request_id, order_number, target_session_id,
623
- actor_type, actor_id, source_type, prompt_hash, status,
624
- created_at, updated_at
625
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'queued', ?, ?)`)
626
- .run(followUpId, resourceId, queue.resourceId, request.runId, request.requestId, order, request.targetSessionId, request.actor.type, request.actor.id ?? null, request.source, promptHash, now, now);
627
- recordViewerDeltas(this.state, request.runId, this.viewerInspectorTargets(request.runId), now);
628
- return followUpId;
629
- }, { payload: { runId: request.runId, requestId: request.requestId } });
630
- return {
631
- followUp: this.followUpRecord(this.requireFollowUpRow(result.value)),
632
- adopted: false,
633
- };
634
- }
635
- catch (error) {
636
- if (!(error instanceof StaleResourceError))
637
- throw error;
608
+ if (this.originSessionId(request.runId) !== request.targetSessionId) {
609
+ throw new Error("Workflow follow-ups must target the run's origin Pi session");
638
610
  }
639
- }
640
- throw new StaleResourceError("Workflow follow-up queue kept changing; retry the request");
611
+ const now = Date.now();
612
+ const order = this.nextFollowUpOrder(request.runId);
613
+ const followUpId = followUpIdFor(request.runId, request.requestId);
614
+ const resourceId = this.mutations.ensureResource("follow_up", followUpId, now);
615
+ const promptHash = this.state.putText(request.prompt, now);
616
+ this.state.connection
617
+ .prepare(`INSERT INTO workflow_follow_ups(
618
+ follow_up_id, resource_id, run_id, request_id, order_number,
619
+ target_session_id, actor_type, actor_id, source_type, prompt_hash,
620
+ status, created_at, updated_at
621
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'queued', ?, ?)`)
622
+ .run(followUpId, resourceId, request.runId, request.requestId, order, request.targetSessionId, request.actor.type, request.actor.id ?? null, request.source, promptHash, now, now);
623
+ const workflowMessageId = workflowMessageIdFor("followUp", followUpId, "initial");
624
+ this.workflowMessages.create({
625
+ workflowMessageId,
626
+ runId: request.runId,
627
+ targetSessionId: request.targetSessionId,
628
+ kind: "followUp",
629
+ sourceId: followUpId,
630
+ idempotencyKey: "initial",
631
+ content: followUpWorkflowMessageContent({
632
+ workflowMessageId,
633
+ followUpId,
634
+ runId: request.runId,
635
+ prompt: request.prompt,
636
+ }),
637
+ now,
638
+ });
639
+ this.bumpResource(resourceId, 0, now);
640
+ insertGenericEvent(this.state, resourceId, 1, "follow-up.queued", request.actor.type, request.actor.id ?? null, this.state.putJson({ runId: request.runId, requestId: request.requestId }, now), now);
641
+ recordViewerDeltas(this.state, request.runId, this.viewerInspectorTargets(request.runId), now);
642
+ return { followUp: this.followUpRecord(this.requireFollowUpRow(followUpId)), adopted: false };
643
+ });
641
644
  }
642
645
  removeFollowUp(request) {
643
646
  assertSourceType(request.source);
@@ -647,12 +650,13 @@ export class WorkflowRunStore {
647
650
  }
648
651
  if (row.status === "removed")
649
652
  return this.followUpRecord(row);
650
- if (row.status === "sent" || row.status === "cancelled") {
651
- throw new Error(`Workflow follow-up cannot be removed after it is ${row.status}`);
653
+ if (row.status === "cancelled") {
654
+ throw new Error("A cancelled workflow follow-up cannot be removed");
652
655
  }
653
656
  this.assertFollowUpRemovalAuthority(row, request.actor, request.source);
654
- if (this.hasLiveLease(row.resourceId)) {
655
- throw new Error("Workflow follow-up is being delivered and cannot be removed");
657
+ const message = this.workflowMessages.latestForSource("followUp", request.followUpId);
658
+ if (message?.status === "sent") {
659
+ throw new Error("A sent workflow follow-up cannot be removed");
656
660
  }
657
661
  const reason = "Removed before delivery";
658
662
  this.mutations.mutate({
@@ -665,21 +669,20 @@ export class WorkflowRunStore {
665
669
  const update = database.connection
666
670
  .prepare(`UPDATE workflow_follow_ups
667
671
  SET status = 'removed', reason_hash = ?, updated_at = ?
668
- WHERE follow_up_id = ? AND status IN ('queued', 'pending_presentation', 'ready')`)
672
+ WHERE follow_up_id = ? AND status = 'queued'`)
669
673
  .run(reasonHash, now, request.followUpId);
670
674
  if (update.changes !== 1) {
671
675
  throw new StaleResourceError("Workflow follow-up changed before removal");
672
676
  }
677
+ this.workflowMessages.cancelPendingForSource(request.followUpId, "followUp", now);
673
678
  recordViewerDeltas(this.state, request.runId, this.viewerInspectorTargets(request.runId), now);
674
679
  }, { payload: { followUpId: request.followUpId, source: request.source } });
675
680
  return this.followUpRecord(this.requireFollowUpRow(request.followUpId));
676
681
  }
677
682
  originSessionId(runId) {
678
683
  const row = this.state.connection
679
- .prepare(`SELECT COALESCE(q.origin_session_id, b.origin_session_id) AS originSessionId
680
- FROM runs r
681
- LEFT JOIN workflow_follow_up_queues q ON q.run_id = r.run_id
682
- LEFT JOIN run_bindings b ON b.run_id = r.run_id
684
+ .prepare(`SELECT b.origin_session_id AS originSessionId
685
+ FROM runs r LEFT JOIN run_bindings b ON b.run_id = r.run_id
683
686
  WHERE r.run_id = ?`)
684
687
  .get(runId);
685
688
  return isRecord(row) && typeof row.originSessionId === "string"
@@ -687,176 +690,26 @@ export class WorkflowRunStore {
687
690
  : undefined;
688
691
  }
689
692
  readFollowUpQueue(runId) {
690
- const queue = this.followUpQueueRow(runId);
691
- if (queue === undefined)
693
+ const originSessionId = this.originSessionId(runId);
694
+ if (originSessionId === undefined)
692
695
  return undefined;
693
696
  const followUps = this.state.connection
694
697
  .prepare(`${FOLLOW_UP_ROW_SELECT} WHERE f.run_id = ? ORDER BY f.order_number`)
695
698
  .all(runId)
696
699
  .filter(isFollowUpRow)
697
700
  .map((row) => this.followUpRecord(row));
698
- return {
699
- runId,
700
- ...(queue.originSessionId !== null ? { originSessionId: queue.originSessionId } : {}),
701
- presentationState: assertPresentationState(queue.presentationState),
702
- ...(queue.presentationEntryId !== null
703
- ? { presentationEntryId: queue.presentationEntryId }
704
- : {}),
705
- ...(queue.presentationAssistantEntryId !== null
706
- ? { presentationAssistantEntryId: queue.presentationAssistantEntryId }
707
- : {}),
708
- ...(queue.presentationReasonHash !== null
709
- ? { presentationReason: this.readText(queue.presentationReasonHash) }
710
- : {}),
711
- followUps,
712
- };
701
+ return { runId, originSessionId, followUps };
713
702
  }
714
703
  readFollowUpQueueRange(runId, range) {
715
- const queue = this.followUpQueueRow(runId);
716
- if (queue === undefined)
704
+ const originSessionId = this.originSessionId(runId);
705
+ if (originSessionId === undefined)
717
706
  return null;
718
707
  const followUps = this.state.connection
719
708
  .prepare(`${FOLLOW_UP_ROW_SELECT} WHERE f.run_id = ? ORDER BY f.order_number LIMIT ? OFFSET ?`)
720
709
  .all(runId, range.limit, range.start)
721
710
  .filter(isFollowUpRow)
722
711
  .map((row) => this.followUpRecord(row));
723
- return {
724
- runId,
725
- ...(queue.originSessionId !== null ? { originSessionId: queue.originSessionId } : {}),
726
- presentationState: assertPresentationState(queue.presentationState),
727
- ...(queue.presentationEntryId !== null
728
- ? { presentationEntryId: queue.presentationEntryId }
729
- : {}),
730
- ...(queue.presentationAssistantEntryId !== null
731
- ? { presentationAssistantEntryId: queue.presentationAssistantEntryId }
732
- : {}),
733
- ...(queue.presentationReasonHash !== null
734
- ? { presentationReason: this.readText(queue.presentationReasonHash) }
735
- : {}),
736
- followUps,
737
- };
738
- }
739
- listPendingPresentations(targetSessionId) {
740
- return this.state.connection
741
- .prepare(`SELECT run_id AS runId FROM workflow_follow_up_queues
742
- WHERE origin_session_id = ? AND presentation_state = 'pending'
743
- ORDER BY created_at`)
744
- .all(targetSessionId)
745
- .flatMap((row) => (isRecord(row) && typeof row.runId === "string" ? [row.runId] : []));
746
- }
747
- settleFollowUpPresentation(options) {
748
- const queue = this.requireFollowUpQueueRow(options.runId);
749
- if (queue.presentationState === options.state) {
750
- return this.readFollowUpQueue(options.runId);
751
- }
752
- if (queue.presentationState !== "pending") {
753
- throw new Error(`Workflow presentation is ${queue.presentationState}, not pending for ${options.runId}`);
754
- }
755
- if (options.state === "settled" &&
756
- (options.presentationEntryId === undefined || options.assistantEntryId === undefined)) {
757
- throw new Error("Settled workflow presentation requires both session entry IDs");
758
- }
759
- if (options.state === "unavailable" && !options.reason?.trim()) {
760
- throw new Error("Unavailable workflow presentation requires a reason");
761
- }
762
- try {
763
- this.mutations.mutate({
764
- resourceId: queue.resourceId,
765
- operation: "follow-up.presentation",
766
- actor: {
767
- type: "session",
768
- ...(queue.originSessionId !== null ? { id: queue.originSessionId } : {}),
769
- },
770
- expectedRevision: queue.revision,
771
- }, `follow-up.presentation-${options.state}`, ({ database, now }) => {
772
- const reasonHash = options.state === "unavailable"
773
- ? database.putText(options.reason, now)
774
- : null;
775
- database.connection
776
- .prepare(`UPDATE workflow_follow_up_queues
777
- SET presentation_state = ?, presentation_entry_id = ?,
778
- presentation_assistant_entry_id = ?, presentation_reason_hash = ?,
779
- presentation_updated_at = ?, updated_at = ?
780
- WHERE run_id = ? AND presentation_state = 'pending'`)
781
- .run(options.state, options.presentationEntryId ?? null, options.assistantEntryId ?? null, reasonHash, now, now, options.runId);
782
- database.connection
783
- .prepare(`UPDATE workflow_follow_ups SET status = 'ready', updated_at = ?
784
- WHERE run_id = ? AND status = 'pending_presentation'`)
785
- .run(now, options.runId);
786
- recordViewerDeltas(this.state, options.runId, this.viewerInspectorTargets(options.runId), now);
787
- }, { payload: { runId: options.runId, state: options.state } });
788
- }
789
- catch (error) {
790
- if (error instanceof StaleResourceError) {
791
- const current = this.requireFollowUpQueueRow(options.runId);
792
- if (current.presentationState === options.state) {
793
- return this.readFollowUpQueue(options.runId);
794
- }
795
- }
796
- throw error;
797
- }
798
- return this.readFollowUpQueue(options.runId);
799
- }
800
- claimNextFollowUp(targetSessionId, ownerId, leaseMs = 30_000) {
801
- const rows = this.state.connection
802
- .prepare(`${FOLLOW_UP_ROW_SELECT}
803
- WHERE f.target_session_id = ? AND f.status = 'ready'
804
- ORDER BY f.created_at, f.order_number LIMIT 16`)
805
- .all(targetSessionId)
806
- .filter(isFollowUpRow);
807
- for (const row of rows) {
808
- let claim;
809
- try {
810
- claim = this.mutations.claim({
811
- resourceId: row.resourceId,
812
- ownerType: "session",
813
- ownerId,
814
- expectedRevision: row.revision,
815
- leaseMs,
816
- });
817
- }
818
- catch (error) {
819
- if (error instanceof StaleResourceError)
820
- continue;
821
- throw error;
822
- }
823
- if (claim === undefined)
824
- continue;
825
- const current = this.requireFollowUpRow(row.followUpId);
826
- if (current.status !== "ready") {
827
- this.mutations.release(claim, claim.resourceRevision);
828
- continue;
829
- }
830
- return { followUp: this.followUpRecord(current), claim };
831
- }
832
- return undefined;
833
- }
834
- markFollowUpSent(followUpId, claim, sessionEntryId) {
835
- const row = this.requireFollowUpRow(followUpId);
836
- if (row.status === "sent")
837
- return this.followUpRecord(row);
838
- this.mutations.mutate({
839
- resourceId: row.resourceId,
840
- operation: "follow-up.sent",
841
- actor: { type: "session", id: claim.ownerId },
842
- expectedRevision: claim.resourceRevision,
843
- lease: claim,
844
- }, "follow-up.sent", ({ database, now }) => {
845
- const update = database.connection
846
- .prepare(`UPDATE workflow_follow_ups
847
- SET status = 'sent', session_entry_id = ?, sent_at = ?, updated_at = ?
848
- WHERE follow_up_id = ? AND status = 'ready'`)
849
- .run(sessionEntryId, now, now, followUpId);
850
- if (update.changes !== 1) {
851
- throw new StaleResourceError("Workflow follow-up changed before delivery completed");
852
- }
853
- recordViewerDeltas(this.state, row.runId, this.viewerInspectorTargets(row.runId), now);
854
- }, { payload: { followUpId, sessionEntryId } });
855
- return this.followUpRecord(this.requireFollowUpRow(followUpId));
856
- }
857
- releaseFollowUpClaim(claim) {
858
- const revision = this.requireResourceRevision(claim.resourceId);
859
- this.mutations.release(claim, revision);
712
+ return { runId, originSessionId, followUps };
860
713
  }
861
714
  async prepareRunResume(runId) {
862
715
  const loaded = this.readRun(runId, { includeTrace: true });
@@ -868,7 +721,6 @@ export class WorkflowRunStore {
868
721
  this.verifySettingsProjections(runId);
869
722
  const revision = this.resourceRevision(runId);
870
723
  this.contexts.set(runId, { revision, lock: Promise.resolve() });
871
- this.finalizeRecordingCaptures(runId, "Workflow host stopped before the run finished");
872
724
  this.state.transaction(() => {
873
725
  const row = this.requireRunRow(runId);
874
726
  const context = this.contextFor(runId);
@@ -1051,7 +903,7 @@ export class WorkflowRunStore {
1051
903
  this.state.transaction(() => {
1052
904
  const run = this.requireRunRow(runId);
1053
905
  const context = this.contextFor(runId);
1054
- this.assertWriteAuthority(run, context.revision);
906
+ this.assertSessionWriteAuthority(runId);
1055
907
  const now = Date.parse(binding.boundAt);
1056
908
  const resourceId = resourceIdFor("session", segmentId);
1057
909
  const bindingHash = this.state.putJson(binding, now);
@@ -1075,11 +927,6 @@ export class WorkflowRunStore {
1075
927
  VALUES (?, ?, 'interactive', ?)
1076
928
  ON CONFLICT(run_id) DO NOTHING`)
1077
929
  .run(runId, binding.piSessionId, now);
1078
- this.state.connection
1079
- .prepare(`UPDATE workflow_follow_up_queues
1080
- SET origin_session_id = COALESCE(origin_session_id, ?), updated_at = ?
1081
- WHERE run_id = ?`)
1082
- .run(binding.piSessionId, now, runId);
1083
930
  const revision = context.revision + 1;
1084
931
  const at = new Date(now).toISOString();
1085
932
  const traceEvent = {
@@ -1659,6 +1506,8 @@ export class WorkflowRunStore {
1659
1506
  }
1660
1507
  assertSessionWriteAuthority(runId) {
1661
1508
  const run = this.requireRunRow(runId);
1509
+ if (this.allowHostSessionRecording)
1510
+ return;
1662
1511
  this.assertWriteAuthority(run, this.contextFor(runId).revision);
1663
1512
  }
1664
1513
  resourceRevision(runId) {
@@ -1808,10 +1657,8 @@ export class WorkflowRunStore {
1808
1657
  order: row.orderNumber,
1809
1658
  state: row.status,
1810
1659
  source: row.sourceType,
1811
- sessionEntryId: row.sessionEntryId,
1812
1660
  }));
1813
1661
  const followUpStart = Math.max(0, followUps.length - VIEWER_PAGE_SIZE);
1814
- const queue = this.requireFollowUpQueueRow(runId);
1815
1662
  return [
1816
1663
  {
1817
1664
  targetType: "inspector",
@@ -1833,10 +1680,7 @@ export class WorkflowRunStore {
1833
1680
  {
1834
1681
  op: "replace",
1835
1682
  path: "/followUpQueue",
1836
- value: parseJson(canonicalJson({
1837
- presentationState: queue.presentationState,
1838
- items: followUps.slice(followUpStart),
1839
- })),
1683
+ value: parseJson(canonicalJson({ items: followUps.slice(followUpStart) })),
1840
1684
  },
1841
1685
  { op: "replace", path: "/followUpStart", value: followUpStart },
1842
1686
  { op: "replace", path: "/followUpTotal", value: followUps.length },
@@ -1845,21 +1689,7 @@ export class WorkflowRunStore {
1845
1689
  ];
1846
1690
  }
1847
1691
  initializeRunSettingsAndFollowUps(state, initialSettings, now) {
1848
- const childQueueResourceId = this.ensureFollowUpQueue(state.runId, now);
1849
1692
  if (state.parentRunId !== undefined) {
1850
- const parentQueue = this.followUpQueueRow(state.parentRunId);
1851
- if (parentQueue !== undefined) {
1852
- this.state.connection
1853
- .prepare(`UPDATE workflow_follow_up_queues
1854
- SET origin_session_id = COALESCE(origin_session_id, ?), updated_at = ?
1855
- WHERE run_id = ?`)
1856
- .run(parentQueue.originSessionId, now, state.runId);
1857
- this.state.connection
1858
- .prepare(`UPDATE workflow_follow_ups
1859
- SET run_id = ?, queue_resource_id = ?, updated_at = ?
1860
- WHERE run_id = ? AND status = 'queued'`)
1861
- .run(state.runId, childQueueResourceId, now, state.parentRunId);
1862
- }
1863
1693
  this.state.connection
1864
1694
  .prepare(`UPDATE workflow_settings SET active_run_id = ?, updated_at = ? WHERE active_run_id = ?`)
1865
1695
  .run(state.runId, now, state.parentRunId);
@@ -1884,16 +1714,6 @@ export class WorkflowRunStore {
1884
1714
  .run(scopeId, resourceId, originRunId, state.runId, scope.mountPath, scope.invocation, settingsHash, settingsHash, now, now);
1885
1715
  }
1886
1716
  }
1887
- ensureFollowUpQueue(runId, now) {
1888
- const resourceId = this.mutations.ensureResource("follow_up_queue", runId, now);
1889
- this.state.connection
1890
- .prepare(`INSERT INTO workflow_follow_up_queues(
1891
- run_id, resource_id, created_at, updated_at
1892
- ) VALUES (?, ?, ?, ?)
1893
- ON CONFLICT(run_id) DO NOTHING`)
1894
- .run(runId, resourceId, now, now);
1895
- return resourceId;
1896
- }
1897
1717
  logicalOriginRunId(runId) {
1898
1718
  let current = runId;
1899
1719
  for (let depth = 0; depth < 100; depth += 1) {
@@ -2026,27 +1846,6 @@ export class WorkflowRunStore {
2026
1846
  throw new Error(`Workflow settings request ID was reused with different content`);
2027
1847
  }
2028
1848
  }
2029
- followUpQueueRow(runId) {
2030
- const row = this.state.connection
2031
- .prepare(`SELECT q.run_id AS runId, q.resource_id AS resourceId,
2032
- q.origin_session_id AS originSessionId,
2033
- q.presentation_state AS presentationState,
2034
- q.presentation_entry_id AS presentationEntryId,
2035
- q.presentation_assistant_entry_id AS presentationAssistantEntryId,
2036
- q.presentation_reason_hash AS presentationReasonHash,
2037
- r.revision
2038
- FROM workflow_follow_up_queues q
2039
- JOIN resources r ON r.resource_id = q.resource_id
2040
- WHERE q.run_id = ?`)
2041
- .get(runId);
2042
- return isFollowUpQueueRow(row) ? row : undefined;
2043
- }
2044
- requireFollowUpQueueRow(runId) {
2045
- const row = this.followUpQueueRow(runId);
2046
- if (row === undefined)
2047
- throw new Error(`Workflow follow-up queue not found: ${runId}`);
2048
- return row;
2049
- }
2050
1849
  followUpRowByRequest(runId, requestId) {
2051
1850
  const row = this.state.connection
2052
1851
  .prepare(`${FOLLOW_UP_ROW_SELECT} WHERE f.run_id = ? AND f.request_id = ?`)
@@ -2079,11 +1878,9 @@ export class WorkflowRunStore {
2079
1878
  source: row.sourceType,
2080
1879
  prompt: this.readText(row.promptHash),
2081
1880
  state: assertFollowUpState(row.status),
2082
- ...(row.sessionEntryId !== null ? { sessionEntryId: row.sessionEntryId } : {}),
2083
1881
  ...(row.reasonHash !== null ? { reason: this.readText(row.reasonHash) } : {}),
2084
1882
  createdAt: new Date(row.createdAt).toISOString(),
2085
1883
  updatedAt: new Date(row.updatedAt).toISOString(),
2086
- ...(row.sentAt !== null ? { sentAt: new Date(row.sentAt).toISOString() } : {}),
2087
1884
  };
2088
1885
  }
2089
1886
  assertSameFollowUp(row, request) {
@@ -2107,12 +1904,6 @@ export class WorkflowRunStore {
2107
1904
  }
2108
1905
  throw new Error("Workflow follow-up can be removed only by its source or a verified human");
2109
1906
  }
2110
- hasLiveLease(resourceId) {
2111
- const row = this.state.connection
2112
- .prepare("SELECT expires_at AS expiresAt FROM leases WHERE resource_id = ?")
2113
- .get(resourceId);
2114
- return isLeaseExpiryRow(row) && row.expiresAt !== null && row.expiresAt > Date.now();
2115
- }
2116
1907
  nextFollowUpOrder(runId) {
2117
1908
  const row = this.state.connection
2118
1909
  .prepare(`SELECT COALESCE(MAX(order_number), 0) + 1 AS nextOrder
@@ -2137,37 +1928,37 @@ export class WorkflowRunStore {
2137
1928
  throw new StaleResourceError(`Settings route ${event.nodeId} used change ${changeNumber}, current change is ${scope.revision}`);
2138
1929
  }
2139
1930
  }
2140
- transitionFollowUpsForRunState(state, event, now) {
2141
- if (state.status === "running" || state.status === "waiting")
1931
+ transitionFollowUpsForRunState(state, _event, now) {
1932
+ if (state.status === "running" || state.status === "waiting" || state.status === "completed") {
2142
1933
  return;
2143
- const queue = this.followUpQueueRow(state.runId);
2144
- if (queue === undefined || queue.presentationState !== "none")
2145
- return;
2146
- let presentationState = "not-needed";
2147
- let followUpState = "cancelled";
2148
- let reasonHash = null;
2149
- if (state.status === "completed") {
2150
- const required = event.payload.presentationRequired === true;
2151
- presentationState = required ? "pending" : "not-needed";
2152
- followUpState = required ? "pending_presentation" : "ready";
2153
1934
  }
2154
- else {
2155
- reasonHash = this.state.putText(state.error ?? `Workflow ended with status ${state.status}`, now);
1935
+ const rows = this.state.connection
1936
+ .prepare(`${FOLLOW_UP_ROW_SELECT}
1937
+ WHERE f.run_id IN (
1938
+ WITH RECURSIVE ancestors(run_id, parent_run_id) AS (
1939
+ SELECT run_id, parent_run_id FROM runs WHERE run_id = ?
1940
+ UNION ALL
1941
+ SELECT parent.run_id, parent.parent_run_id
1942
+ FROM runs parent JOIN ancestors ON ancestors.parent_run_id = parent.run_id
1943
+ )
1944
+ SELECT run_id FROM ancestors
1945
+ ) AND f.status = 'queued'`)
1946
+ .all(state.runId)
1947
+ .filter(isFollowUpRow);
1948
+ for (const row of rows) {
1949
+ const reason = state.error ?? `Workflow ended with status ${state.status}`;
1950
+ const reasonHash = this.state.putText(reason, now);
1951
+ const update = this.state.connection
1952
+ .prepare(`UPDATE workflow_follow_ups
1953
+ SET status = 'cancelled', reason_hash = ?, updated_at = ?
1954
+ WHERE follow_up_id = ? AND status = 'queued'`)
1955
+ .run(reasonHash, now, row.followUpId);
1956
+ if (update.changes !== 1)
1957
+ continue;
1958
+ this.workflowMessages.cancelPendingForSource(row.followUpId, "followUp", now);
1959
+ this.bumpResource(row.resourceId, row.revision, now);
1960
+ insertGenericEvent(this.state, row.resourceId, row.revision + 1, "follow-up.cancelled", "system", null, this.state.putJson({ runId: state.runId, status: state.status, reason }, now), now);
2156
1961
  }
2157
- this.state.connection
2158
- .prepare(`UPDATE workflow_follow_up_queues
2159
- SET presentation_state = ?, presentation_updated_at = ?, updated_at = ?
2160
- WHERE run_id = ? AND presentation_state = 'none'`)
2161
- .run(presentationState, now, now, state.runId);
2162
- this.state.connection
2163
- .prepare(`UPDATE workflow_follow_ups
2164
- SET status = ?, reason_hash = ?, updated_at = ?
2165
- WHERE run_id = ? AND status IN ('queued', 'pending_presentation', 'ready')`)
2166
- .run(followUpState, reasonHash, now, state.runId);
2167
- const revision = this.requireResourceRevision(queue.resourceId);
2168
- this.bumpResource(queue.resourceId, revision, now);
2169
- const payloadHash = this.state.putJson({ runId: state.runId, state: presentationState, followUps: followUpState }, now);
2170
- insertGenericEvent(this.state, queue.resourceId, revision + 1, "follow-up.run-finished", "system", null, payloadHash, now);
2171
1962
  }
2172
1963
  syncNodeAttempts(state, snapshot, now) {
2173
1964
  for (const step of state.steps.slice(state.carriedStepCount ?? 0)) {
@@ -2300,7 +2091,7 @@ export class WorkflowRunStore {
2300
2091
  JOIN session_segments s ON s.segment_id = e.segment_id
2301
2092
  JOIN blobs b ON b.blob_hash = e.entry_hash
2302
2093
  WHERE s.run_id = ?
2303
- AND json_extract(CAST(b.content AS TEXT), '$.customType') = 'pi-workflows-agent-step'
2094
+ AND json_extract(CAST(b.content AS TEXT), '$.customType') = 'pi-workflows-step'
2304
2095
  AND json_extract(CAST(b.content AS TEXT), '$.details.contract.attemptId') = ?
2305
2096
  ORDER BY e.recorded_at DESC LIMIT 1`)
2306
2097
  .get(runId, attemptId);
@@ -2438,7 +2229,13 @@ export class WorkflowRunStore {
2438
2229
  buildRunState(row, snapshot, projectionSteps, visibleSteps, visibleUpdates) {
2439
2230
  const sources = this.readRunSources(row.runId, snapshot);
2440
2231
  const carriedStepCount = this.carriedStepCount(row.runId);
2441
- const activeAttempt = row.status === "running" ? this.readActiveAttempt(row.runId) : undefined;
2232
+ const activeAttempt = row.status === "running" || row.status === "waiting"
2233
+ ? this.readActiveAttempt(row.runId)
2234
+ : undefined;
2235
+ const runningAttempt = row.status === "running" ? activeAttempt : undefined;
2236
+ const waitingNode = row.status === "waiting"
2237
+ ? (activeAttempt?.nodeId ?? projectionSteps.at(-1)?.nodeId)
2238
+ : undefined;
2442
2239
  const humanDecision = this.readHumanDecisionReceipt(row.runId);
2443
2240
  const outputs = {};
2444
2241
  const results = {};
@@ -2481,7 +2278,7 @@ export class WorkflowRunStore {
2481
2278
  results,
2482
2279
  steps: visibleSteps,
2483
2280
  ...(visibleUpdates.length === 0 ? {} : { updates: visibleUpdates }),
2484
- ...(activeAttempt === undefined ? {} : { currentNode: activeAttempt.nodeId }),
2281
+ ...(runningAttempt === undefined ? {} : { currentNode: runningAttempt.nodeId }),
2485
2282
  ...(activeAttempt === undefined ? {} : { currentAttemptId: activeAttempt.attemptId }),
2486
2283
  ...(activeAttempt?.startedAt === null || activeAttempt === undefined
2487
2284
  ? {}
@@ -2497,9 +2294,7 @@ export class WorkflowRunStore {
2497
2294
  ...(row.statusDetail === null ? {} : { statusDetail: row.statusDetail }),
2498
2295
  ...(humanDecision === undefined ? {} : { humanDecision }),
2499
2296
  ...(row.paused === 0 ? {} : { paused: true }),
2500
- ...(row.status === "waiting" && projectionSteps.at(-1) !== undefined
2501
- ? { waitingOn: projectionSteps.at(-1)?.nodeId }
2502
- : {}),
2297
+ ...(waitingNode === undefined ? {} : { waitingOn: waitingNode }),
2503
2298
  ...(row.finalOutputHash === null
2504
2299
  ? {}
2505
2300
  : { finalOutput: this.state.readJson(row.finalOutputHash) }),
@@ -3334,26 +3129,11 @@ function assertActorType(value) {
3334
3129
  return value;
3335
3130
  }
3336
3131
  function assertFollowUpState(value) {
3337
- if (value !== "queued" &&
3338
- value !== "pending_presentation" &&
3339
- value !== "ready" &&
3340
- value !== "sent" &&
3341
- value !== "removed" &&
3342
- value !== "cancelled") {
3132
+ if (value !== "queued" && value !== "removed" && value !== "cancelled") {
3343
3133
  throw new Error(`Unknown workflow follow-up state: ${value}`);
3344
3134
  }
3345
3135
  return value;
3346
3136
  }
3347
- function assertPresentationState(value) {
3348
- if (value !== "none" &&
3349
- value !== "not-needed" &&
3350
- value !== "pending" &&
3351
- value !== "settled" &&
3352
- value !== "unavailable") {
3353
- throw new Error(`Unknown workflow presentation state: ${value}`);
3354
- }
3355
- return value;
3356
- }
3357
3137
  function isSettingsScopeRow(value) {
3358
3138
  return (isRecord(value) &&
3359
3139
  typeof value.scopeId === "string" &&
@@ -3395,23 +3175,10 @@ function isSettingsChangeRow(value) {
3395
3175
  Buffer.isBuffer(value.afterHash) &&
3396
3176
  typeof value.acceptedAt === "number");
3397
3177
  }
3398
- function isFollowUpQueueRow(value) {
3399
- return (isRecord(value) &&
3400
- typeof value.runId === "string" &&
3401
- typeof value.resourceId === "string" &&
3402
- (typeof value.originSessionId === "string" || value.originSessionId === null) &&
3403
- typeof value.presentationState === "string" &&
3404
- (typeof value.presentationEntryId === "string" || value.presentationEntryId === null) &&
3405
- (typeof value.presentationAssistantEntryId === "string" ||
3406
- value.presentationAssistantEntryId === null) &&
3407
- (Buffer.isBuffer(value.presentationReasonHash) || value.presentationReasonHash === null) &&
3408
- typeof value.revision === "number");
3409
- }
3410
3178
  function isFollowUpRow(value) {
3411
3179
  return (isRecord(value) &&
3412
3180
  typeof value.followUpId === "string" &&
3413
3181
  typeof value.resourceId === "string" &&
3414
- typeof value.queueResourceId === "string" &&
3415
3182
  typeof value.runId === "string" &&
3416
3183
  typeof value.requestId === "string" &&
3417
3184
  typeof value.orderNumber === "number" &&
@@ -3421,16 +3188,19 @@ function isFollowUpRow(value) {
3421
3188
  typeof value.sourceType === "string" &&
3422
3189
  Buffer.isBuffer(value.promptHash) &&
3423
3190
  typeof value.status === "string" &&
3424
- (typeof value.sessionEntryId === "string" || value.sessionEntryId === null) &&
3425
3191
  (Buffer.isBuffer(value.reasonHash) || value.reasonHash === null) &&
3426
3192
  typeof value.createdAt === "number" &&
3427
3193
  typeof value.updatedAt === "number" &&
3428
- (typeof value.sentAt === "number" || value.sentAt === null) &&
3429
3194
  typeof value.revision === "number");
3430
3195
  }
3431
3196
  function isParentRunRow(value) {
3432
3197
  return isRecord(value) && (typeof value.parentRunId === "string" || value.parentRunId === null);
3433
3198
  }
3199
+ function isRunLineageRow(value) {
3200
+ return (isRecord(value) &&
3201
+ typeof value.rootRunId === "string" &&
3202
+ typeof value.restartNumber === "number");
3203
+ }
3434
3204
  function isStatusRow(value) {
3435
3205
  return isRecord(value) && typeof value.status === "string";
3436
3206
  }
@@ -3440,9 +3210,6 @@ function isCountRow(value) {
3440
3210
  function isStepIndexRow(value) {
3441
3211
  return isRecord(value) && typeof value.stepIndex === "number";
3442
3212
  }
3443
- function isLeaseExpiryRow(value) {
3444
- return isRecord(value) && (typeof value.expiresAt === "number" || value.expiresAt === null);
3445
- }
3446
3213
  function isNextOrderRow(value) {
3447
3214
  return isRecord(value) && typeof value.nextOrder === "number";
3448
3215
  }