@voltro/workflow 0.80.0 → 0.82.0

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/index.d.ts CHANGED
@@ -8,6 +8,8 @@ import { DurableRateLimiter as durableRateLimiterModule } from '@effect/workflow
8
8
  import { Duration } from 'effect';
9
9
  import { Effect } from 'effect';
10
10
  import { FiberRef } from 'effect';
11
+ import { FieldBinding } from '@voltro/database';
12
+ import { FieldCipher } from '@voltro/database';
11
13
  import { FieldDefinitions } from '@voltro/database';
12
14
  import { Layer } from 'effect';
13
15
  import { ParseResult } from 'effect';
@@ -24,6 +26,8 @@ import { WorkflowEngine } from '@effect/workflow';
24
26
  import { Workflow as workflowModule } from '@effect/workflow';
25
27
  import { WorkflowParentClosePolicy } from '@voltro/protocol';
26
28
  import { WorkflowQueuedCancellationResult } from '@voltro/protocol';
29
+ import { WorkflowRestartObservation } from '@voltro/protocol';
30
+ import { WorkflowRestartTarget } from '@voltro/protocol';
27
31
 
28
32
  /** Dispatch an already committed admission using the same attempt/ACK boundary
29
33
  * as the drainer. Returning requires durable acceptance, not just a prepared ID.
@@ -586,11 +590,12 @@ export declare const beginWorkflowControlDispatchInTransaction: (tx: Store, id:
586
590
  readonly enteredAt: number;
587
591
  } | null;
588
592
  readonly generation: string;
593
+ readonly runId: string;
589
594
  readonly requestedAt: number;
590
595
  readonly executionId: string;
591
- readonly runId: string;
592
596
  readonly workflowName: string;
593
597
  readonly operation: "resume" | "cancel" | "terminate";
598
+ readonly dispatchAttempts: number;
594
599
  readonly owner: {
595
600
  readonly tenantId: string | null;
596
601
  } | null;
@@ -599,7 +604,6 @@ export declare const beginWorkflowControlDispatchInTransaction: (tx: Store, id:
599
604
  readonly replyId: string;
600
605
  readonly restartToken: string | null;
601
606
  } | null;
602
- readonly dispatchAttempts: number;
603
607
  };
604
608
  run?: never;
605
609
  } | {
@@ -626,11 +630,12 @@ export declare const beginWorkflowControlDispatchInTransaction: (tx: Store, id:
626
630
  readonly enteredAt: number;
627
631
  } | null;
628
632
  readonly generation: string;
633
+ readonly runId: string;
629
634
  readonly requestedAt: number;
630
635
  readonly executionId: string;
631
- readonly runId: string;
632
636
  readonly workflowName: string;
633
637
  readonly operation: "resume" | "cancel" | "terminate";
638
+ readonly dispatchAttempts: number;
634
639
  readonly owner: {
635
640
  readonly tenantId: string | null;
636
641
  } | null;
@@ -639,7 +644,6 @@ export declare const beginWorkflowControlDispatchInTransaction: (tx: Store, id:
639
644
  readonly replyId: string;
640
645
  readonly restartToken: string | null;
641
646
  } | null;
642
- readonly dispatchAttempts: number;
643
647
  };
644
648
  run?: never;
645
649
  } | {
@@ -666,11 +670,12 @@ export declare const beginWorkflowControlDispatchInTransaction: (tx: Store, id:
666
670
  readonly enteredAt: number;
667
671
  } | null;
668
672
  readonly generation: string;
673
+ readonly runId: string;
669
674
  readonly requestedAt: number;
670
675
  readonly executionId: string;
671
- readonly runId: string;
672
676
  readonly workflowName: string;
673
677
  readonly operation: "resume" | "cancel" | "terminate";
678
+ readonly dispatchAttempts: number;
674
679
  readonly owner: {
675
680
  readonly tenantId: string | null;
676
681
  } | null;
@@ -679,7 +684,6 @@ export declare const beginWorkflowControlDispatchInTransaction: (tx: Store, id:
679
684
  readonly replyId: string;
680
685
  readonly restartToken: string | null;
681
686
  } | null;
682
- readonly dispatchAttempts: number;
683
687
  };
684
688
  run?: never;
685
689
  } | {
@@ -706,11 +710,12 @@ export declare const beginWorkflowControlDispatchInTransaction: (tx: Store, id:
706
710
  readonly enteredAt: number;
707
711
  } | null;
708
712
  readonly generation: string;
713
+ readonly runId: string;
709
714
  readonly requestedAt: number;
710
715
  readonly executionId: string;
711
- readonly runId: string;
712
716
  readonly workflowName: string;
713
717
  readonly operation: "resume" | "cancel" | "terminate";
718
+ readonly dispatchAttempts: number;
714
719
  readonly owner: {
715
720
  readonly tenantId: string | null;
716
721
  } | null;
@@ -719,7 +724,6 @@ export declare const beginWorkflowControlDispatchInTransaction: (tx: Store, id:
719
724
  readonly replyId: string;
720
725
  readonly restartToken: string | null;
721
726
  } | null;
722
- readonly dispatchAttempts: number;
723
727
  };
724
728
  run: Readonly<Record<string, unknown>>;
725
729
  } | {
@@ -747,9 +751,9 @@ export declare const beginWorkflowControlDispatchInTransaction: (tx: Store, id:
747
751
  readonly enteredAt: number;
748
752
  } | null;
749
753
  generation: string;
754
+ runId: string;
750
755
  requestedAt: number;
751
756
  executionId: string;
752
- runId: string;
753
757
  workflowName: string;
754
758
  operation: "resume" | "cancel" | "terminate";
755
759
  owner: {
@@ -797,11 +801,12 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
797
801
  readonly enteredAt: number;
798
802
  } | null;
799
803
  readonly generation: string;
804
+ readonly runId: string;
800
805
  readonly requestedAt: number;
801
806
  readonly executionId: string;
802
- readonly runId: string;
803
807
  readonly workflowName: string;
804
808
  readonly operation: "resume" | "cancel" | "terminate";
809
+ readonly dispatchAttempts: number;
805
810
  readonly owner: {
806
811
  readonly tenantId: string | null;
807
812
  } | null;
@@ -810,7 +815,6 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
810
815
  readonly replyId: string;
811
816
  readonly restartToken: string | null;
812
817
  } | null;
813
- readonly dispatchAttempts: number;
814
818
  };
815
819
  } | {
816
820
  kind: "unknown";
@@ -836,11 +840,12 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
836
840
  readonly enteredAt: number;
837
841
  } | null;
838
842
  readonly generation: string;
843
+ readonly runId: string;
839
844
  readonly requestedAt: number;
840
845
  readonly executionId: string;
841
- readonly runId: string;
842
846
  readonly workflowName: string;
843
847
  readonly operation: "resume" | "cancel" | "terminate";
848
+ readonly dispatchAttempts: number;
844
849
  readonly owner: {
845
850
  readonly tenantId: string | null;
846
851
  } | null;
@@ -849,7 +854,6 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
849
854
  readonly replyId: string;
850
855
  readonly restartToken: string | null;
851
856
  } | null;
852
- readonly dispatchAttempts: number;
853
857
  };
854
858
  } | {
855
859
  kind: "superseded";
@@ -875,11 +879,12 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
875
879
  readonly enteredAt: number;
876
880
  } | null;
877
881
  readonly generation: string;
882
+ readonly runId: string;
878
883
  readonly requestedAt: number;
879
884
  readonly executionId: string;
880
- readonly runId: string;
881
885
  readonly workflowName: string;
882
886
  readonly operation: "resume" | "cancel" | "terminate";
887
+ readonly dispatchAttempts: number;
883
888
  readonly owner: {
884
889
  readonly tenantId: string | null;
885
890
  } | null;
@@ -888,7 +893,6 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
888
893
  readonly replyId: string;
889
894
  readonly restartToken: string | null;
890
895
  } | null;
891
- readonly dispatchAttempts: number;
892
896
  };
893
897
  } | {
894
898
  kind: "not-suspended";
@@ -914,11 +918,12 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
914
918
  readonly enteredAt: number;
915
919
  } | null;
916
920
  readonly generation: string;
921
+ readonly runId: string;
917
922
  readonly requestedAt: number;
918
923
  readonly executionId: string;
919
- readonly runId: string;
920
924
  readonly workflowName: string;
921
925
  readonly operation: "resume" | "cancel" | "terminate";
926
+ readonly dispatchAttempts: number;
922
927
  readonly owner: {
923
928
  readonly tenantId: string | null;
924
929
  } | null;
@@ -927,7 +932,6 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
927
932
  readonly replyId: string;
928
933
  readonly restartToken: string | null;
929
934
  } | null;
930
- readonly dispatchAttempts: number;
931
935
  };
932
936
  } | {
933
937
  kind: "bound";
@@ -953,11 +957,12 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
953
957
  readonly enteredAt: number;
954
958
  } | null;
955
959
  readonly generation: string;
960
+ readonly runId: string;
956
961
  readonly requestedAt: number;
957
962
  readonly executionId: string;
958
- readonly runId: string;
959
963
  readonly workflowName: string;
960
964
  readonly operation: "resume" | "cancel" | "terminate";
965
+ readonly dispatchAttempts: number;
961
966
  readonly owner: {
962
967
  readonly tenantId: string | null;
963
968
  } | null;
@@ -966,7 +971,6 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
966
971
  readonly replyId: string;
967
972
  readonly restartToken: string | null;
968
973
  } | null;
969
- readonly dispatchAttempts: number;
970
974
  };
971
975
  } | {
972
976
  kind: "waiting-journal";
@@ -992,11 +996,12 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
992
996
  readonly enteredAt: number;
993
997
  } | null;
994
998
  readonly generation: string;
999
+ readonly runId: string;
995
1000
  readonly requestedAt: number;
996
1001
  readonly executionId: string;
997
- readonly runId: string;
998
1002
  readonly workflowName: string;
999
1003
  readonly operation: "resume" | "cancel" | "terminate";
1004
+ readonly dispatchAttempts: number;
1000
1005
  readonly owner: {
1001
1006
  readonly tenantId: string | null;
1002
1007
  } | null;
@@ -1005,7 +1010,6 @@ export declare const bindWorkflowResumeJournalInTransaction: (tx: Store, id: str
1005
1010
  readonly replyId: string;
1006
1011
  readonly restartToken: string | null;
1007
1012
  } | null;
1008
- readonly dispatchAttempts: number;
1009
1013
  };
1010
1014
  }>;
1011
1015
 
@@ -1368,6 +1372,28 @@ export declare const CurrentWorkflowReplayShape: FiberRef.FiberRef<ReplayShapeTr
1368
1372
  * `Effect.locally(CurrentWorkflowRunId, runId)`. */
