@voltro/workflow 0.81.0 → 0.83.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
 
@@ -1652,7 +1656,9 @@ export declare const dueIntents: (store: AdmissionDataStore, now: number, limit:
1652
1656
  export { durableClock }
1653
1657
 
1654
1658
  /** Define a durable queue for concurrency-controlled side work. */
1655
- 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>);
1656
1662
 
1657
1663
  export { durableQueueModule }
1658
1664
 
@@ -1677,6 +1683,7 @@ export declare const enterWorkflowRunGenerationInTransaction: (tx: AdmissionData
1677
1683
  readonly row: Row;
1678
1684
  readonly previous: Row | undefined;
1679
1685
  readonly terminalEvent?: Row;
1686
+ readonly restartEvent?: Row;
1680
1687
  }>;
1681
1688
 
1682
1689
  /**
@@ -1767,6 +1774,9 @@ export declare const getCurrentWorkflowRunId: () => Effect.Effect<string | undef
1767
1774
  * the path it took before this feature existed. */
1768
1775
  export declare const getWorkflowFlowControl: (value: unknown) => ResolvedFlowControl | undefined;
1769
1776
 
1777
+ /** The journal cipher in effect, if any. */
1778
+ export declare const getWorkflowJournalCipher: () => FieldCipher | undefined;
1779
+
1770
1780
  /** The resolved schedule attached to a workflow definition, or undefined. */
1771
1781
  export declare const getWorkflowSchedule: (value: unknown) => ResolvedWorkflowSchedule | undefined;
1772
1782
 
@@ -1878,6 +1888,9 @@ export declare const isFinishExpired: (control: ResolvedFlowControl, run: {
1878
1888
  readonly terminal: boolean;
1879
1889
  }, now: number) => boolean;
1880
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
+
1881
1894
  /**
1882
1895
  * Has a pending row outlived `timeouts.start`?
1883
1896
  *
@@ -1898,6 +1911,9 @@ export declare const isStartExpired: (control: ResolvedFlowControl, pending: {
1898
1911
  */
1899
1912
  export declare const isUnacknowledgedSubmission: (cause: unknown) => boolean;
1900
1913
 
1914
+ /** Is the workflow of this name declared `encrypted`? */
1915
+ export declare const isWorkflowEncrypted: (name: string) => boolean;
1916
+
1901
1917
  export declare const isWorkflowWorkerLayer: (value: unknown) => value is WorkflowWorkerLayerBrand;
1902
1918
 
1903
1919
  /**
@@ -2007,6 +2023,36 @@ export declare const noteAdmissionSubmissionFailure: (tx: Pick<DataStore, "query
2007
2023
 
2008
2024
  export declare const noteIntentAttempt: (store: AdmissionDataStore, intent: PendingIntent, error: string, now: number) => Promise<void>;
2009
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
+
2010
2056
  /**
2011
2057
  * Park a `start(name, payload, { at })` as a DURABLE pending row.
2012
2058
  *
@@ -2244,7 +2290,13 @@ export declare const pooledConcurrencyKey: (pool: string | undefined, rawKey: st
2244
2290
  * This records intent, not engine acceptance, and does not release capacity. */
2245
2291
  export declare const prepareAdmissionSubmissionExecutionInTransaction: (tx: Pick<DataStore, "query" | "update">, id: string, proposedExecutionId: string) => Promise<AdmissionSubmission>;
2246
2292
 
2247
- /** 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
+ */
2248
2300
  export declare const processQueue: typeof durableQueueModule.process;
2249
2301
 
2250
2302
  /**
@@ -2321,9 +2373,64 @@ export declare const readWorkflowControlInTransaction: (tx: Pick<Store, "query">
2321
2373
  /** Strict read: a damaged record is not an absent report. */
2322
2374
  export declare const readWorkflowFailureRecord: (tx: Pick<DataStore, "query">, id: string) => Promise<WorkflowFailureRecord | undefined>;
2323
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
+
2324
2421
  /** Strict lookup shared by receipt owners; a malformed receipt is not absent. */
2325
2422
  export declare const readWorkflowStartReceipt: (store: Pick<DataStore, "query">, id: string) => Promise<WorkflowStartReceipt | undefined>;
2326
2423
 
