@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
@@ -4,8 +4,13 @@ import {
4
4
  type CommandBatchItemResult,
5
5
  } from "../workflows/command-batch.js";
6
6
  import { action, compute, defineWorkflow, notify } from "../workflows/definition.js";
7
- import type { WorkflowActionContext } from "../workflows/types.js";
8
- import { runIsolatedReviewSessions, type IsolatedReviewRequest } from "./sanity-check-session.js";
7
+ import { extractJsonValue } from "../workflows/json.js";
8
+ import type { WorkflowActionContext, WorkflowProgressStatus } from "../workflows/types.js";
9
+ import {
10
+ runPiAgentGroup,
11
+ type PiAgentLifecycleEvent,
12
+ type PiAgentRequest,
13
+ } from "./pi-agent-group.js";
9
14
 
10
15
  const REVIEW_TIMEOUT_MS = 20 * 60_000;
11
16
  const MAX_STRING_CHARS = 4_000;
@@ -237,21 +242,37 @@ export async function collectContributionEvidence(
237
242
  };
238
243
  }
239
244
 
245
+ type SanityCheckAgentPrompt = Pick<PiAgentRequest, "id" | "role" | "prompt">;
246
+
240
247
  export function buildReviewRequests(
241
248
  mode: SanityCheckMode,
242
249
  evidence: ContributionEvidence,
243
- ): IsolatedReviewRequest[] {
250
+ ): SanityCheckAgentPrompt[] {
244
251
  if (mode === "serial") {
245
- return [{ id: "review", prompt: reviewPrompt(reviewAreas, evidence) }];
252
+ return [{ id: "review", role: "Combined review", prompt: reviewPrompt(reviewAreas, evidence) }];
246
253
  }
247
- return reviewAreas.map((area) => ({ id: area, prompt: reviewPrompt([area], evidence) }));
254
+ const roles: Record<SanityCheckArea, string> = {
255
+ necessity: "Necessity",
256
+ duplication: "Duplication and refactoring",
257
+ contracts: "Data models and public APIs",
258
+ scope_tests: "Scope and tests",
259
+ };
260
+ return reviewAreas.map((area) => ({
261
+ id: area,
262
+ role: roles[area],
263
+ prompt: reviewPrompt([area], evidence),
264
+ }));
248
265
  }
249
266
 
250
267
  export function buildVerificationRequest(
251
268
  evidence: ContributionEvidence,
252
269
  reviews: SanityCheckReview[],
253
- ): IsolatedReviewRequest {
254
- return { id: "verification", prompt: verificationPrompt(evidence, reviews) };
270
+ ): SanityCheckAgentPrompt {
271
+ return {
272
+ id: "verification",
273
+ role: "Verification",
274
+ prompt: verificationPrompt(evidence, reviews),
275
+ };
255
276
  }
256
277
 
257
278
  export function parseReviewOutput(
@@ -321,53 +342,177 @@ async function runReviews(context: WorkflowActionContext): Promise<SanityCheckRe
321
342
  const config = context.outputs.prepare as SanityCheckConfig;
322
343
  const evidence = context.outputs.collectEvidence as ContributionEvidence;
323
344
  const requests = buildReviewRequests(config.mode, evidence);
324
- await publishProgress(context, "review", 0, requests.length);
325
- const outputs = await runIsolatedReviewSessions(requests, evidence.repository, context.signal, {
326
- maxConcurrency: config.mode === "parallel" ? 4 : 1,
327
- });
328
- const reviews = requests.map((request) =>
329
- parseReviewOutput(
330
- outputs[request.id],
331
- config.mode === "serial" ? reviewAreas : [request.id as SanityCheckArea],
332
- ),
333
- );
334
- await publishProgress(context, "review", requests.length, requests.length);
335
- return reviews;
345
+ const progress = await createAgentProgress(context, "review", requests);
346
+ try {
347
+ const results = await runPiAgentGroup(agentRequests(requests, evidence.repository), {
348
+ maxConcurrency: config.mode === "parallel" ? 4 : 1,
349
+ signal: context.signal,
350
+ onLifecycle: progress.onLifecycle,
351
+ });
352
+ const reviews = results.map((result, index) =>
353
+ parseReviewOutput(
354
+ parseAgentJsonOutput(result.text, result.id),
355
+ config.mode === "serial" ? reviewAreas : [requests[index]!.id as SanityCheckArea],
356
+ ),
357
+ );
358
+ await progress.complete();
359
+ return reviews;
360
+ } catch (error) {
361
+ await progress.fail();
362
+ throw error;
363
+ }
336
364
  }