1369
1373
  export declare const CurrentWorkflowRunId: FiberRef.FiberRef<string | undefined>;
1370
1374
 
1375
+ /**
1376
+ * FiberRef carrying the name of the `step()` whose body is running — set by
1377
+ * `step()` around its execute, `undefined` everywhere else.
1378
+ *
1379
+ * It exists so work a step does OUTSIDE the process can be deduplicated
1380
+ * without the author spelling a key: a call to another app of the project made
1381
+ * inside a step is re-sent when the step is retried after its reply was lost,
1382
+ * and `(executionId, step, call)` is the identity that makes the second send a
1383
+ * replay. Read through `currentWorkflowStep`, never directly.
1384
+ */
1385
+ export declare const CurrentWorkflowStep: FiberRef.FiberRef<string | undefined>;
1386
+
1387
+ /**
1388
+ * The step this fiber is running inside, with its durable execution — or
1389
+ * `undefined` outside a recorded step. Stable across replays and retries of the
1390
+ * same step, and different for every other step and every other run.
1391
+ */
1392
+ export declare const currentWorkflowStep: Effect.Effect<{
1393
+ readonly executionId: string;
1394
+ readonly step: string;
1395
+ } | undefined>;
1396
+
1371
1397
  /**
1372
1398
  * Stop retrying an intent whose exact payload can never start.
1373
1399
  *
@@ -1630,7 +1656,9 @@ export declare const dueIntents: (store: AdmissionDataStore, now: number, limit:
1630
1656
  export { durableClock }
1631
1657
 
1632
1658
  /** Define a durable queue for concurrency-controlled side work. */