2424
+ /** Observe an immutable operator request before allocating another pending intent.
2425
+ * Call inside the resident admission transaction. Old unbound receipts cannot
2426
+ * be adopted by a new request, even when their workflow and tenant match. */
2427
+ export declare const readWorkflowStartRequest: (store: Pick<DataStore, "query">, input: {
2428
+ readonly id: string;
2429
+ readonly workflowName: string;
2430
+ readonly tenantId: string | null;
2431
+ readonly requestFingerprint: string;
2432
+ }) => Promise<WorkflowStartReceipt | undefined>;
2433
+
2327
2434
  declare const ReceiptSchema: Schema.filter<Schema.Struct<{
2328
2435
  id: typeof Schema.String;
2329
2436
  workflowName: typeof Schema.String;
@@ -2350,6 +2457,7 @@ declare const ReceiptSchema: Schema.filter<Schema.Struct<{
2350
2457
  at: typeof Schema.Number;
2351
2458
  failureId: typeof Schema.NonEmptyString;
2352
2459
  }>]>>;
2460
+ requestFingerprint: Schema.optional<Schema.NullOr<Schema.filter<typeof Schema.String>>>;
2353
2461
  }>>;
2354
2462
 
2355
2463
  /** Reconcile under the resident admission lock. Only a terminal pre-admission
@@ -2663,7 +2771,7 @@ export declare interface ResolvedFlowControl {
2663
2771
  readonly finishMs?: number;
2664
2772
  };
2665
2773
  readonly onFailure?: string;
2666
- readonly encryptSteps?: boolean;
2774
+ readonly encrypted?: boolean;
2667
2775
  readonly cancelOn?: ReadonlyArray<ResolvedCancelOn>;
2668
2776
  }
2669
2777
 
@@ -2714,6 +2822,20 @@ export declare const resumeWorkflow: (store: AdmissionDataStore, workflowName: s
2714
2822
  * the terminal classification and retry history are reset. */
2715
2823
  export declare const retryDeadLetterIntent: (store: AdmissionDataStore, intentId: string, now: number) => Promise<boolean>;
2716
2824
 
