@osolmaz/pi-workflows 0.12.0 → 0.12.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 (119) hide show
  1. package/README.md +4 -3
  2. package/dist/builtins/autoimplement.workflow.d.ts +485 -126
  3. package/dist/builtins/autoimplement.workflow.js +17 -105
  4. package/dist/builtins/autoimplement.workflow.js.map +1 -1
  5. package/dist/builtins/catalog.js +4 -4
  6. package/dist/builtins/index.d.ts +2 -1
  7. package/dist/builtins/index.js +2 -1
  8. package/dist/builtins/index.js.map +1 -1
  9. package/dist/builtins/monitor.workflow.d.ts +2 -4
  10. package/dist/builtins/monitor.workflow.js +26 -128
  11. package/dist/builtins/monitor.workflow.js.map +1 -1
  12. package/dist/builtins/pi-agent-group.d.ts +72 -0
  13. package/dist/builtins/pi-agent-group.js +1087 -0
  14. package/dist/builtins/pi-agent-group.js.map +1 -0
  15. package/dist/builtins/plan-approval.workflow.d.ts +39 -5
  16. package/dist/builtins/plan-approval.workflow.js +92 -14
  17. package/dist/builtins/plan-approval.workflow.js.map +1 -1
  18. package/dist/builtins/plan-change.workflow.d.ts +301 -0
  19. package/dist/builtins/plan-change.workflow.js +256 -0
  20. package/dist/builtins/plan-change.workflow.js.map +1 -0
  21. package/dist/builtins/plan-presentation.js +2 -2
  22. package/dist/builtins/plan-presentation.js.map +1 -1
  23. package/dist/builtins/sanity-check.workflow.d.ts +5 -3
  24. package/dist/builtins/sanity-check.workflow.js +105 -21
  25. package/dist/builtins/sanity-check.workflow.js.map +1 -1
  26. package/dist/extension/decision-channels.d.ts +2 -2
  27. package/dist/extension/decision-channels.js +22 -28
  28. package/dist/extension/decision-channels.js.map +1 -1
  29. package/dist/extension/index.js +62 -33
  30. package/dist/extension/index.js.map +1 -1
  31. package/dist/extension/session-events.d.ts +2 -2
  32. package/dist/extension/widget.js +23 -3
  33. package/dist/extension/widget.js.map +1 -1
  34. package/dist/render/graph-render.js +1 -2
  35. package/dist/render/graph-render.js.map +1 -1
  36. package/dist/viewer/render.js +7 -6
  37. package/dist/viewer/render.js.map +1 -1
  38. package/dist/workflows/catalog.js +7 -2
  39. package/dist/workflows/catalog.js.map +1 -1
  40. package/dist/workflows/composition.js +8 -0
  41. package/dist/workflows/composition.js.map +1 -1
  42. package/dist/workflows/decision-presentation.d.ts +1 -1
  43. package/dist/workflows/decision-presentation.js +51 -38
  44. package/dist/workflows/decision-presentation.js.map +1 -1
  45. package/dist/workflows/engine.d.ts +2 -2
  46. package/dist/workflows/engine.js +14 -13
  47. package/dist/workflows/engine.js.map +1 -1
  48. package/dist/workflows/errors.d.ts +13 -0
  49. package/dist/workflows/errors.js +15 -0
  50. package/dist/workflows/errors.js.map +1 -1
  51. package/dist/workflows/human-decision.d.ts +16 -4
  52. package/dist/workflows/human-decision.js +175 -72
  53. package/dist/workflows/human-decision.js.map +1 -1
  54. package/dist/workflows/index.d.ts +2 -2
  55. package/dist/workflows/index.js +1 -1
  56. package/dist/workflows/index.js.map +1 -1
  57. package/dist/workflows/progress.d.ts +1 -0
  58. package/dist/workflows/progress.js +15 -3
  59. package/dist/workflows/progress.js.map +1 -1
  60. package/dist/workflows/schema.js +10 -0
  61. package/dist/workflows/schema.js.map +1 -1
  62. package/dist/workflows/store.js +5 -0
  63. package/dist/workflows/store.js.map +1 -1
  64. package/dist/workflows/types.d.ts +33 -45
  65. package/docs/HUMAN_DECISIONS.md +25 -35
  66. package/docs/HUMAN_DECISION_PRESENTATIONS.md +14 -24
  67. package/docs/MONITOR.md +5 -11
  68. package/docs/WORKFLOW_COMPOSITION.md +8 -7
  69. package/docs/plans/2026-08-21-plan-change-approval-policy-plan.md +322 -0
  70. package/docs/plans/2026-08-21-sanity-check-plan.md +202 -94
  71. package/docs/run-bundles.md +6 -6
  72. package/docs/workflows.md +26 -6
  73. package/examples/workflows/approved-plan.workflow.ts +19 -46
  74. package/herdr-plugin.toml +1 -1
  75. package/package.json +7 -7
  76. package/schemas/human-decision-accepted-v1.schema.json +15 -3
  77. package/schemas/human-decision-continuation-v1.schema.json +10 -1
  78. package/schemas/human-decision-delivery-v1.schema.json +8 -0
  79. package/schemas/human-decision-receipt-v1.schema.json +8 -0
  80. package/schemas/human-decision-request-v1.schema.json +24 -4
  81. package/skills/autoimplement/SKILL.md +27 -0
  82. package/skills/monitor/SKILL.md +31 -3
  83. package/skills/pi-workflows/SKILL.md +2 -1
  84. package/skills/sanity-check/SKILL.md +44 -0
  85. package/src/builtins/autoimplement.workflow.ts +19 -118
  86. package/src/builtins/catalog.ts +4 -4
  87. package/src/builtins/index.ts +11 -0
  88. package/src/builtins/monitor.workflow.ts +27 -150
  89. package/src/builtins/pi-agent-group.ts +1407 -0
  90. package/src/builtins/plan-approval.workflow.ts +157 -24
  91. package/src/builtins/plan-change.workflow.ts +321 -0
  92. package/src/builtins/plan-presentation.ts +2 -2
  93. package/src/builtins/sanity-check.workflow.ts +186 -41
  94. package/src/extension/decision-channels.ts +29 -59
  95. package/src/extension/index.ts +79 -41
  96. package/src/extension/session-events.ts +2 -2
  97. package/src/extension/widget.ts +24 -5
  98. package/src/render/graph-render.ts +1 -2
  99. package/src/viewer/render.ts +7 -6
  100. package/src/workflows/catalog.ts +7 -2
  101. package/src/workflows/composition.ts +9 -0
  102. package/src/workflows/decision-presentation.ts +56 -43
  103. package/src/workflows/engine.ts +17 -15
  104. package/src/workflows/errors.ts +24 -0
  105. package/src/workflows/human-decision.ts +218 -101
  106. package/src/workflows/index.ts +5 -11
  107. package/src/workflows/progress.ts +18 -3
  108. package/src/workflows/schema.ts +17 -0
  109. package/src/workflows/store.ts +5 -0
  110. package/src/workflows/types.ts +39 -56
  111. package/dist/builtins/sanity-check-session.d.ts +0 -17
  112. package/dist/builtins/sanity-check-session.js +0 -168
  113. package/dist/builtins/sanity-check-session.js.map +0 -1
  114. package/schemas/human-decision-accepted-v2.schema.json +0 -50
  115. package/schemas/human-decision-delivery-v2.schema.json +0 -36
  116. package/schemas/human-decision-receipt-v2.schema.json +0 -39
  117. package/schemas/human-decision-request-v2.schema.json +0 -69
  118. package/schemas/human-decision-resolution-v2.schema.json +0 -27
  119. package/src/builtins/sanity-check-session.ts +0 -205