1633
- export declare const durableQueue: typeof durableQueueModule.make;
1659
+ export declare const durableQueue: typeof durableQueueModule.make & (<Options extends Parameters<typeof durableQueueModule.make>[0] & {
1660
+ readonly encrypted?: boolean;
1661
+ }>(options: Options) => ReturnType<typeof durableQueueModule.make>);
1634
1662
 
1635
1663
  export { durableQueueModule }
1636
1664
 
@@ -1655,6 +1683,7 @@ export declare const enterWorkflowRunGenerationInTransaction: (tx: AdmissionData
1655
1683
  readonly row: Row;
1656
1684
  readonly previous: Row | undefined;
1657
1685
  readonly terminalEvent?: Row;
1686
+ readonly restartEvent?: Row;
1658
1687
  }>;
1659
1688
 
1660
1689
  /**
@@ -1745,6 +1774,9 @@ export declare const getCurrentWorkflowRunId: () => Effect.Effect<string | undef
1745
1774
  * the path it took before this feature existed. */
1746
1775
  export declare const getWorkflowFlowControl: (value: unknown) => ResolvedFlowControl | undefined;
1747
1776
 
1777
+ /** The journal cipher in effect, if any. */
1778
+ export declare const getWorkflowJournalCipher: () => FieldCipher | undefined;
1779
+
1748
1780
  /** The resolved schedule attached to a workflow definition, or undefined. */
1749
1781
  export declare const getWorkflowSchedule: (value: unknown) => ResolvedWorkflowSchedule | undefined;
1750
1782
 
@@ -1856,6 +1888,9 @@ export declare const isFinishExpired: (control: ResolvedFlowControl, run: {
1856
1888
  readonly terminal: boolean;
1857
1889
  }, now: number) => boolean;
1858
1890
 
1891
+ /** A value in one of the cipher's envelopes (`enc:v1:` / `enc:v2:`). */
1892
+ export declare const isSealedJournalValue: (value: unknown) => value is string;
1893
+
1859
1894
  /**
1860
1895
  * Has a pending row outlived `timeouts.start`?
1861
1896
  *
@@ -1876,6 +1911,9 @@ export declare const isStartExpired: (control: ResolvedFlowControl, pending: {
1876
1911
  */
1877
1912
  export declare const isUnacknowledgedSubmission: (cause: unknown) => boolean;
1878
1913
 
1914
+ /** Is the workflow of this name declared `encrypted`? */
1915
+ export declare const isWorkflowEncrypted: (name: string) => boolean;
1916
+
1879
1917
  export declare const isWorkflowWorkerLayer: (value: unknown) => value is WorkflowWorkerLayerBrand;
1880
1918
 