337
365
 
338
366
  async function verifyReviews(context: WorkflowActionContext): Promise<SanityCheckResult> {
339
367
  const evidence = context.outputs.collectEvidence as ContributionEvidence;
340
368
  const reviews = context.outputs.review as SanityCheckReview[];
341
- await publishProgress(context, "verification", 0, 1);
342
- const outputs = await runIsolatedReviewSessions(
343
- [buildVerificationRequest(evidence, reviews)],
344
- evidence.repository,
345
- context.signal,
346
- { maxConcurrency: 1 },
369
+ const requests = [buildVerificationRequest(evidence, reviews)];
370
+ const progress = await createAgentProgress(context, "verification", requests);
371
+ try {
372
+ const [result] = await runPiAgentGroup(agentRequests(requests, evidence.repository), {
373
+ maxConcurrency: 1,
374
+ signal: context.signal,
375
+ onLifecycle: progress.onLifecycle,
376
+ });
377
+ const parsed = parseSanityCheckResult(parseAgentJsonOutput(result!.text, result!.id));
378
+ await progress.complete();
379
+ return parsed;
380
+ } catch (error) {
381
+ await progress.fail();
382
+ throw error;
383
+ }
384
+ }
385
+
386
+ export function parseAgentJsonOutput(text: string, agentId: string): unknown {
387
+ try {
388
+ return extractJsonValue(text);
389
+ } catch {
390
+ throw new Error(`Sanity Check agent ${agentId} returned invalid JSON`);
391
+ }
392
+ }
393
+
394
+ function agentRequests(requests: SanityCheckAgentPrompt[], cwd: string): PiAgentRequest[] {
395
+ return requests.map((request) => ({
396
+ ...request,
397
+ cwd,
398
+ tools: ["read", "grep", "find", "ls"],
399
+ timeoutMs: REVIEW_TIMEOUT_MS,
400
+ }));
401
+ }
402
+
403
+ type AgentProgress = {
404
+ onLifecycle(event: PiAgentLifecycleEvent): Promise<void>;
405
+ complete(): Promise<void>;
406
+ fail(): Promise<void>;
407
+ };
408
+
409
+ async function createAgentProgress(
410
+ context: WorkflowActionContext,
411
+ group: "review" | "verification",
412
+ requests: SanityCheckAgentPrompt[],
413
+ ): Promise<AgentProgress> {
414
+ const aggregateKey = `agents/${group}`;
415
+ const settled = new Set<string>();
416
+ await safeProgress(context, aggregateKey, "running", group, 0, requests.length);
417
+ await Promise.all(
418
+ requests.map(
419
+ async (request) =>
420
+ await safeProgress(
421
+ context,
422
+ `${aggregateKey}/${request.id}`,
423
+ "pending",
424
+ "pending",
425
+ 0,
426
+ 1,
427
+ request.role,
428
+ ),
429
+ ),
347
430
  );
348
- const result = parseSanityCheckResult(outputs.verification);
349
- await publishProgress(context, "verification", 1, 1);
350
- return result;
431
+ let updateWork = Promise.resolve();
432
+ const enqueue = (update: () => Promise<void>) => {
433
+ updateWork = updateWork.then(update).catch(() => undefined);
434
+ return updateWork;
435
+ };
436
+ return {
437
+ async onLifecycle(event) {
438
+ await enqueue(async () => {
439
+ const terminal = event.state !== "running";
440
+ if (terminal) settled.add(event.id);
441
+ const label = event.model === undefined ? event.role : `${event.role} · ${event.model}`;
442
+ await safeProgress(
443
+ context,
444
+ `${aggregateKey}/${event.id}`,
445
+ event.state,
446
+ event.phase,
447
+ terminal ? 1 : 0,
448
+ 1,
449
+ label,
450
+ );
451
+ if (terminal) {
452
+ await safeProgress(
453
+ context,
454
+ aggregateKey,
455
+ "running",
456
+ group,
457
+ settled.size,
458
+ requests.length,
459
+ );
460
+ }
461
+ });
462
+ },
463
+ async complete() {
464
+ await enqueue(
465
+ async () =>
466
+ await safeProgress(
467
+ context,
468
+ aggregateKey,
469
+ "completed",
470
+ group,
471
+ requests.length,
472
+ requests.length,
473
+ ),
474
+ );
475
+ },
476
+ async fail() {
477
+ await enqueue(
478
+ async () =>
479
+ await safeProgress(
480
+ context,
481
+ aggregateKey,
482
+ context.signal.aborted ? "cancelled" : "failed",
483
+ group,
484
+ settled.size,
485
+ requests.length,
486
+ ),
487
+ );
488
+ },
489
+ };
351
490
  }
352
491
 
353
- async function publishProgress(
492
+ async function safeProgress(
354
493
  context: WorkflowActionContext,
494
+ key: string,
495
+ status: WorkflowProgressStatus,
355
496
  phase: string,
356
497
  completed: number,
357
498
  total: number,
499
+ label?: string,
358
500
  ): Promise<void> {
359
- await context.publishUpdate({
360
- type: "progress",
361
- key: phase,
362
- data: {
363
- schema: "pi-workflows.progress.v1",
364
- status: completed === total ? "completed" : "running",
365
- phase,
366
- completed,
367
- total,
368
- unit: "sessions",
369
- },
370
- });
501
+ await context
502
+ .publishUpdate({
503
+ type: "progress",
504
+ key,
505
+ data: {
506
+ schema: "pi-workflows.progress.v1",
507
+ status,
508
+ phase,
509
+ completed,
510
+ total,
511
+ unit: "sessions",
512
+ ...(label !== undefined ? { label: label.slice(0, 200) } : {}),
513
+ },
514
+ })
515
+ .catch(() => undefined);
371
516
  }
372
517
 
373
518
  function reviewPrompt(areas: readonly SanityCheckArea[], evidence: ContributionEvidence): string {
@@ -14,7 +14,7 @@ import {
14
14
  } from "../workflows/decision-presentation.js";
15
15
  import { HumanDecisionStore, createHumanDecisionAttemptId } from "../workflows/human-decision.js";
16
16
  import type {
17
- AcceptedHumanDecision,
17
+ ResolvedHumanDecision,
18
18
  HumanDecisionAnswerSource,
19
19
  HumanDecisionCancellationRecord,
20
20
  HumanDecisionChannelRequest,
@@ -47,7 +47,7 @@ export type HumanDecisionDeliveryResult = {
47
47
  errorCode?: string;
48
48
  };
49
49
 
50
- export type SettledHumanDecision = AcceptedHumanDecision | HumanDecisionCancellationRecord;
50
+ export type SettledHumanDecision = ResolvedHumanDecision | HumanDecisionCancellationRecord;
51
51
 
52
52
  export interface HumanDecisionChannel {
53
53
  readonly id: string;
@@ -162,7 +162,7 @@ export class PiDecisionChannel implements HumanDecisionChannel {
162
162
  await this.options.store.recordDelivery(
163
163
  request,
164
164
  this.id,
165
- v2DeliveryRecord(request, this.id, attemptId, "intent", "intent", createdAt),
165
+ deliveryRecord(request, this.id, attemptId, "intent", "intent", createdAt),
166
166
  );
167
167
  const entries = Object.entries(request.choices);
168
168
  const selectedChoice = await this.options.ui.custom<string | undefined>(
@@ -241,7 +241,7 @@ export class PiDecisionChannel implements HumanDecisionChannel {
241
241
  await this.options.store.recordDelivery(
242
242
  request,
243
243
  this.id,
244
- v2DeliveryRecord(
244
+ deliveryRecord(
245
245
  request,
246
246
  this.id,
247
247
  `${attemptId}-cancelled`,
@@ -267,7 +267,7 @@ export class PiDecisionChannel implements HumanDecisionChannel {
267
267
  await this.options.store.recordDelivery(
268
268
  request,
269
269
  this.id,
270
- v2DeliveryRecord(
270
+ deliveryRecord(
271
271
  request,
272
272
  this.id,
273
273
  `${attemptId}-cancelled`,
@@ -291,7 +291,7 @@ export class PiDecisionChannel implements HumanDecisionChannel {
291
291
  await this.options.store.recordDelivery(
292
292
  request,
293
293
  this.id,
294
- v2DeliveryRecord(
294
+ deliveryRecord(
295
295
  request,
296
296
  this.id,
297
297
  `${attemptId}-confirmed`,
@@ -684,7 +684,7 @@ export class TelegramDecisionChannel implements HumanDecisionChannel {
684
684
  await this.store.recordDelivery(
685
685
  request,
686
686
  channelPath,
687
- v2DeliveryRecord(request, this.id, attemptId, "intent", "intent", createdAt, {
687
+ deliveryRecord(request, this.id, attemptId, "intent", "intent", createdAt, {
688
688
  partCount: parts.length,
689
689
  }),
690
690
  );
@@ -704,7 +704,6 @@ export class TelegramDecisionChannel implements HumanDecisionChannel {
704
704
  );
705
705
  const confirmed = prior.some(
706
706
  (record) =>
707
- record.schema === "pi-workflows.human-decision-delivery.v2" &&
708
707
  record.phase === "part" &&
709
708
  record.state === "confirmed" &&
710
709
  record.recipientIndex === recipientIndex &&
@@ -724,7 +723,7 @@ export class TelegramDecisionChannel implements HumanDecisionChannel {
724
723
  await this.store.recordDelivery(
725
724
  request,
726
725
  channelPath,
727
- v2DeliveryRecord(
726
+ deliveryRecord(
728
727
  request,
729
728
  this.id,
730
729
  `${attemptId}-r${recipientIndex}-p${partIndex}-unknown`,
@@ -740,15 +739,12 @@ export class TelegramDecisionChannel implements HumanDecisionChannel {
740
739
  await this.store.recordDelivery(
741
740
  request,
742
741
  channelPath,
743
- v2DeliveryRecord(
744
- request,
745
- this.id,
746
- `${partAttemptId}-intent`,
747
- "part",
748
- "intent",
749
- createdAt,
750
- { recipientIndex, partIndex, partCount: parts.length, contentDigest },
751
- ),
742
+ deliveryRecord(request, this.id, `${partAttemptId}-intent`, "part", "intent", createdAt, {
743
+ recipientIndex,
744
+ partIndex,
745
+ partCount: parts.length,
746
+ contentDigest,
747
+ }),
752
748
  );
753
749
  try {
754
750
  const result = await this.call("sendMessage", {
@@ -769,7 +765,7 @@ export class TelegramDecisionChannel implements HumanDecisionChannel {
769
765
  await this.store.recordDelivery(
770
766
  request,
771
767
  channelPath,
772
- v2DeliveryRecord(
768
+ deliveryRecord(
773
769
  request,
774
770
  this.id,
775
771
  `${partAttemptId}-confirmed`,
@@ -785,7 +781,7 @@ export class TelegramDecisionChannel implements HumanDecisionChannel {
785
781
  await this.store.recordDelivery(
786
782
  request,
787
783
  channelPath,
788
- v2DeliveryRecord(
784
+ deliveryRecord(
789
785
  request,
790
786
  this.id,
791
787
  `${partAttemptId}-${state}`,
@@ -804,7 +800,7 @@ export class TelegramDecisionChannel implements HumanDecisionChannel {
804
800
  await this.store.recordDelivery(
805
801
  request,
806
802
  channelPath,
807
- v2DeliveryRecord(
803
+ deliveryRecord(
808
804
  request,
809
805
  this.id,
810
806
  `${attemptId}-${state}`,
@@ -821,7 +817,7 @@ export class TelegramDecisionChannel implements HumanDecisionChannel {
821
817
  await this.store.recordDelivery(
822
818
  request,
823
819
  channelPath,
824
- v2DeliveryRecord(
820
+ deliveryRecord(
825
821
  request,
826
822
  this.id,
827
823
  `${attemptId}-confirmed`,
@@ -1234,29 +1230,26 @@ function parseCredentialConfig(value: unknown): DecisionCredentialConfig {
1234
1230
  return { schema: "pi-workflows.credentials.v1", telegram };
1235
1231
  }
1236
1232
 
1237
- type DeliveryV2 = Extract<
1238
- HumanDecisionDeliveryRecord,
1239
- { schema: "pi-workflows.human-decision-delivery.v2" }
1240
- >;
1233
+ type DeliveryRecord = HumanDecisionDeliveryRecord;
1241
1234
 
1242
- type DeliveryV2Extra = Partial<
1235
+ type DeliveryRecordExtra = Partial<
1243
1236
  Pick<
1244
- DeliveryV2,
1237
+ DeliveryRecord,
1245
1238
  "recipientIndex" | "partIndex" | "partCount" | "contentDigest" | "messageCount" | "errorCode"
1246
1239
  >
1247
1240
  >;
1248
1241
 
1249
- function v2DeliveryRecord(
1242
+ function deliveryRecord(
1250
1243
  request: HumanDecisionChannelRequest,
1251
1244
  channel: string,
1252
1245
  attemptId: string,
1253
- phase: DeliveryV2["phase"],
1254
- state: DeliveryV2["state"],
1246
+ phase: DeliveryRecord["phase"],
1247
+ state: DeliveryRecord["state"],
1255
1248
  createdAt: string,
1256
- extra: DeliveryV2Extra = {},
1257
- ): DeliveryV2 {
1249
+ extra: DeliveryRecordExtra = {},
1250
+ ): DeliveryRecord {
1258
1251
  return {
1259
- schema: "pi-workflows.human-decision-delivery.v2",
1252
+ schema: "pi-workflows.human-decision-delivery.v1",
1260
1253
  attemptId,
1261
1254
  decisionId: request.decisionId,
1262
1255
  requestDigest: request.requestDigest,
@@ -1271,37 +1264,14 @@ function v2DeliveryRecord(
1271
1264
  }
1272
1265
 
1273
1266
  function isCompleteDelivery(record: HumanDecisionDeliveryRecord): boolean {
1274
- return (
1275
- record.state === "confirmed" &&
1276
- (record.schema === "pi-workflows.human-decision-delivery.v1" || record.phase === "complete")
1277
- );
1267
+ return record.state === "confirmed" && record.phase === "complete";
1278
1268
  }
1279
1269
 
1280
1270
  function hasUnsettledDeliveryIntent(records: HumanDecisionDeliveryRecord[]): boolean {
1281
- const hasLegacyIntent = records.some(
1282
- (record) =>
1283
- record.schema === "pi-workflows.human-decision-delivery.v1" && record.state === "intent",
1284
- );
1285
- if (
1286
- hasLegacyIntent &&
1287
- !records.some(
1288
- (record) =>
1289
- record.schema === "pi-workflows.human-decision-delivery.v1" && record.state !== "intent",
1290
- )
1291
- ) {
1292
- return true;
1293
- }
1294
1271
  return records.some((record) => {
1295
- if (
1296
- record.schema !== "pi-workflows.human-decision-delivery.v2" ||
1297
- record.phase !== "part" ||
1298
- record.state !== "intent"
1299
- ) {
1300
- return false;
1301
- }
1272
+ if (record.phase !== "part" || record.state !== "intent") return false;
1302
1273
  return !records.some(
1303
1274
  (other) =>
1304
- other.schema === "pi-workflows.human-decision-delivery.v2" &&
1305
1275
  other.phase === "part" &&
1306
1276
  other.state !== "intent" &&
1307
1277
  other.recipientIndex === record.recipientIndex &&