@@ -6,7 +6,7 @@ import { projectControllerStorePath, SqliteControllerStore, } from "../controlle
6
6
  import { compositionMetadata } from "../workflows/composition.js";
7
7
  import { humanDecisionChannelRequest } from "../workflows/decision-presentation.js";
8
8
  import { WorkflowEngine } from "../workflows/engine.js";
9
- import { ClaimLostError, errorMessage, isClaimLostError, TimeoutError, } from "../workflows/errors.js";
9
+ import { BuiltinWorkflowRevisionChangedError, ClaimLostError, errorMessage, isClaimLostError, TimeoutError, } from "../workflows/errors.js";
10
10
  import { HumanDecisionStore, createHumanDecisionAttemptId, validateHumanDecisionResponse, } from "../workflows/human-decision.js";
11
11
  import { discoverWorkflows, resolveWorkflowRef } from "../workflows/loader.js";
12
12
  import { migrateLegacyWorkflowSources } from "../workflows/migrate-sources.js";
@@ -55,10 +55,8 @@ function humanDecisionRequest(value) {
55
55
  const schema = value !== null && typeof value === "object"
56
56
  ? value.schema
57
57
  : undefined;
58
- if (schema !== "pi-workflows.human-decision-request.v1" &&
59
- schema !== "pi-workflows.human-decision-request.v2") {
58
+ if (schema !== "pi-workflows.human-decision-request.v1")
60
59
  return null;
61
- }
62
60
  return value;
63
61
  }