1881
1919
  /**
@@ -1985,6 +2023,36 @@ export declare const noteAdmissionSubmissionFailure: (tx: Pick<DataStore, "query
1985
2023
 
1986
2024
  export declare const noteIntentAttempt: (store: AdmissionDataStore, intent: PendingIntent, error: string, now: number) => Promise<void>;
1987
2025
 
2026
+ /** An error message as stored, opened (see {@link sealErrorText}). */
2027
+ export declare const openErrorText: (stored: string | null | undefined) => string | null;
2028
+
2029
+ /** The inverse of {@link sealEventPayload}, tolerant like every read path. */
2030
+ export declare const openEventPayload: <P>(payload: P) => P;
2031
+
2032
+ /** The inverse of {@link sealJournalValue}; a plaintext value passes through. */
2033
+ export declare const openJournalValue: (stored: unknown, what: string, binding?: FieldBinding) => unknown;
2034
+
2035
+ /**
2036
+ * A stored payload with its sealed field values opened — what an authorised
2037
+ * reader (the app's live run rows, `listRuns`, the dashboard, a failure
2038
+ * handler) receives. At-rest protection is the point; hiding the value from the
2039
+ * caller the access rules already let in is not. A plaintext payload returns
2040
+ * unchanged.
2041
+ */
2042
+ export declare const openPayloadFields: (payload: unknown, what: string) => unknown;
2043
+
2044
+ /**
2045
+ * A stored workflow row — a run, a step or a run event — with every sealed
2046
+ * value opened, for a reader the access rules already let in: the live rows
2047
+ * pushed to subscribers, the client's read built-ins, the inspect surface.
2048
+ *
2049
+ * ONE function for every reader, because a reader that opened one column and
2050
+ * not the next is how a feature ends up "encrypted" in one view and garbage in
2051
+ * another. Tolerant throughout: a value that will not open (no cipher here, a
2052
+ * rotated key) stays as stored rather than failing the whole read.
2053
+ */
2054
+ export declare const openWorkflowRow: <R>(row: R) => R;
2055
+
1988
2056
  /**
1989
2057
  * Park a `start(name, payload, { at })` as a DURABLE pending row.
1990
2058
  *
@@ -2222,7 +2290,13 @@ export declare const pooledConcurrencyKey: (pool: string | undefined, rawKey: st
2222
2290
  * This records intent, not engine acceptance, and does not release capacity. */
2223
2291
  export declare const prepareAdmissionSubmissionExecutionInTransaction: (tx: Pick<DataStore, "query" | "update">, id: string, proposedExecutionId: string) => Promise<AdmissionSubmission>;
2224
2292
 
2225
- /** Enqueue work and await the durable queue worker's result. */
2293
+ /**
2294
+ * Enqueue work and await the durable queue worker's result.
2295
+ *
2296
+ * From inside an encrypted workflow the queue must be encrypted too — its
2297
+ * journal would otherwise hold the workflow's data in plaintext one hop away,
2298
+ * the same reason an encrypted workflow's `onFailure` handler must be.
2299
+ */
2226
2300
  export declare const processQueue: typeof durableQueueModule.process;
2227
2301
 
2228
2302
  /**
@@ -2299,6 +2373,51 @@ export declare const readWorkflowControlInTransaction: (tx: Pick<Store, "query">
2299
2373
  /** Strict read: a damaged record is not an absent report. */
2300
2374
  export declare const readWorkflowFailureRecord: (tx: Pick<DataStore, "query">, id: string) => Promise<WorkflowFailureRecord | undefined>;
2301
2375
 
2376
+ /** Read an accepted command before observing the journal again. Its captured
2377
+ * replies remain authoritative after reset or body entry. This grants no new
2378
+ * dispatch permission: dispatch must still recheck the resident generation. */
2379
+ declare const readWorkflowRestartCommandInTransaction: (tx: AdmissionDataStore, input: {
2380
+ readonly id: string;
2381
+ readonly runId: string;
2382
+ readonly workflowName: string;
2383
+ readonly tenantId: string | null;
2384
+ readonly selection: WorkflowRestartSelection;
2385
+ }) => Promise<{
2386
+ readonly id: string;
2387
+ readonly tenantId: string | null;
2388
+ readonly token: string;
2389
+ readonly generation: string;
2390
+ readonly runId: string;
2391
+ readonly requestedAt: Date;
2392
+ readonly executionId: string;
2393
+ readonly workflowName: string;
2394
+ readonly selection: {
2395
+ readonly kind: "failed-steps";
2396
+ } | {
2397
+ readonly kind: "from-step";
2398
+ readonly stepName: string;
2399
+ };
2400
+ readonly dispatchAttempts: number;
2401
+ readonly enteredGeneration: string | null;
2402
+ readonly ledgerId: string;
2403
+ readonly eviction: {
2404
+ readonly runId: string;
2405
+ readonly executionId: string;
2406
+ } | null;
2407
+ readonly delivery: {
2408
+ readonly requestId: string;
2409
+ readonly restartToken: string | null;
2410
+ };
2411
+ readonly replies: readonly {
2412
+ readonly requestId: string;
2413
+ readonly replyId: string;
2414
+ }[];
2415
+ } | undefined>;
2416
+
2417
+ /** Read-only reconciliation. No observation of engine replies, admission,
2418
+ * journal reset, or dispatch is performed. Internal ownership data stays here. */
2419
+ export declare const readWorkflowRestartObservationInTransaction: (tx: AdmissionDataStore, target: WorkflowRestartTarget) => Promise<WorkflowRestartObservation>;
2420
+
2302
2421
  /** Strict lookup shared by receipt owners; a malformed receipt is not absent. */
