@osolmaz/pi-workflows 0.15.1 → 0.15.3
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.
- package/dist/controllers/sqlite.d.ts +21 -0
- package/dist/controllers/sqlite.js +82 -1
- package/dist/controllers/sqlite.js.map +1 -1
- package/dist/extension/index.js +264 -139
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/session-delivery.d.ts +28 -0
- package/dist/extension/session-delivery.js +138 -0
- package/dist/extension/session-delivery.js.map +1 -0
- package/dist/extension/session-view.d.ts +16 -0
- package/dist/extension/session-view.js +119 -0
- package/dist/extension/session-view.js.map +1 -0
- package/dist/extension/widget.js +2 -1
- package/dist/extension/widget.js.map +1 -1
- package/dist/host/runner.d.ts +1 -0
- package/dist/host/runner.js +92 -26
- package/dist/host/runner.js.map +1 -1
- package/dist/host/state.d.ts +2 -0
- package/dist/host/state.js +9 -3
- package/dist/host/state.js.map +1 -1
- package/docs/2026-09-01-restore-session-delivery-controls-plan.md +139 -0
- package/docs/WORKFLOW_HOST.md +12 -5
- package/docs/WORKFLOW_STEP_MESSAGES.md +12 -5
- package/docs/workflows.md +12 -2
- package/herdr-plugin.toml +1 -1
- package/package.json +1 -1
- package/src/controllers/sqlite.ts +129 -1
- package/src/extension/index.ts +331 -159
- package/src/extension/session-delivery.ts +174 -0
- package/src/extension/session-view.ts +131 -0
- package/src/extension/widget.ts +3 -2
- package/src/host/runner.ts +103 -29
- package/src/host/state.ts +12 -3
package/dist/host/runner.js
CHANGED
|
@@ -402,15 +402,37 @@ export class WorkflowHost {
|
|
|
402
402
|
case "run.pause": {
|
|
403
403
|
const runId = requireRunId(request);
|
|
404
404
|
const active = this.activeRuns.get(runId);
|
|
405
|
-
if (active
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
405
|
+
if (active !== undefined) {
|
|
406
|
+
if (active.control === "pause") {
|
|
407
|
+
return { outcome: "adopted", receipt: { runId, status: "parked", paused: true } };
|
|
408
|
+
}
|
|
409
|
+
if (active.control === "handoff") {
|
|
410
|
+
const paused = this.queue.pauseParkedWorkflowRun({ runId });
|
|
411
|
+
return paused
|
|
412
|
+
? { outcome: "accepted", receipt: { runId, status: "parked", paused: true } }
|
|
413
|
+
: { outcome: "rejected", error: "Run handoff is not pausable" };
|
|
414
|
+
}
|
|
415
|
+
if (active.control !== undefined) {
|
|
416
|
+
return { outcome: "rejected", error: `Run is already handling ${active.control}` };
|
|
417
|
+
}
|
|
418
|
+
this.commitActivePause(active);
|
|
419
|
+
active.control = "pause";
|
|
420
|
+
afterCommit.push(() => void active.supervisor.stop("cancelled"));
|
|
421
|
+
return { outcome: "accepted", receipt: { runId, status: "parked", paused: true } };
|
|
422
|
+
}
|
|
423
|
+
const paused = this.queue.pauseParkedWorkflowRun({ runId });
|
|
424
|
+
return paused
|
|
425
|
+
? { outcome: "accepted", receipt: { runId, status: "parked", paused: true } }
|
|
426
|
+
: { outcome: "rejected", error: "Run is not pausable" };
|
|
411
427
|
}
|
|
412
428
|
case "run.resume": {
|
|
413
429
|
const runId = requireRunId(request);
|
|
430
|
+
if (this.queue.resumePausedInteraction({ runId })) {
|
|
431
|
+
return {
|
|
432
|
+
outcome: "accepted",
|
|
433
|
+
receipt: { runId, status: "parked", paused: false, waitingForInteraction: true },
|
|
434
|
+
};
|
|
435
|
+
}
|
|
414
436
|
if (this.activeRuns.has(runId) || this.pendingStarts.has(runId)) {
|
|
415
437
|
return { outcome: "adopted", receipt: { runId, active: true } };
|
|
416
438
|
}
|
|
@@ -448,17 +470,25 @@ export class WorkflowHost {
|
|
|
448
470
|
case "interaction.update": {
|
|
449
471
|
const payload = requireRecord(request.payload, "interaction update payload");
|
|
450
472
|
if (payload.claimPresentation === true) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
473
|
+
try {
|
|
474
|
+
const interaction = this.hostState.claimInteractionPresentation({
|
|
475
|
+
requestId: requireString(payload.requestId, "requestId"),
|
|
476
|
+
expectedRevision: requireNonNegativeInteger(request.expectedRevision, "expectedRevision"),
|
|
477
|
+
presenterId: request.clientId,
|
|
478
|
+
leaseMs: PRESENTATION_CLAIM_LEASE_MS,
|
|
479
|
+
});
|
|
480
|
+
return {
|
|
481
|
+
outcome: "accepted",
|
|
482
|
+
revision: interaction.revision,
|
|
483
|
+
receipt: interaction,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
if (errorMessage(error) === "Interactive request presentation claim conflict") {
|
|
488
|
+
return { outcome: "conflict", error: errorMessage(error) };
|
|
489
|
+
}
|
|
490
|
+
throw error;
|
|
491
|
+
}
|
|
462
492
|
}
|
|
463
493
|
if (typeof payload.sessionEntryId === "string") {
|
|
464
494
|
const interaction = this.hostState.markInteractionPresented({
|
|
@@ -585,11 +615,9 @@ export class WorkflowHost {
|
|
|
585
615
|
this.deliveryClaims.delete(claimId);
|
|
586
616
|
}
|
|
587
617
|
const claimId = randomUUID();
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
});
|
|
592
|
-
return claimId;
|
|
618
|
+
const expiresAt = now + DELIVERY_CLAIM_LEASE_MS;
|
|
619
|
+
this.deliveryClaims.set(claimId, { ...options, expiresAt });
|
|
620
|
+
return { claimId, claimExpiresAt: new Date(expiresAt).toISOString() };
|
|
593
621
|
}
|
|
594
622
|
deliveryClaim(claimId, clientId, kind, resourceId, targetSessionId) {
|
|
595
623
|
const claim = this.deliveryClaims.get(claimId);
|
|
@@ -604,8 +632,34 @@ export class WorkflowHost {
|
|
|
604
632
|
}
|
|
605
633
|
return claim;
|
|
606
634
|
}
|
|
635
|
+
validateDelivery(command, payload, kind) {
|
|
636
|
+
const resourceId = requireString(payload.resourceId, "resourceId");
|
|
637
|
+
const targetSessionId = requireString(payload.targetSessionId, "targetSessionId");
|
|
638
|
+
const claimId = requireString(payload.claimId, "claimId");
|
|
639
|
+
const claim = this.deliveryClaim(claimId, command.clientId, kind, resourceId, targetSessionId);
|
|
640
|
+
if (claim === undefined) {
|
|
641
|
+
return { outcome: "accepted", receipt: { claimId, live: false } };
|
|
642
|
+
}
|
|
643
|
+
const live = kind === "notification"
|
|
644
|
+
? this.queue.isWorkflowNotificationClaimLive({
|
|
645
|
+
notificationId: resourceId,
|
|
646
|
+
targetSessionId,
|
|
647
|
+
claimToken: claim.token,
|
|
648
|
+
})
|
|
649
|
+
: this.queue.isWorkflowTurnIntentClaimLive({
|
|
650
|
+
intentId: resourceId,
|
|
651
|
+
targetSessionId,
|
|
652
|
+
claimToken: claim.token,
|
|
653
|
+
});
|
|
654
|
+
if (!live)
|
|
655
|
+
this.deliveryClaims.delete(claimId);
|
|
656
|
+
return { outcome: "accepted", receipt: { claimId, live } };
|
|
657
|
+
}
|
|
607
658
|
claimNotification(command) {
|
|
608
659
|
const payload = requireRecord(command.payload, "notification claim payload");
|
|
660
|
+
if (payload.validateClaim === true) {
|
|
661
|
+
return this.validateDelivery(command, payload, "notification");
|
|
662
|
+
}
|
|
609
663
|
const targetSessionId = requireString(payload.targetSessionId, "targetSessionId");
|
|
610
664
|
const token = randomUUID();
|
|
611
665
|
const notification = this.queue.claimPendingWorkflowNotifications({
|
|
@@ -617,7 +671,7 @@ export class WorkflowHost {
|
|
|
617
671
|
if (notification === undefined) {
|
|
618
672
|
return { outcome: "accepted", receipt: { notification: null } };
|
|
619
673
|
}
|
|
620
|
-
const
|
|
674
|
+
const claim = this.rememberDeliveryClaim({
|
|
621
675
|
clientId: command.clientId,
|
|
622
676
|
token,
|
|
623
677
|
targetSessionId,
|
|
@@ -626,7 +680,7 @@ export class WorkflowHost {
|
|
|
626
680
|
});
|
|
627
681
|
return {
|
|
628
682
|
outcome: "accepted",
|
|
629
|
-
receipt: {
|
|
683
|
+
receipt: { ...claim, notification },
|
|
630
684
|
};
|
|
631
685
|
}
|
|
632
686
|
deliverNotification(command) {
|
|
@@ -650,6 +704,9 @@ export class WorkflowHost {
|
|
|
650
704
|
}
|
|
651
705
|
claimTurn(command) {
|
|
652
706
|
const payload = requireRecord(command.payload, "turn claim payload");
|
|
707
|
+
if (payload.validateClaim === true) {
|
|
708
|
+
return this.validateDelivery(command, payload, "turn");
|
|
709
|
+
}
|
|
653
710
|
const targetSessionId = requireString(payload.targetSessionId, "targetSessionId");
|
|
654
711
|
const token = randomUUID();
|
|
655
712
|
const intent = this.queue.claimEligibleWorkflowTurnIntents({
|
|
@@ -661,7 +718,7 @@ export class WorkflowHost {
|
|
|
661
718
|
if (intent === undefined) {
|
|
662
719
|
return { outcome: "accepted", receipt: { turn: null } };
|
|
663
720
|
}
|
|
664
|
-
const
|
|
721
|
+
const claim = this.rememberDeliveryClaim({
|
|
665
722
|
clientId: command.clientId,
|
|
666
723
|
token,
|
|
667
724
|
targetSessionId,
|
|
@@ -670,7 +727,7 @@ export class WorkflowHost {
|
|
|
670
727
|
});
|
|
671
728
|
return {
|
|
672
729
|
outcome: "accepted",
|
|
673
|
-
receipt: {
|
|
730
|
+
receipt: { ...claim, turn: intent },
|
|
674
731
|
};
|
|
675
732
|
}
|
|
676
733
|
resolveTurn(command) {
|
|
@@ -740,6 +797,9 @@ export class WorkflowHost {
|
|
|
740
797
|
if (interaction === undefined || interaction.kind !== "decision") {
|
|
741
798
|
return { outcome: "notFound", error: `Decision request not found: ${requestId}` };
|
|
742
799
|
}
|
|
800
|
+
if (this.queue.isWorkflowRunPaused(interaction.runId)) {
|
|
801
|
+
return { outcome: "conflict", error: "Workflow run is paused" };
|
|
802
|
+
}
|
|
743
803
|
const request = interaction.contract;
|
|
744
804
|
const response = payload.response;
|
|
745
805
|
const accepted = this.decisions.acceptSync(request, {
|
|
@@ -878,6 +938,9 @@ export class WorkflowHost {
|
|
|
878
938
|
if (interaction === undefined || interaction.runId !== runId) {
|
|
879
939
|
return { outcome: "notFound", error: `Interactive request not found: ${requestId}` };
|
|
880
940
|
}
|
|
941
|
+
if (this.queue.isWorkflowRunPaused(runId)) {
|
|
942
|
+
return { outcome: "conflict", error: "Workflow run is paused" };
|
|
943
|
+
}
|
|
881
944
|
const attemptId = requireString(payload.attempt, "attempt");
|
|
882
945
|
const nodeId = requireString(payload.step, "step");
|
|
883
946
|
const storedContract = requireRecord(interaction.contract, "interactive contract");
|
|
@@ -931,6 +994,9 @@ export class WorkflowHost {
|
|
|
931
994
|
if (current === undefined || current.runId !== requireRunId(request)) {
|
|
932
995
|
return { outcome: "notFound", error: `Interactive request not found: ${requestId}` };
|
|
933
996
|
}
|
|
997
|
+
if (this.queue.isWorkflowRunPaused(current.runId)) {
|
|
998
|
+
return { outcome: "conflict", error: "Workflow run is paused" };
|
|
999
|
+
}
|
|
934
1000
|
const attemptId = requireString(payload.attempt, "attempt");
|
|
935
1001
|
const nodeId = requireString(payload.step, "step");
|
|
936
1002
|
const storedContract = requireRecord(current.contract, "interactive contract");
|