64
62
  function definitionDigest(snapshot) {
@@ -70,6 +68,20 @@ function launchSourceIdentity(workflow, root) {
70
68
  mounted: compositionMetadata(workflow)?.sources ?? [],
71
69
  };
72
70
  }
71
+ function continuationSourceChangedError(runId, previous, current) {
72
+ if (previous.kind === "builtin" &&
73
+ current.kind === "builtin" &&
74
+ previous.id === current.id &&
75
+ previous.revision !== current.revision) {
76
+ return new BuiltinWorkflowRevisionChangedError({
77
+ runId,
78
+ workflowId: current.id,
79
+ previousRevision: previous.revision,
80
+ currentRevision: current.revision,
81
+ });
82
+ }
83
+ return new Error(`Workflow source changed since run ${runId} started; revert the edit to answer its checkpoint`);
84
+ }
73
85
  function preparedLaunchOptions(options) {
74
86
  return {
75
87
  ...(options.presentation !== undefined ? { presentation: options.presentation } : {}),
@@ -888,7 +900,7 @@ export default function piWorkflows(pi) {
888
900
  }
889
901
  if (parent.state.workflowSource !== undefined &&
890
902
  !isDeepStrictEqual(parent.state.workflowSource, workflowSource)) {
891
- throw new Error(`Workflow source changed since run ${options.parentRunId} started; revert the edit to answer its checkpoint`);
903
+ throw continuationSourceChangedError(options.parentRunId, parent.state.workflowSource, workflowSource);
892
904
  }
893
905
  }
894
906
  // Interactive runs are queued and claimed atomically, so this session
@@ -1633,7 +1645,7 @@ export default function piWorkflows(pi) {
1633
1645
  };
1634
1646
  const store = new HumanDecisionStore(new WorkflowRunStore().outputRoot);
1635
1647
  const acceptance = await store.accept(request, submission);
1636
- if (acceptance.status === "conflict") {
1648
+ if (acceptance.status === "conflict" || acceptance.decision.provenance !== "human") {
1637
1649
  throw new Error("That human decision was already answered differently.");
1638
1650
  }
1639
1651
  accepted = acceptance.decision;
@@ -1663,6 +1675,7 @@ export default function piWorkflows(pi) {
1663
1675
  schema: "pi-workflows.human-decision-continuation.v1",
1664
1676
  decisionId: request.decisionId,
1665
1677
  requestDigest: request.requestDigest,
1678
+ provenance: accepted.provenance,
1666
1679
  parentRunId: waiting.parentRunId,
1667
1680
  runId: continuationRunId,
1668
1681
  createdAt: accepted.acceptedAt,
@@ -1775,60 +1788,76 @@ export default function piWorkflows(pi) {
1775
1788
  const ownedBySession = queueRecord !== undefined &&
1776
1789
  (queueRecord.originSessionId === null ||
1777
1790
  queueRecord.originSessionId === ctx.sessionManager.getSessionId());
1778
- const accepted = await store.readAccepted(request.decisionId);
1779
- if (accepted === null) {
1791
+ let resolved = await store.readResolved(request.decisionId);
1792
+ if (resolved === null) {
1780
1793
  let cancellation = await store.readCancellation(request.decisionId);
1781
- if (cancellation === null &&
1782
- request.expiresAt !== undefined &&
1783
- Date.parse(request.expiresAt) <= Date.now()) {
1784
- await store.cancel(request, "expired");
1785
- cancellation = await store.readCancellation(request.decisionId);
1794
+ const expired = request.expiresAt !== undefined && Date.parse(request.expiresAt) <= Date.now();
1795
+ if (cancellation === null && expired) {
1796
+ if (request.defaultResponse === undefined) {
1797
+ await store.cancel(request, "expired");
1798
+ cancellation = await store.readCancellation(request.decisionId);
1799
+ }
1800
+ else {
1801
+ try {
1802
+ resolved = (await store.resolveTimeout(request)).decision;
1803
+ }
1804
+ catch {
1805
+ cancellation = await store.readCancellation(request.decisionId);
1806
+ resolved = await store.readResolved(request.decisionId);
1807
+ }
1808
+ }
1786
1809
  }
1787
1810
  if (cancellation !== null) {
1788
1811
  await settleHumanDecisionChannels(cancellation);
1789
1812
  continue;
1790
1813
  }
1791
- if (!deliverPending || !ownedBySession)
1814
+ if (resolved === null) {
1815
+ if (!deliverPending || !ownedBySession)
1816
+ continue;
1817
+ const channels = audienceChannels(decisionChannelConfig, request.audience);
1818
+ for (const channelId of channels) {
1819
+ const channel = telegramDecisionChannels.get(channelId);
1820
+ if (channel !== undefined)
1821
+ await channel.deliver(humanDecisionChannelRequest(request));
1822
+ }
1823
+ if (ctx.mode === "tui" && channels.includes("pi") && lastWaitingRunId === null) {
1824
+ lastWaitingRunId = request.runId;
1825
+ queueMicrotask(() => void promptHumanDecision(ctx, request));
1826
+ }
1792
1827
  continue;
1793
- const channels = audienceChannels(decisionChannelConfig, request.audience);
1794
- for (const channelId of channels) {
1795
- const channel = telegramDecisionChannels.get(channelId);
1796
- if (channel !== undefined)
1797
- await channel.deliver(humanDecisionChannelRequest(request));
1798
- }
1799
- if (ctx.mode === "tui" && channels.includes("pi") && lastWaitingRunId === null) {
1800
- lastWaitingRunId = request.runId;
1801
- queueMicrotask(() => void promptHumanDecision(ctx, request));
1802
1828
  }
1803
- continue;
1804
1829
  }
1805
1830
  if (!ownedBySession)
1806
1831
  continue;
1832
+ const currentParent = await readRunBundle(runStore.runDirFor(request.runId));
1833
+ if (currentParent === null || currentParent.state.status !== "waiting")
1834
+ continue;
1807
1835
  const runId = `continuation-${request.decisionId.slice("decision-".length)}`;
1808
1836
  const continuation = (await store.readContinuation(request.decisionId)) ?? {
1809
1837
  schema: "pi-workflows.human-decision-continuation.v1",
1810
1838
  decisionId: request.decisionId,
1811
1839
  requestDigest: request.requestDigest,
1840
+ provenance: resolved.provenance,
1812
1841
  parentRunId: request.runId,
1813
1842
  runId,
1814
- createdAt: accepted.acceptedAt,
1843
+ createdAt: resolved.acceptedAt,
1815
1844
  };
1816
1845
  await store.recordContinuation(request.decisionId, continuation);
1817
1846
  const existing = await readRunBundle(runStore.runDirFor(continuation.runId));
1818
1847
  if (existing === null && activeRun === null) {
1819
- if (parent.state.workflowSource === undefined)
1848
+ if (currentParent.state.workflowSource === undefined)
1820
1849
  continue;
1821
- const workflowRef = parent.state.workflowSource.kind === "builtin"
1822
- ? `builtin:${parent.state.workflowSource.id}`
1823
- : parent.state.workflowSource.path;
1824
- await startRun(ctx, workflowRef, parent.state.input, {
1850
+ const workflowRef = currentParent.state.workflowSource.kind === "builtin"
1851
+ ? `builtin:${currentParent.state.workflowSource.id}`
1852
+ : currentParent.state.workflowSource.path;
1853
+ await startRun(ctx, workflowRef, currentParent.state.input, {
1825
1854
  parentRunId: request.runId,
1826
- humanDecision: accepted,
1855
+ humanDecision: resolved,
1827
1856
  runId: continuation.runId,
1828
1857
  quiet: true,
1829
1858
  });
1830
1859
  }
1831
- await settleHumanDecisionChannels(accepted);
1860
+ await settleHumanDecisionChannels(resolved);
1832
1861
  if (activeRun !== null)
1833
1862
  break;
1834
1863
  }
@@ -1890,7 +1919,7 @@ export default function piWorkflows(pi) {
1890
1919
  }
1891
1920
  if (parent.state.workflowSource !== undefined &&
1892
1921
  !isDeepStrictEqual(parent.state.workflowSource, workflowSource)) {
1893
- throw new Error(`Workflow source changed since run ${options.parentRunId} started; revert the edit to answer its checkpoint`);
1922
+ throw continuationSourceChangedError(options.parentRunId, parent.state.workflowSource, workflowSource);
1894
1923
  }
1895
1924
  }
1896
1925
  const snapshot = createDefinitionSnapshot(workflow);