2825
+ /**
2826
+ * Seal a JSON-safe value for a framework table. `undefined` / `null` stay as
2827
+ * they are — there is nothing in them to hide, and a nullable column must keep
2828
+ * reading as empty.
2829
+ */
2830
+ export declare const sealJournalValue: (value: unknown, what: string, binding?: FieldBinding) => unknown;
2831
+
2832
+ declare const Selection: Schema.Union<[Schema.Struct<{
2833
+ kind: Schema.Literal<["failed-steps"]>;
2834
+ }>, Schema.Struct<{
2835
+ kind: Schema.Literal<["from-step"]>;
2836
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
2837
+ }>]>;
2838
+
2717
2839
  export declare const sendWorkflowSignal: (input: {
2718
2840
  readonly store: WorkflowMessageStore;
2719
2841
  readonly recorder: WorkflowRunRecorderService;
@@ -2744,6 +2866,14 @@ export declare const serialiseWorkflowRowForWire: (row: Record<string, unknown>)
2744
2866
  */
2745
2867
  export declare const setSuspendSignalHintMs: (ms: number | undefined) => void;
2746
2868
 
2869
+ /**
2870
+ * Register the cipher encrypted workflows use — a cipher, or a function that
2871
+ * returns the current one. The boot paths pass the field-cipher registry's
2872
+ * getter, so the key `governancePlugin({ fieldEncryption })` registers is the
2873
+ * one used, including after it is set. `undefined` clears it.
2874
+ */
2875
+ export declare const setWorkflowJournalCipher: (cipher: FieldCipher | (() => FieldCipher | undefined) | undefined) => void;
2876
+
2747
2877
  export declare interface ShardLockHolder {
2748
2878
  readonly shardId: string;
2749
2879
  readonly mode: 'advisory' | 'row';
@@ -2925,6 +3055,10 @@ export declare type StallReason =
2925
3055
 
2926
3056
  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>>;
2927
3057
 
3058
+ /** The address the step journal's values are sealed to (`encrypted` via the
3059
+ * recorder's `encryptStepPayload`). ONE definition, shared with the CLI. */
3060
+ export declare const STEP_JOURNAL_BINDING: FieldBinding;
3061
+
2928
3062
  /**
2929
3063
  * Derive a deterministic idempotency key for the current workflow run,
2930
3064
  * scoped to `name`. Call it inside a `step()` so a retried external
@@ -3160,8 +3294,8 @@ export declare const takeAdmittedSubmissionInTransaction: (tx: AdmissionDataStor
3160
3294
  readonly stoppedAt: number | null;
3161
3295
  };
3162
3296
  readonly payload: unknown;
3163
- readonly executionId: string | null;
3164
3297
  readonly runId: string | null;
3298
+ readonly executionId: string | null;
3165
3299
  readonly failureId: string | null;
3166
3300
  readonly workflowName: string;
3167
3301
  readonly dispatchAttempts: number;
@@ -3171,8 +3305,8 @@ export declare const takeAdmittedSubmissionInTransaction: (tx: AdmissionDataStor
3171
3305
  readonly receiptsBound: boolean;
3172
3306
  readonly preparedExecutionId: string | null;
3173
3307
  readonly eviction: {
3174
- readonly executionId: string;
3175
3308
  readonly runId: string;
3309
+ readonly executionId: string;
3176
3310
  } | null;
3177
3311
  }>;
3178
3312
 
@@ -3193,6 +3327,13 @@ declare const Target: Schema.Struct<{
3193
3327
 
3194
3328
  export declare const truncateWorkflowValue: (value: unknown, bytes?: number) => unknown;
3195
3329
 
3330
+ /**
3331
+ * A stored value opened for an authorised reader, or left as stored when it
3332
+ * cannot be (no cipher, or sealed for another place). For read paths that must
3333
+ * not turn an unreadable value into a failed read of everything around it.
3334
+ */
3335
+ export declare const tryOpenJournalValue: (stored: unknown, what: string) => unknown;
3336
+
3196
3337
  export declare interface UnreadMessage extends ClusterMessageState {
3197
3338
  /** How long ago the last runner read it. */
3198
3339
  readonly unreadForMs: number;
@@ -3326,59 +3467,76 @@ export declare const withAdmissionTransaction: <A>(store: Pick<DataStore, "inser
3326
3467
  */
3327
3468
  export declare const withCompensation: typeof workflowModule.withCompensation;
3328
3469
 
3470
+ /** Resident withdrawal boundary used by the authorized Inspect host. A reservation wins even
3471
+ * before its first dispatch attempt; this never cancels accepted engine work.
3472
+ * The caller must hold the physical resident admission transaction throughout. */
3473
+ export declare const withdrawWorkflowRestartInTransaction: (tx: AdmissionDataStore, input: Parameters<typeof readWorkflowRestartCommandInTransaction>[1] & {
3474
+ readonly now: number;
3475
+ }) => Promise<{
3476
+ kind: "withdrawn";
3477
+ withdrawal: {
3478
+ readonly id: string;
3479
+ readonly tenantId: string | null;
3480
+ readonly runId: string;
3481
+ readonly workflowName: string;
3482
+ readonly selection: {
3483
+ readonly kind: "failed-steps";
3484
+ } | {
3485
+ readonly kind: "from-step";
3486
+ readonly stepName: string;
3487
+ };
3488
+ readonly withdrawnAt: Date;
3489
+ };
3490
+ reused: boolean;
3491
+ request?: never;
3492
+ } | {
3493
+ kind: "reserved";
3494
+ request: {
3495
+ readonly id: string;
3496
+ readonly tenantId: string | null;
3497
+ readonly token: string;
3498
+ readonly generation: string;
3499
+ readonly runId: string;
3500
+ readonly requestedAt: Date;
3501
+ readonly executionId: string;
3502
+ readonly workflowName: string;
3503
+ readonly selection: {
3504
+ readonly kind: "failed-steps";
3505
+ } | {
3506
+ readonly kind: "from-step";
3507
+ readonly stepName: string;
3508
+ };
3509
+ readonly dispatchAttempts: number;
3510
+ readonly enteredGeneration: string | null;
3511
+ readonly ledgerId: string;
3512
+ readonly eviction: {
3513
+ readonly runId: string;
3514
+ readonly executionId: string;
3515
+ } | null;
3516
+ readonly delivery: {
3517
+ readonly requestId: string;
3518
+ readonly restartToken: string | null;
3519
+ };
3520
+ readonly replies: readonly {
3521
+ readonly requestId: string;
3522
+ readonly replyId: string;
3523
+ }[];
3524
+ };
3525
+ withdrawal?: never;
3526
+ reused?: never;
3527
+ }>;
3528
+
3529
+ export declare const workflow: WorkflowFn;
3530
+
3329
3531
  /**
3330
- * Define a durable workflow.
3331
- *
3332
- * ── `idempotencyKey` is the execution's IDENTITY, permanently ──────────────
3333
- *
3334
- * Read this before choosing one. It is the single most expensive
3335
- * misunderstanding this API has produced — a downstream project lost a day and
3336
- * shipped a design around the wrong model, and the wording they read
3337
- * ("deduplicates concurrent invocations with the same input") is what led them
3338
- * there. It sounds like a WINDOW: dedupe while a run is in flight. It is not.
3339
- *
3340
- * The key IS the execution. A second start with the same key REPLAYS the first
3341
- * run's journaled result — forever, not just while it is running. Once the run
3342
- * completes the key is SPENT: a later, genuinely new invocation under that key
3343
- * is a silent no-op that returns the old output. Nothing errors and nothing
3344
- * logs, because from the engine's point of view you asked for a run it already
3345
- * has.
3346
- *
3347
- * ```ts
3348
- * const a = yield* wf.execute({ id: 'same' })
3349
- * const b = yield* wf.execute({ id: 'same' }) // ← does NOT run; replays `a`
3350
- * ```
3351
- *
3352
- * So a key must be **unique per unit of work you want to happen**, not per
3353
- * logical subject. `tour:${rowId}` is wrong if the tour can be re-narrated;
3354
- * `tour:${rowId}:${updatedAt}` is right, because every edit is a new unit of
3355
- * work with its own identity.
3356
- *
3357
- * ── And a flow-control key is a DIFFERENT thing ────────────────────────────
3358
- *
3359
- * The two get conflated, and conflating them is what makes "I need to re-arm a
3360
- * key" feel like a missing feature. It is not missing; it is two fields:
3361
- *
3362
- * • `idempotencyKey` — the execution's identity. Vary it per unit of work.
3363
- * • `debounce.key` / `singleton.key` / `concurrency.key` — the RESOURCE runs
3364
- * compete for. Keep it stable.
3365
- *
3366
- * "One job, fifteen minutes after the last edit, latest state wins" is then:
3367
- *
3368
- * ```ts
3369
- * workflow({
3370
- * name: 'tourNarration',
3371
- * payload: TourPayload,
3372
- * idempotencyKey: ({ rowId, editedAt }) => `tour:${rowId}:${editedAt}`, // varies
3373
- * debounce: { key: (p) => `tour:${p.rowId}`, period: '15 minutes' }, // stable
3374
- * })
3375
- * ```
3376
- *
3377
- * Twenty edits mint twenty identities; exactly one is ever admitted. There is
3378
- * no `restart: true` and no generation counter in this API because separating
3379
- * the two keys is the mechanism those would have been.
3532
+ * The address every sealed workflow value is bound to. ONE address for the
3533
+ * engine journal and the framework's own tables: the same value travels
3534
+ * between them (a run row's payload is the journal's encoding of it, a failure
3535
+ * report copies it), and a per-table binding would refuse the copy. What the
3536
+ * GCM tag still refuses is a value moved into an `.encrypted()` column or the
3537
+ * step journal, whose addresses differ.
3380
3538
  */
3381
- export declare const workflow: WorkflowFn;
3539
+ export declare const WORKFLOW_JOURNAL_BINDING: FieldBinding;
3382
3540
 
3383
3541
  declare type WorkflowBaseOptions = Parameters<WorkflowMake>[0];
3384
3542
 
@@ -3715,27 +3873,27 @@ export declare interface WorkflowFlowControl<Payload> {
3715
3873
  */
3716
3874
  readonly onFailure?: string;
3717
3875
  /**
3718
- * Encrypt this workflow's journaled step `input` / `output` / `errorCause`.
3876
+ * Encrypt every durable copy of this workflow's data.
3719
3877
  *
3720
- * `step({ input })` is written to `_voltro_workflow_run_steps` and rendered in
3721
- * the dashboard — a feature, and the reason people pass rich input. For a step
3722
- * carrying personal data it is also a SECOND COPY, outside the `.encrypted()`
3723
- * boundary the governance plugin establishes for tables. An app that carefully
3724
- * encrypted a column and then handed the same value to a step has it in
3725
- * plaintext one table over.
3878
+ * Covered: the payload, the result and a typed failure in the engine journal
3879
+ * (`cluster_messages`, `cluster_replies`), every step's result and typed
3880
+ * failure there, the run row (`_voltro_workflow_runs.payload` / `.output`),
3881
+ * the step journal (`_voltro_workflow_run_steps`), a queued start's payload
3882
+ * and a failure report's copy of the payload. The body sees the values it was
3883
+ * given, and a run replays unchanged.
3726
3884
  *
3727
- * Reuses the cipher `governancePlugin({ fieldEncryption })` already registers,
3728
- * so there is ONE key and one rotation story rather than a second scheme that
3729
- * only workflows know about. It is therefore a BOOT ERROR to declare this
3730
- * without that plugin configured: silently falling back to plaintext would be
3731
- * the worst outcome available — the declaration reads as protection and the
3732
- * rows are readable.
3885
+ * Not covered, and said here so it is not assumed: the `idempotencyKey`
3886
+ * (hashed into the execution id, but a primary key built from free text is
3887
+ * still that text in the engine's key column — derive it from ids), signal
3888
+ * and update payloads, and anything a step writes elsewhere itself.
3733
3889
  *
3734
- * Only the journal is affected. The values the workflow BODY sees are
3735
- * unchanged, and the durable execution journal `@effect/workflow` keeps is
3736
- * untouched (encrypting that would break replay).
3890
+ * Reuses the cipher `governancePlugin({ fieldEncryption })` registers — one
3891
+ * key, one rotation story. Declaring it without that plugin configured is a
3892
+ * BOOT ERROR: a plaintext fallback would leave the declaration reading as
3893
+ * protection while the rows are readable. A plaintext value written before
3894
+ * the declaration (a run already in flight) is still read.
3737
3895
  */
3738
- readonly encryptSteps?: boolean;
3896
+ readonly encrypted?: boolean;
3739
3897
  }
3740
3898
 
3741
3899
  /** Where the RESOLVED flow-control declaration hangs off a definition. A
@@ -3917,12 +4075,19 @@ export declare const workflowRestartRequestsTable: Table<"_voltro_workflow_resta
3917
4075
  readonly tenantId: ColumnBuilder<string | null, "text", boolean>;
3918
4076
  readonly delivery: ColumnBuilder<unknown, "json", boolean>;
3919
4077
  readonly replies: ColumnBuilder<unknown, "json", boolean>;
4078
+ readonly selection: ColumnBuilder<unknown, "json", boolean>;
3920
4079
  readonly eviction: ColumnBuilder<unknown, "json", boolean>;
3921
4080
  readonly requestedAt: ColumnBuilder<Date, "timestamp", boolean>;
3922
4081
  readonly dispatchAttempts: ColumnBuilder<number, "integer", true>;
3923
4082
  readonly enteredGeneration: ColumnBuilder<string | null, "text", boolean>;
3924
4083
  }>, false, never>;
3925
4084
 
4085
+ declare type WorkflowRestartSelection = typeof Selection.Type;
4086
+
4087
+ /** Durable negative authority. Never expire while delayed commands can reuse an
4088
+ * identity. Acceptance and reservation require the same resident admission lock. */
4089
+ export declare const workflowRestartWithdrawalsTable: SchemaTable;
4090
+
3926
4091
  export declare interface WorkflowRunCompletion {
3927
4092
  readonly owner: WorkflowRunGeneration;
3928
4093
  readonly now: number;
@@ -3954,7 +4119,7 @@ export declare interface WorkflowRunCompletion {
3954
4119
  */
3955
4120
  export declare type WorkflowRunEventType = 'run-started' | 'run-suspended' | 'run-resumed'
3956
4121
  /** Emitted when an operator re-drives a terminally-FAILED run from its
3957
- * durable journal (`voltro workflows redrive` / `ctx.workflows.redrive`).
4122
+ * durable journal (`voltro workflows restart` / `ctx.workflows.restart`).
3958
4123
  * `payload` carries `{ reason, activitiesReset }` — how many failed step
3959
4124
  * attempts were reset so they re-execute (completed steps replay). Distinct
3960
4125
  * from `run-resumed`, which continues a *suspended* run. */
@@ -4061,26 +4226,6 @@ export declare interface WorkflowRunRecorderOptions {
4061
4226
  * per run at a TERMINAL outcome (succeeded/failed), never on suspend. Absent →
4062
4227
  * no metrics, zero overhead. */
4063
4228
  readonly recordRun?: (outcome: WorkflowRunOutcome) => void;
4064
- /**
4065
- * Encrypt the `input` / `output` / `errorCause` a step journals.
4066
- *
4067
- * `step({ input })` is written to `_voltro_workflow_run_steps` and shown in
4068
- * the dashboard, which is a feature and the reason people pass rich input. For
4069
- * a step carrying personal data it is also a SECOND COPY, outside the
4070
- * `.encrypted()` boundary the governance plugin establishes for tables — so an
4071
- * app that carefully encrypted a column then handed the same value to a step
4072
- * has it in plaintext one table over.
4073
- *
4074
- * Supplied by the CLI from the cipher `governancePlugin({ fieldEncryption })`
4075
- * already registers, so there is ONE key and one rotation story rather than a
4076
- * second scheme for workflows. Absent → plaintext, exactly as before.
4077
- *
4078
- * Encryption happens on the WRITE and decryption on the inspect read, so the
4079
- * dashboard is unchanged for a viewer who is allowed to see it and the value
4080
- * at rest is not. A ciphertext that cannot be decrypted renders as a marker
4081
- * rather than as garbage — see `inspectWorkflow`.
4082
- */
4083
- readonly encryptStepPayload?: (value: unknown) => unknown;
4084
4229
  /**
4085
4230
  * How much this recorder writes per STEP — `'full'` (default) or `'coarse'`.
4086
4231
  *
@@ -4117,6 +4262,10 @@ export declare interface WorkflowRunRecorderService {
4117
4262
  readonly runId: string;
4118
4263
  readonly stepName: string;
4119
4264
  readonly attempt: number;
4265
+ /** The step runs inside an `encrypted: true` workflow: a recorder that
4266
+ * persists the step's values seals them (`sealJournalValue`, bound to
4267
+ * `STEP_JOURNAL_BINDING`). Absent or false: store them as they are. */
4268
+ readonly sealed?: boolean;
4120
4269
  /** User-declared structured input recorded into
4121
4270
  * `_voltro_workflow_run_steps.input`. The recorder truncates large
4122
4271
  * payloads — pass whatever the step needs to make sense in
@@ -4133,11 +4282,15 @@ export declare interface WorkflowRunRecorderService {
4133
4282
  }) => Promise<string | undefined>;
4134
4283
  readonly endStepSuccess: (input: {
4135
4284
  readonly stepRecordId: string;
4285
+ /** As on `startStep`. */
4286
+ readonly sealed?: boolean;
4136
4287
  readonly output: unknown;
4137
4288
  readonly durationMs: number;
4138
4289
  }) => Promise<void>;
4139
4290
  readonly endStepFailure: (input: {
4140
4291
  readonly stepRecordId: string;
4292
+ /** As on `startStep`. */
4293
+ readonly sealed?: boolean;
4141
4294
  readonly errorTag: string | null;
4142
4295
  readonly errorMessage: string;
4143
4296
  /** Structured Cause record — pretty form +
@@ -4262,6 +4415,12 @@ export declare interface WorkflowSingleton<Payload> {
4262
4415
 
4263
4416
  export declare type WorkflowStartReceipt = typeof ReceiptSchema.Type;
4264
4417
 
4418
+ /** Reusing a start identity must never overwrite or admit a different request. */
4419
+ export declare class WorkflowStartRequestConflictError extends Error {
4420
+ readonly _tag = "WorkflowStartRequestConflictError";
4421
+ constructor();
4422
+ }
4423
+
4265
4424
  export declare type WorkflowStartResolution = {
4266
4425
  readonly kind: 'pending';
4267
4426
  } | {