2303
2422
  export declare const readWorkflowStartReceipt: (store: Pick<DataStore, "query">, id: string) => Promise<WorkflowStartReceipt | undefined>;
2304
2423
 
@@ -2641,7 +2760,7 @@ export declare interface ResolvedFlowControl {
2641
2760
  readonly finishMs?: number;
2642
2761
  };
2643
2762
  readonly onFailure?: string;
2644
- readonly encryptSteps?: boolean;
2763
+ readonly encrypted?: boolean;
2645
2764
  readonly cancelOn?: ReadonlyArray<ResolvedCancelOn>;
2646
2765
  }
2647
2766
 
@@ -2692,6 +2811,20 @@ export declare const resumeWorkflow: (store: AdmissionDataStore, workflowName: s
2692
2811
  * the terminal classification and retry history are reset. */
2693
2812
  export declare const retryDeadLetterIntent: (store: AdmissionDataStore, intentId: string, now: number) => Promise<boolean>;
2694
2813
 
2814
+ /**
2815
+ * Seal a JSON-safe value for a framework table. `undefined` / `null` stay as
2816
+ * they are — there is nothing in them to hide, and a nullable column must keep
2817
+ * reading as empty.
2818
+ */
2819
+ export declare const sealJournalValue: (value: unknown, what: string, binding?: FieldBinding) => unknown;
2820
+
2821
+ declare const Selection: Schema.Union<[Schema.Struct<{
2822
+ kind: Schema.Literal<["failed-steps"]>;
2823
+ }>, Schema.Struct<{
2824
+ kind: Schema.Literal<["from-step"]>;
2825
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
2826
+ }>]>;
2827
+
2695
2828
  export declare const sendWorkflowSignal: (input: {
2696
2829
  readonly store: WorkflowMessageStore;
2697
2830
  readonly recorder: WorkflowRunRecorderService;
@@ -2722,6 +2855,14 @@ export declare const serialiseWorkflowRowForWire: (row: Record<string, unknown>)
2722
2855
  */
2723
2856
  export declare const setSuspendSignalHintMs: (ms: number | undefined) => void;
2724
2857
 
2858
+ /**
2859
+ * Register the cipher encrypted workflows use — a cipher, or a function that
2860
+ * returns the current one. The boot paths pass the field-cipher registry's
2861
+ * getter, so the key `governancePlugin({ fieldEncryption })` registers is the
2862
+ * one used, including after it is set. `undefined` clears it.
2863
+ */
2864
+ export declare const setWorkflowJournalCipher: (cipher: FieldCipher | (() => FieldCipher | undefined) | undefined) => void;
2865
+
2725
2866
  export declare interface ShardLockHolder {
2726
2867
  readonly shardId: string;
2727
2868
  readonly mode: 'advisory' | 'row';
@@ -2903,6 +3044,10 @@ export declare type StallReason =
2903
3044
 
2904
3045
  export declare const step: <R, Success extends Schema.Schema.Any = typeof Schema.Void, Error extends Schema.Schema.All = typeof Schema.Never>(options: StepOptions<R, Success, Error>) => ReturnType<typeof stepModule.make<R, Success, Error>>;
2905
3046
 
3047
+ /** The address the step journal's values are sealed to (`encrypted` via the
3048
+ * recorder's `encryptStepPayload`). ONE definition, shared with the CLI. */
3049
+ export declare const STEP_JOURNAL_BINDING: FieldBinding;
3050
+
2906
3051
  /**
2907
3052
  * Derive a deterministic idempotency key for the current workflow run,
2908
3053
  * scoped to `name`. Call it inside a `step()` so a retried external
@@ -3138,8 +3283,8 @@ export declare const takeAdmittedSubmissionInTransaction: (tx: AdmissionDataStor
3138
3283
  readonly stoppedAt: number | null;
3139
3284
  };
3140
3285
  readonly payload: unknown;
3141
- readonly executionId: string | null;
3142
3286
  readonly runId: string | null;
3287
+ readonly executionId: string | null;
3143
3288
  readonly failureId: string | null;
3144
3289
  readonly workflowName: string;
3145
3290
  readonly dispatchAttempts: number;
@@ -3149,8 +3294,8 @@ export declare const takeAdmittedSubmissionInTransaction: (tx: AdmissionDataStor
3149
3294
  readonly receiptsBound: boolean;
3150
3295
  readonly preparedExecutionId: string | null;
3151
3296
  readonly eviction: {
3152
- readonly executionId: string;
3153
3297
  readonly runId: string;
3298
+ readonly executionId: string;
3154
3299
  } | null;
3155
3300
  }>;
3156
3301
 
@@ -3171,6 +3316,13 @@ declare const Target: Schema.Struct<{
3171
3316
 
3172
3317
  export declare const truncateWorkflowValue: (value: unknown, bytes?: number) => unknown;
3173
3318
 
3319
+ /**
3320
+ * A stored value opened for an authorised reader, or left as stored when it
3321
+ * cannot be (no cipher, or sealed for another place). For read paths that must
3322
+ * not turn an unreadable value into a failed read of everything around it.
3323
+ */
3324
+ export declare const tryOpenJournalValue: (stored: unknown, what: string) => unknown;
3325
+
3174
3326
  export declare interface UnreadMessage extends ClusterMessageState {
3175
3327
  /** How long ago the last runner read it. */
3176
3328
  readonly unreadForMs: number;
@@ -3304,59 +3456,76 @@ export declare const withAdmissionTransaction: <A>(store: Pick<DataStore, "inser
3304
3456
  */
3305
3457
  export declare const withCompensation: typeof workflowModule.withCompensation;
3306
3458
 
3459
+ /** Resident withdrawal boundary used by the authorized Inspect host. A reservation wins even
3460
+ * before its first dispatch attempt; this never cancels accepted engine work.
3461
+ * The caller must hold the physical resident admission transaction throughout. */
3462
+ export declare const withdrawWorkflowRestartInTransaction: (tx: AdmissionDataStore, input: Parameters<typeof readWorkflowRestartCommandInTransaction>[1] & {
3463
+ readonly now: number;
3464
+ }) => Promise<{
3465
+ kind: "withdrawn";
3466
+ withdrawal: {
3467
+ readonly id: string;
3468
+ readonly tenantId: string | null;
3469
+ readonly runId: string;
3470
+ readonly workflowName: string;
3471
+ readonly selection: {
3472
+ readonly kind: "failed-steps";
3473
+ } | {
3474
+ readonly kind: "from-step";
3475
+ readonly stepName: string;
3476
+ };
3477
+ readonly withdrawnAt: Date;
3478
+ };
3479
+ reused: boolean;
3480
+ request?: never;
3481
+ } | {
3482
+ kind: "reserved";
3483
+ request: {
3484
+ readonly id: string;
3485
+ readonly tenantId: string | null;
3486
+ readonly token: string;
3487
+ readonly generation: string;
3488
+ readonly runId: string;
3489
+ readonly requestedAt: Date;
3490
+ readonly executionId: string;
3491
+ readonly workflowName: string;
3492
+ readonly selection: {
3493
+ readonly kind: "failed-steps";
3494
+ } | {
3495
+ readonly kind: "from-step";
3496
+ readonly stepName: string;
3497
+ };
3498
+ readonly dispatchAttempts: number;
3499
+ readonly enteredGeneration: string | null;
3500
+ readonly ledgerId: string;
3501
+ readonly eviction: {
3502
+ readonly runId: string;
3503
+ readonly executionId: string;
3504
+ } | null;
3505
+ readonly delivery: {
3506
+ readonly requestId: string;
3507
+ readonly restartToken: string | null;
3508
+ };
3509
+ readonly replies: readonly {
3510
+ readonly requestId: string;
3511
+ readonly replyId: string;
3512
+ }[];
3513
+ };
3514
+ withdrawal?: never;
3515
+ reused?: never;
3516
+ }>;
3517
+
3518
+ export declare const workflow: WorkflowFn;
3519
+
3307
3520
  /**
3308
- * Define a durable workflow.
3309
- *
3310
- * ── `idempotencyKey` is the execution's IDENTITY, permanently ──────────────
3311
- *
3312
- * Read this before choosing one. It is the single most expensive
3313
- * misunderstanding this API has produced — a downstream project lost a day and
3314
- * shipped a design around the wrong model, and the wording they read
3315
- * ("deduplicates concurrent invocations with the same input") is what led them
3316
- * there. It sounds like a WINDOW: dedupe while a run is in flight. It is not.
3317
- *
3318
- * The key IS the execution. A second start with the same key REPLAYS the first
3319
- * run's journaled result — forever, not just while it is running. Once the run
3320
- * completes the key is SPENT: a later, genuinely new invocation under that key
3321
- * is a silent no-op that returns the old output. Nothing errors and nothing
3322
- * logs, because from the engine's point of view you asked for a run it already
3323
- * has.
3324
- *
3325
- * ```ts
3326
- * const a = yield* wf.execute({ id: 'same' })
3327
- * const b = yield* wf.execute({ id: 'same' }) // ← does NOT run; replays `a`
3328
- * ```
3329
- *
3330
- * So a key must be **unique per unit of work you want to happen**, not per
3331
- * logical subject. `tour:${rowId}` is wrong if the tour can be re-narrated;
3332
- * `tour:${rowId}:${updatedAt}` is right, because every edit is a new unit of
3333
- * work with its own identity.
3334
- *
3335
- * ── And a flow-control key is a DIFFERENT thing ────────────────────────────
3336
- *
3337
- * The two get conflated, and conflating them is what makes "I need to re-arm a
3338
- * key" feel like a missing feature. It is not missing; it is two fields:
3339
- *
3340
- * • `idempotencyKey` — the execution's identity. Vary it per unit of work.
3341
- * • `debounce.key` / `singleton.key` / `concurrency.key` — the RESOURCE runs
3342
- * compete for. Keep it stable.
3343
- *
3344
- * "One job, fifteen minutes after the last edit, latest state wins" is then:
3345
- *
3346
- * ```ts
3347
- * workflow({
3348
- * name: 'tourNarration',
3349
- * payload: TourPayload,
3350
- * idempotencyKey: ({ rowId, editedAt }) => `tour:${rowId}:${editedAt}`, // varies
3351
- * debounce: { key: (p) => `tour:${p.rowId}`, period: '15 minutes' }, // stable
3352
- * })
3353
- * ```
3354
- *
3355
- * Twenty edits mint twenty identities; exactly one is ever admitted. There is
3356
- * no `restart: true` and no generation counter in this API because separating
3357
- * the two keys is the mechanism those would have been.
3521
+ * The address every sealed workflow value is bound to. ONE address for the
3522
+ * engine journal and the framework's own tables: the same value travels
3523
+ * between them (a run row's payload is the journal's encoding of it, a failure
3524
+ * report copies it), and a per-table binding would refuse the copy. What the
3525
+ * GCM tag still refuses is a value moved into an `.encrypted()` column or the
3526
+ * step journal, whose addresses differ.
3358
3527
  */
3359
- export declare const workflow: WorkflowFn;
3528
+ export declare const WORKFLOW_JOURNAL_BINDING: FieldBinding;
3360
3529
 
3361
3530
  declare type WorkflowBaseOptions = Parameters<WorkflowMake>[0];
3362
3531
 
@@ -3693,27 +3862,27 @@ export declare interface WorkflowFlowControl<Payload> {
3693
3862
  */
3694
3863
  readonly onFailure?: string;
3695
3864
  /**
3696
- * Encrypt this workflow's journaled step `input` / `output` / `errorCause`.
3865
+ * Encrypt every durable copy of this workflow's data.
3697
3866
  *
3698
- * `step({ input })` is written to `_voltro_workflow_run_steps` and rendered in
3699
- * the dashboard — a feature, and the reason people pass rich input. For a step
3700
- * carrying personal data it is also a SECOND COPY, outside the `.encrypted()`
3701
- * boundary the governance plugin establishes for tables. An app that carefully
3702
- * encrypted a column and then handed the same value to a step has it in
3703
- * plaintext one table over.
3867
+ * Covered: the payload, the result and a typed failure in the engine journal
3868
+ * (`cluster_messages`, `cluster_replies`), every step's result and typed
3869
+ * failure there, the run row (`_voltro_workflow_runs.payload` / `.output`),
3870
+ * the step journal (`_voltro_workflow_run_steps`), a queued start's payload
3871
+ * and a failure report's copy of the payload. The body sees the values it was
3872
+ * given, and a run replays unchanged.
3704
3873
  *
3705
- * Reuses the cipher `governancePlugin({ fieldEncryption })` already registers,
3706
- * so there is ONE key and one rotation story rather than a second scheme that
3707
- * only workflows know about. It is therefore a BOOT ERROR to declare this
3708
- * without that plugin configured: silently falling back to plaintext would be
3709
- * the worst outcome available — the declaration reads as protection and the
3710
- * rows are readable.
3874
+ * Not covered, and said here so it is not assumed: the `idempotencyKey`
3875
+ * (hashed into the execution id, but a primary key built from free text is
3876
+ * still that text in the engine's key column — derive it from ids), signal
3877
+ * and update payloads, and anything a step writes elsewhere itself.
3711
3878
  *
3712
- * Only the journal is affected. The values the workflow BODY sees are
3713
- * unchanged, and the durable execution journal `@effect/workflow` keeps is
3714
- * untouched (encrypting that would break replay).
3879
+ * Reuses the cipher `governancePlugin({ fieldEncryption })` registers — one
3880
+ * key, one rotation story. Declaring it without that plugin configured is a
3881
+ * BOOT ERROR: a plaintext fallback would leave the declaration reading as
3882
+ * protection while the rows are readable. A plaintext value written before
3883
+ * the declaration (a run already in flight) is still read.
3715
3884
  */
3716
- readonly encryptSteps?: boolean;
3885
+ readonly encrypted?: boolean;
3717
3886
  }
3718
3887
 
3719
3888
  /** Where the RESOLVED flow-control declaration hangs off a definition. A
@@ -3895,12 +4064,19 @@ export declare const workflowRestartRequestsTable: Table<"_voltro_workflow_resta
3895
4064
  readonly tenantId: ColumnBuilder<string | null, "text", boolean>;
3896
4065
  readonly delivery: ColumnBuilder<unknown, "json", boolean>;
3897
4066
  readonly replies: ColumnBuilder<unknown, "json", boolean>;
4067
+ readonly selection: ColumnBuilder<unknown, "json", boolean>;
3898
4068
  readonly eviction: ColumnBuilder<unknown, "json", boolean>;
3899
4069
  readonly requestedAt: ColumnBuilder<Date, "timestamp", boolean>;
3900
4070
  readonly dispatchAttempts: ColumnBuilder<number, "integer", true>;
3901
4071
  readonly enteredGeneration: ColumnBuilder<string | null, "text", boolean>;
3902
4072
  }>, false, never>;
3903
4073
 
4074
+ declare type WorkflowRestartSelection = typeof Selection.Type;
4075
+
4076
+ /** Durable negative authority. Never expire while delayed commands can reuse an
4077
+ * identity. Acceptance and reservation require the same resident admission lock. */
4078
+ export declare const workflowRestartWithdrawalsTable: SchemaTable;
4079
+
3904
4080
  export declare interface WorkflowRunCompletion {
3905
4081
  readonly owner: WorkflowRunGeneration;
3906
4082
  readonly now: number;
@@ -3932,7 +4108,7 @@ export declare interface WorkflowRunCompletion {
3932
4108
  */
3933
4109
  export declare type WorkflowRunEventType = 'run-started' | 'run-suspended' | 'run-resumed'
3934
4110
  /** Emitted when an operator re-drives a terminally-FAILED run from its
3935
- * durable journal (`voltro workflows redrive` / `ctx.workflows.redrive`).
4111
+ * durable journal (`voltro workflows restart` / `ctx.workflows.restart`).
3936
4112
  * `payload` carries `{ reason, activitiesReset }` — how many failed step
3937
4113
  * attempts were reset so they re-execute (completed steps replay). Distinct
3938
4114
  * from `run-resumed`, which continues a *suspended* run. */
@@ -4039,26 +4215,6 @@ export declare interface WorkflowRunRecorderOptions {
4039
4215
  * per run at a TERMINAL outcome (succeeded/failed), never on suspend. Absent →
4040
4216
  * no metrics, zero overhead. */
4041
4217
  readonly recordRun?: (outcome: WorkflowRunOutcome) => void;
4042
- /**
4043
- * Encrypt the `input` / `output` / `errorCause` a step journals.
4044
- *
4045
- * `step({ input })` is written to `_voltro_workflow_run_steps` and shown in
4046
- * the dashboard, which is a feature and the reason people pass rich input. For
4047
- * a step carrying personal data it is also a SECOND COPY, outside the
4048
- * `.encrypted()` boundary the governance plugin establishes for tables — so an
4049
- * app that carefully encrypted a column then handed the same value to a step
4050
- * has it in plaintext one table over.
4051
- *
4052
- * Supplied by the CLI from the cipher `governancePlugin({ fieldEncryption })`
4053
- * already registers, so there is ONE key and one rotation story rather than a
4054
- * second scheme for workflows. Absent → plaintext, exactly as before.
4055
- *
4056
- * Encryption happens on the WRITE and decryption on the inspect read, so the
4057
- * dashboard is unchanged for a viewer who is allowed to see it and the value
4058
- * at rest is not. A ciphertext that cannot be decrypted renders as a marker
4059
- * rather than as garbage — see `inspectWorkflow`.
4060
- */
4061
- readonly encryptStepPayload?: (value: unknown) => unknown;
4062
4218
  /**
4063
4219
  * How much this recorder writes per STEP — `'full'` (default) or `'coarse'`.
4064
4220
  *
@@ -4095,6 +4251,10 @@ export declare interface WorkflowRunRecorderService {
4095
4251
  readonly runId: string;
4096
4252
  readonly stepName: string;
4097
4253
  readonly attempt: number;
4254
+ /** The step runs inside an `encrypted: true` workflow: a recorder that
4255
+ * persists the step's values seals them (`sealJournalValue`, bound to
4256
+ * `STEP_JOURNAL_BINDING`). Absent or false: store them as they are. */
4257
+ readonly sealed?: boolean;
4098
4258
  /** User-declared structured input recorded into
4099
4259
  * `_voltro_workflow_run_steps.input`. The recorder truncates large
4100
4260
  * payloads — pass whatever the step needs to make sense in
@@ -4111,11 +4271,15 @@ export declare interface WorkflowRunRecorderService {
4111
4271
  }) => Promise<string | undefined>;
4112
4272
  readonly endStepSuccess: (input: {
4113
4273
  readonly stepRecordId: string;
4274
+ /** As on `startStep`. */
4275
+ readonly sealed?: boolean;
4114
4276
  readonly output: unknown;
4115
4277
  readonly durationMs: number;
4116
4278
  }) => Promise<void>;
4117
4279
  readonly endStepFailure: (input: {
4118
4280
  readonly stepRecordId: string;
4281
+ /** As on `startStep`. */
4282
+ readonly sealed?: boolean;
4119
4283
  readonly errorTag: string | null;
4120
4284
  readonly errorMessage: string;
4121
4285
  /** Structured Cause record — pretty form +