@fabricorg/platform-host 8.2.0 → 8.4.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.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { ActionDefinition, ActionId, ActionStatus, ActorType, AdapterCircuitBreakerProvider, AdapterImplementation, AgentActionPolicyEvaluator, AgentActionRiskTier, AgentActionRoute, AssetEventEnvelope, EvidencePacketReference, ExecutionAttestation, ExecutionPrincipal, ExternalReconciliation, FabricRuntimeServices, GovernanceRuntimeEvidence, ModuleRegistry, MutationFootprint, MutationGovernanceResolver, PolicyObligation, PolicyObligationStatus, PolicyOutcome, RuntimePolicyDefinition, canonicalJson } from "@fabricorg/platform";
1
+ import { ActionDefinition, ActionId, ActionStatus, ActorType, AdapterCircuitBreakerProvider, AdapterImplementation, AgentActionPolicyEvaluator, AgentActionRiskTier, AgentActionRoute, AssetEventEnvelope, EvidencePacketReference, ExecutionAttestation, ExecutionPrincipal, ExternalReconciliation, FabricRuntimeServices, GovernanceRuntimeEvidence, ModuleRegistry, MutationFootprint, MutationGovernanceResolver, PolicyObligation, PolicyObligationStatus, PolicyOutcome, RuntimePolicyDefinition, StagedInvocationRequest, canonicalJson } from "@fabricorg/platform";
2
2
  import { AssemblyLockfile } from "@fabricorg/assembly";
3
3
  //#region src/observability.d.ts
4
4
  /** Version of the vendor-neutral health/readiness contract. */
@@ -218,6 +218,14 @@ export declare class ActionAdmissionError extends Error {
218
218
  /** The check's own code, present only for `refused`. */
219
219
  refusalCode?: string | undefined);
220
220
  }
221
+ export type SubmissionRefusal = "entitlement" | "authorization";
222
+ /** A definitive authorization-boundary refusal before an invocation is created. */
223
+ export declare class SubmissionRefusalError extends Error {
224
+ readonly actionId: string;
225
+ readonly refusal: SubmissionRefusal;
226
+ readonly code: "ACTION_SUBMISSION_REFUSED";
227
+ constructor(actionId: string, refusal: SubmissionRefusal, message: string);
228
+ }
221
229
  export type ExternalCompletionRefusal = "still_executing" | "not_found" | "not_awaiting" | "immediate_contract" | "reference_mismatch" | "provider_mismatch" | "invalid_completion" | "result_invalid" | "redaction_failed";
222
230
  /**
223
231
  * Why a completion was not applied, typed so a webhook handler can answer
@@ -259,6 +267,12 @@ export interface FinalizationCheckpoint {
259
267
  payload: import("@fabricorg/platform").FinalizationJson;
260
268
  };
261
269
  skipped: number[];
270
+ /** Handler requests retained so finalization replay stages the identical batch. */
271
+ stagedInvocations?: readonly import("@fabricorg/platform").StagedInvocationRequest[];
272
+ }
273
+ export interface StagedInvocationOrigin {
274
+ parentInvocationId: string;
275
+ stageKey: string;
262
276
  }
263
277
  export interface ActionInvocationRecord {
264
278
  id: string;
@@ -278,6 +292,8 @@ export interface ActionInvocationRecord {
278
292
  parameterDigestAlgorithm?: "fabric-canonical-json-sha256-v1";
279
293
  idempotencyActorId?: string;
280
294
  idempotencyAuthorizationBindingId?: string;
295
+ /** Host-only identity for a staged child; public submissions cannot set it. */
296
+ stagedOrigin?: StagedInvocationOrigin;
281
297
  provenance?: Omit<InvocationProvenance, "traceAttributes">;
282
298
  authorizationBinding?: AuthorizationBinding;
283
299
  executionReason?: ActionExecutionReason;
@@ -479,7 +495,7 @@ export interface AdapterInvocationRecord {
479
495
  createdAt: Date;
480
496
  updatedAt: Date;
481
497
  }
482
- export interface CreateActionInvocationInput extends Omit<ActionInvocationRecord, "createdAt" | "updatedAt" | "attemptCount" | "leaseOwner" | "leaseExpiresAt" | "leaseToken"> {}
498
+ export interface CreateActionInvocationInput extends Omit<ActionInvocationRecord, "createdAt" | "updatedAt" | "attemptCount" | "leaseOwner" | "leaseExpiresAt" | "leaseToken" | "stagedOrigin"> {}
483
499
  /** Scope of an adapter checkpoint within its owning action invocation. */
484
500
  export interface AdapterInvocationScope {
485
501
  tenantId: string;
@@ -537,6 +553,8 @@ export interface PlatformHostStore<TDb> {
537
553
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
538
554
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
539
555
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
556
+ /** Resolve Host-owned staged identity within one tenant and space. */
557
+ getActionInvocationByStagedOrigin?(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
540
558
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
541
559
  appendPolicyEvaluation(record: PolicyEvaluationRecord): Promise<void>;
542
560
  createAdapterInvocation(record: AdapterInvocationRecord): Promise<void>;
@@ -668,6 +686,135 @@ export interface PlatformHostMutationTransaction<TDb> {
668
686
  * governance evidence cannot share the event/verdict transaction.
669
687
  */
670
688
  appendExternalReconciliation?: GovernancePlatformHostStore<TDb>["appendExternalReconciliation"];
689
+ /** Atomically retain an immutable staged batch with the parent mutation. */
690
+ stageInvocations?(records: readonly StagedInvocationRecord[]): Promise<void>;
691
+ }
692
+ export type StagedInvocationStatus = "pending" | "dispatched" | "rejected" | "dead_lettered" | "cancelled";
693
+ export interface StagedInvocationRecord {
694
+ id: string;
695
+ tenantId: string;
696
+ spaceId: string;
697
+ parentInvocationId: string;
698
+ parentActionId: ActionId;
699
+ key: string;
700
+ actionId: ActionId;
701
+ expectedActionVersion: number;
702
+ parameters: Record<string, unknown>;
703
+ parameterDigest: string;
704
+ batchDigest: string;
705
+ status: StagedInvocationStatus;
706
+ /**
707
+ * The child's id, recorded by the first settlement whose attempt knew it.
708
+ * A settlement without an id leaves an earlier one in place. After a crash
709
+ * between child creation and settlement it stays absent until a later
710
+ * attempt finds the child by its staged origin.
711
+ */
712
+ childInvocationId?: string;
713
+ attemptCount: number;
714
+ availableAt: Date;
715
+ leaseOwner?: string;
716
+ leaseExpiresAt?: Date;
717
+ leaseToken: number;
718
+ lastError?: string;
719
+ createdAt: Date;
720
+ updatedAt: Date;
721
+ }
722
+ export interface ClaimStagedInvocationsInput {
723
+ workerId: string;
724
+ leaseDurationMs: number;
725
+ limit?: number;
726
+ tenantId?: string;
727
+ spaceId?: string;
728
+ now?: Date;
729
+ }
730
+ export interface CreateStagedActionInvocationInput {
731
+ stageId: string;
732
+ workerId: string;
733
+ leaseToken: number;
734
+ now?: Date;
735
+ invocation: CreateActionInvocationInput & {
736
+ stagedOrigin: StagedInvocationOrigin;
737
+ };
738
+ }
739
+ export type CreateStagedActionInvocationResult = {
740
+ kind: "created" | "existing";
741
+ invocation: ActionInvocationRecord;
742
+ } | {
743
+ kind: "lease_lost";
744
+ };
745
+ /** Optional durable capability for transactional staging and post-completion dispatch. */
746
+ export interface StagedInvocationPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
747
+ readonly transactionalStagedInvocations?: boolean;
748
+ getActionInvocationByStagedOrigin(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
749
+ /** Return the existing origin or create it only while this exact stage lease is live. */
750
+ createActionInvocationForStagedLease(input: CreateStagedActionInvocationInput): Promise<CreateStagedActionInvocationResult>;
751
+ claimStagedInvocations(input: ClaimStagedInvocationsInput): Promise<StagedInvocationRecord[]>;
752
+ /**
753
+ * The settlements below are each one compare-and-set on a pending row's
754
+ * lease owner and token, and release the lease. A settlement that carries
755
+ * `childInvocationId` records it; one without leaves a recorded id in place.
756
+ */
757
+ markStagedInvocationDispatched(input: {
758
+ id: string;
759
+ workerId: string;
760
+ leaseToken: number;
761
+ childInvocationId: string;
762
+ now?: Date;
763
+ }): Promise<boolean>;
764
+ markStagedInvocationRejected(input: {
765
+ id: string;
766
+ workerId: string;
767
+ leaseToken: number;
768
+ error: string;
769
+ now?: Date;
770
+ }): Promise<boolean>;
771
+ markStagedInvocationFailed(input: {
772
+ id: string;
773
+ workerId: string;
774
+ leaseToken: number;
775
+ error: string;
776
+ availableAt: Date;
777
+ deadLetter: boolean;
778
+ childInvocationId?: string;
779
+ now?: Date;
780
+ }): Promise<boolean>;
781
+ renewStagedInvocationLease(input: {
782
+ id: string;
783
+ workerId: string;
784
+ leaseToken: number;
785
+ leaseDurationMs: number;
786
+ now?: Date;
787
+ }): Promise<boolean>;
788
+ /**
789
+ * Replace the lease token of a pending row this worker leases with a larger
790
+ * one and extend the lease, in one compare-and-set on owner, token and an
791
+ * unexpired lease. Returns the new token, or `undefined` when the lease no
792
+ * longer matches. Tokens only grow, as with claims, so a later claim never
793
+ * hands an old token out again. Afterwards the old token fails the
794
+ * child-creation fence, renewal and every settlement. A store must order
795
+ * the rotation after any child creation the fence already admitted under
796
+ * the old token (PostgreSQL: the fence's `FOR SHARE` row lock makes the
797
+ * rotation wait). Host rotates before the lookup that decides a stage
798
+ * whose attempt holds no child id.
799
+ */
800
+ rotateStagedInvocationLease(input: {
801
+ id: string;
802
+ workerId: string;
803
+ leaseToken: number;
804
+ leaseDurationMs: number;
805
+ now?: Date;
806
+ }): Promise<number | undefined>;
807
+ cancelStagedInvocationsForFailedParents(input?: {
808
+ tenantId?: string;
809
+ spaceId?: string;
810
+ now?: Date;
811
+ }): Promise<number>;
812
+ listStagedInvocations(input: {
813
+ tenantId: string;
814
+ spaceId: string;
815
+ parentInvocationId?: string;
816
+ statuses?: readonly StagedInvocationStatus[];
817
+ }): Promise<StagedInvocationRecord[]>;
671
818
  }
672
819
  export type OutboxStatus = "pending" | "published" | "dead_letter";
673
820
  export type EventPayloadClassification = "public" | "internal" | "confidential" | "restricted";
@@ -1013,7 +1160,7 @@ export interface ActionAuthorizationInput {
1013
1160
  actorId: string;
1014
1161
  actorType: ActorType;
1015
1162
  }
1016
- export type KnownActionExecutionReason = "initial" | "approval_resume" | "recovery" | "offline_replay";
1163
+ export type KnownActionExecutionReason = "initial" | "approval_resume" | "recovery" | "offline_replay" | "staged_dispatch";
1017
1164
  export type ActionExecutionReason = KnownActionExecutionReason | (string & {});
1018
1165
  /**
1019
1166
  * Trusted execution-boundary authorization input.
@@ -1027,6 +1174,14 @@ export interface ActionExecutionAuthorizationInput extends ActionAuthorizationIn
1027
1174
  invocation: Readonly<ActionInvocationRecord>;
1028
1175
  executionReason: ActionExecutionReason;
1029
1176
  }
1177
+ /** Trusted authorization input for deciding a parked approval. */
1178
+ export interface ActionApprovalAuthorizationInput extends ActionAuthorizationInput {
1179
+ decision: ActionApprovalDecision;
1180
+ /** The parked invocation being decided. */
1181
+ actionInvocationId: string;
1182
+ /** The durable (post-redaction) invocation record as read before the decision transition. */
1183
+ invocation: Readonly<ActionInvocationRecord>;
1184
+ }
1030
1185
  /**
1031
1186
  * Input to `resolvePolicies`. Carries the invocation identity and the
1032
1187
  * schema-parsed durable (post-redaction) parameters so a resolver can choose
@@ -1046,9 +1201,7 @@ export interface PlatformHostAuthorization {
1046
1201
  */
1047
1202
  authorizeExecution?(input: ActionExecutionAuthorizationInput): Promise<boolean>;
1048
1203
  /** Optional distinct authorization boundary for human/system approval decisions. */
1049
- authorizeApproval?(input: ActionAuthorizationInput & {
1050
- decision: ActionApprovalDecision;
1051
- }): Promise<boolean>;
1204
+ authorizeApproval?(input: ActionApprovalAuthorizationInput): Promise<boolean>;
1052
1205
  /**
1053
1206
  * Authorization for `requeueOutboxDeadLetter`. There is no action to fall
1054
1207
  * back to, so when this is absent every requeue is refused as
@@ -1154,6 +1307,14 @@ export interface PlatformActionWorkerOptions<TDb> {
1154
1307
  * on every cycle, and run the sweep elsewhere.
1155
1308
  */
1156
1309
  sweepOverdueCompletions?: boolean;
1310
+ /** Dispatch staged descendants each cycle. Defaults to on when a registered action declares them. */
1311
+ dispatchStagedInvocations?: boolean;
1312
+ stagedInvocationBatchSize?: number;
1313
+ stagedInvocationLeaseDurationMs?: number;
1314
+ stagedInvocationLeaseRenewalIntervalMs?: number;
1315
+ stagedInvocationSubmissionTimeoutMs?: number;
1316
+ stagedInvocationMaxAttempts?: number;
1317
+ stagedInvocationRetryDelayMs?: (attempt: number) => number;
1157
1318
  }
1158
1319
  export interface PlatformActionWorkerCycleResult {
1159
1320
  claimed: number;
@@ -1167,6 +1328,38 @@ export interface PlatformActionWorkerCycleResult {
1167
1328
  * The row stays leased and a later claim retries it; excluded from failed.
1168
1329
  */
1169
1330
  deferred?: number;
1331
+ staged?: StagedInvocationDispatchResult;
1332
+ }
1333
+ export interface StagedInvocationDispatchInput {
1334
+ workerId: string;
1335
+ limit?: number;
1336
+ leaseDurationMs?: number;
1337
+ leaseRenewalIntervalMs?: number;
1338
+ submissionTimeoutMs?: number;
1339
+ maxAttempts?: number;
1340
+ retryDelayMs?: (attempt: number) => number;
1341
+ tenantId?: string;
1342
+ spaceId?: string;
1343
+ /**
1344
+ * Receives a failed staged-origin lookup and any error that ends one claimed
1345
+ * row's processing early (a store error from renewal, rotation or
1346
+ * settlement). The sweep counts that row as failed and continues.
1347
+ */
1348
+ onError?: (error: unknown) => void;
1349
+ }
1350
+ /**
1351
+ * Counters for one sweep. Each claimed row is counted once, as dispatched,
1352
+ * rejected or failed. `failed` includes rows settled for retry, rows whose
1353
+ * lease was lost before settlement, and rows whose processing threw;
1354
+ * `deadLettered` counts the subset of `failed` settled as `dead_lettered`.
1355
+ */
1356
+ export interface StagedInvocationDispatchResult {
1357
+ claimed: number;
1358
+ dispatched: number;
1359
+ rejected: number;
1360
+ failed: number;
1361
+ deadLettered: number;
1362
+ cancelled: number;
1170
1363
  }
1171
1364
  /** Origin of error text redacted before Host records or returns it. */
1172
1365
  export interface ErrorTextRedactionContext {
@@ -1369,6 +1562,10 @@ export interface PlatformHostOptions<TDb> {
1369
1562
  }
1370
1563
  export interface GovernedActionHost {
1371
1564
  submitAction(input: SubmitActionInput): Promise<SubmitActionResult>;
1565
+ /** Whether this Host's registry contains an action with a non-empty staged-descendant allowlist. */
1566
+ readonly hasStagedInvocationActions?: boolean;
1567
+ /** One bounded lease-fenced sweep of completed parents' staged descendants. */
1568
+ dispatchStagedInvocations?(input: StagedInvocationDispatchInput): Promise<StagedInvocationDispatchResult>;
1372
1569
  executeInvocation(actionInvocationId: string, tenantId: string, spaceId: string, options?: ExecuteInvocationOptions): Promise<ExecuteActionResult>;
1373
1570
  resumeApprovedInvocation(actionInvocationId: string, tenantId: string, spaceId: string, decision: ActionApprovalDecision): Promise<ExecuteActionResult>;
1374
1571
  recordExecutionAttestation(actionInvocationId: string, tenantId: string, spaceId: string, attestation: ExecutionAttestation): Promise<void>;
@@ -1415,9 +1612,10 @@ export interface MemoryPlatformHostTransactionProvider<TDb> {
1415
1612
  snapshot(db: TDb): TDb;
1416
1613
  restore(db: TDb, snapshot: TDb): void;
1417
1614
  }
1418
- export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb> {
1615
+ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
1419
1616
  readonly db: TDb;
1420
1617
  readonly transactionalOutbox: boolean;
1618
+ readonly transactionalStagedInvocations: boolean;
1421
1619
  transactionWithEvents?: <TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>) => Promise<TResult>;
1422
1620
  private adapterCommit;
1423
1621
  private transactionTail;
@@ -1429,6 +1627,7 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1429
1627
  readonly executionAttestations: ExecutionAttestationRecord[];
1430
1628
  readonly externalReconciliations: ExternalReconciliationRecord[];
1431
1629
  readonly outbox: OutboxRecord[];
1630
+ readonly stagedInvocations: StagedInvocationRecord[];
1432
1631
  /**
1433
1632
  * Requeue evidence is append-only audit history, so unlike the ledger
1434
1633
  * arrays above, which tests have always inspected and seeded directly, it
@@ -1440,7 +1639,10 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1440
1639
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
1441
1640
  private runTransactionWithEvents;
1442
1641
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
1642
+ createActionInvocationForStagedLease(input: CreateStagedActionInvocationInput): Promise<CreateStagedActionInvocationResult>;
1643
+ private applyStagedBatch;
1443
1644
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
1645
+ getActionInvocationByStagedOrigin(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
1444
1646
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
1445
1647
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1446
1648
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
@@ -1504,6 +1706,60 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1504
1706
  claimActionInvocations(input: ClaimActionInvocationsInput): Promise<ActionInvocationRecord[]>;
1505
1707
  updateLeasedActionInvocation(input: UpdateLeasedActionInvocationInput): Promise<boolean>;
1506
1708
  renewActionInvocationLease(input: RenewActionInvocationLeaseInput): Promise<boolean>;
1709
+ claimStagedInvocations(input: ClaimStagedInvocationsInput): Promise<StagedInvocationRecord[]>;
1710
+ markStagedInvocationDispatched(input: {
1711
+ id: string;
1712
+ workerId: string;
1713
+ leaseToken: number;
1714
+ childInvocationId: string;
1715
+ now?: Date;
1716
+ }): Promise<boolean>;
1717
+ markStagedInvocationRejected(input: {
1718
+ id: string;
1719
+ workerId: string;
1720
+ leaseToken: number;
1721
+ error: string;
1722
+ now?: Date;
1723
+ }): Promise<boolean>;
1724
+ markStagedInvocationFailed(input: {
1725
+ id: string;
1726
+ workerId: string;
1727
+ leaseToken: number;
1728
+ error: string;
1729
+ availableAt: Date;
1730
+ deadLetter: boolean;
1731
+ childInvocationId?: string;
1732
+ now?: Date;
1733
+ }): Promise<boolean>;
1734
+ renewStagedInvocationLease(input: {
1735
+ id: string;
1736
+ workerId: string;
1737
+ leaseToken: number;
1738
+ leaseDurationMs: number;
1739
+ now?: Date;
1740
+ }): Promise<boolean>;
1741
+ /** Await-free, like the fenced create above, so a create runs wholly before or after it. */
1742
+ rotateStagedInvocationLease(input: {
1743
+ id: string;
1744
+ workerId: string;
1745
+ leaseToken: number;
1746
+ leaseDurationMs: number;
1747
+ now?: Date;
1748
+ }): Promise<number | undefined>;
1749
+ cancelStagedInvocationsForFailedParents(input?: {
1750
+ tenantId?: string;
1751
+ spaceId?: string;
1752
+ now?: Date;
1753
+ }): Promise<number>;
1754
+ listStagedInvocations(input: {
1755
+ tenantId: string;
1756
+ spaceId: string;
1757
+ parentInvocationId?: string;
1758
+ statuses?: readonly StagedInvocationStatus[];
1759
+ }): Promise<StagedInvocationRecord[]>;
1760
+ private leasedStagedInvocation;
1761
+ /** Mirrors PostgreSQL: a missing child id keeps the recorded one; `lastError` is replaced. */
1762
+ private settleStagedInvocation;
1507
1763
  listEvents(tenantId: string, spaceId: string): Promise<AssetEventEnvelope[]>;
1508
1764
  }
1509
1765
  //#endregion
@@ -1571,16 +1827,20 @@ export interface PostgresPlatformHostTransactionProvider<TDb> {
1571
1827
  run<TResult>(run: (context: PostgresPlatformHostTransactionContext<TDb>) => Promise<TResult>): Promise<TResult>;
1572
1828
  }
1573
1829
  /** Durable mutation-pipeline ledger for Databricks Lakebase or standard Postgres. */
1574
- export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb> {
1830
+ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
1575
1831
  readonly db: TDb;
1576
1832
  private readonly sql;
1577
1833
  readonly transactionWithEvents?: AtomicMutationPlatformHostStore<TDb>["transactionWithEvents"];
1578
1834
  readonly transactionalOutbox: boolean;
1835
+ readonly transactionalStagedInvocations: boolean;
1579
1836
  constructor(db: TDb, sql: PlatformHostSqlClient, transactionProvider?: PostgresPlatformHostTransactionProvider<TDb>);
1580
1837
  ensureSchema(): Promise<void>;
1581
1838
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
1582
1839
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
1840
+ createActionInvocationForStagedLease(input: CreateStagedActionInvocationInput): Promise<CreateStagedActionInvocationResult>;
1841
+ stageInvocations(records: readonly StagedInvocationRecord[]): Promise<void>;
1583
1842
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
1843
+ getActionInvocationByStagedOrigin(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
1584
1844
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
1585
1845
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1586
1846
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
@@ -1631,6 +1891,66 @@ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatfo
1631
1891
  spaceId: string;
1632
1892
  outboxId?: string;
1633
1893
  }): Promise<OutboxRequeueRecord[]>;
1894
+ claimStagedInvocations(input: ClaimStagedInvocationsInput): Promise<StagedInvocationRecord[]>;
1895
+ markStagedInvocationDispatched(input: {
1896
+ id: string;
1897
+ workerId: string;
1898
+ leaseToken: number;
1899
+ childInvocationId: string;
1900
+ now?: Date;
1901
+ }): Promise<boolean>;
1902
+ markStagedInvocationRejected(input: {
1903
+ id: string;
1904
+ workerId: string;
1905
+ leaseToken: number;
1906
+ error: string;
1907
+ now?: Date;
1908
+ }): Promise<boolean>;
1909
+ markStagedInvocationFailed(input: {
1910
+ id: string;
1911
+ workerId: string;
1912
+ leaseToken: number;
1913
+ error: string;
1914
+ availableAt: Date;
1915
+ deadLetter: boolean;
1916
+ childInvocationId?: string;
1917
+ now?: Date;
1918
+ }): Promise<boolean>;
1919
+ renewStagedInvocationLease(input: {
1920
+ id: string;
1921
+ workerId: string;
1922
+ leaseToken: number;
1923
+ leaseDurationMs: number;
1924
+ now?: Date;
1925
+ }): Promise<boolean>;
1926
+ /**
1927
+ * The UPDATE needs a row lock that conflicts with the `FOR SHARE` lock the
1928
+ * fenced child insert takes on this row, so it waits for an in-flight
1929
+ * insert to commit. A fenced insert under the old token that reaches the
1930
+ * row while this rotation is uncommitted waits for it, rechecks the new row
1931
+ * version and inserts nothing (live tests "orders a token rotation after an
1932
+ * in-flight fenced create ..." and "refuses a fenced create under the old
1933
+ * token once a token rotation is ordered before it").
1934
+ */
1935
+ rotateStagedInvocationLease(input: {
1936
+ id: string;
1937
+ workerId: string;
1938
+ leaseToken: number;
1939
+ leaseDurationMs: number;
1940
+ now?: Date;
1941
+ }): Promise<number | undefined>;
1942
+ cancelStagedInvocationsForFailedParents(input?: {
1943
+ tenantId?: string;
1944
+ spaceId?: string;
1945
+ now?: Date;
1946
+ }): Promise<number>;
1947
+ listStagedInvocations(input: {
1948
+ tenantId: string;
1949
+ spaceId: string;
1950
+ parentInvocationId?: string;
1951
+ statuses?: readonly StagedInvocationStatus[];
1952
+ }): Promise<StagedInvocationRecord[]>;
1953
+ private settleStagedInvocation;
1634
1954
  nextEventSequence(tenantId: string, spaceId: string): Promise<number>;
1635
1955
  getEntityStateSource(tenantId: string, spaceId: string, entityType: string, entityId: string): Promise<{
1636
1956
  state: string;
@@ -1764,6 +2084,28 @@ export interface CreateDurableSagaParentLifecycleOptions<TDb> {
1764
2084
  */
1765
2085
  export declare function createDurableSagaParentLifecycle<TDb>(options: CreateDurableSagaParentLifecycleOptions<TDb>): HostSagaParentLifecycle;
1766
2086
  //#endregion
2087
+ //#region src/staged-invocations.d.ts
2088
+ export declare const MAX_STAGED_INVOCATIONS = 32;
2089
+ export declare const MAX_STAGED_INVOCATION_KEY_LENGTH = 128;
2090
+ export declare const MAX_STAGED_INVOCATION_BYTES: number;
2091
+ export declare const MAX_STAGED_INVOCATION_BATCH_BYTES: number;
2092
+ export declare const STAGED_INVOCATION_KEY_PATTERN: RegExp;
2093
+ interface PrepareStagedInvocationBatchInput<TDb> {
2094
+ parent: Pick<ActionInvocationRecord, "id" | "tenantId" | "spaceId" | "actionId">;
2095
+ parentAction: ActionDefinition<TDb>;
2096
+ requests: readonly StagedInvocationRequest[];
2097
+ resolveAction(actionId: ActionId): ActionDefinition<TDb> | undefined;
2098
+ redactParameters?(actionId: ActionId, parameters: Record<string, unknown>): Record<string, unknown>;
2099
+ /** Keys whose parameters already passed the redactor into a durable checkpoint. */
2100
+ redactedKeys?: readonly string[];
2101
+ now: Date;
2102
+ }
2103
+ /** Validate and normalize one immutable batch before its parent transaction commits. */
2104
+ export declare function prepareStagedInvocationBatch<TDb>(input: PrepareStagedInvocationBatchInput<TDb>): StagedInvocationRecord[];
2105
+ export declare function stagedInvocationRequestIdentity(record: StagedInvocationRecord): string;
2106
+ export declare function cloneStagedInvocationRecord(record: StagedInvocationRecord): StagedInvocationRecord;
2107
+ export declare function asStagedInvocationStore<TDb>(store: PlatformHostStore<TDb>): StagedInvocationPlatformHostStore<TDb> | undefined;
2108
+ //#endregion
1767
2109
  //#region src/tenant-isolation.d.ts
1768
2110
  export interface TenantIsolationScope {
1769
2111
  tenantId: string;
@@ -1801,7 +2143,7 @@ export interface TenantIsolationSubject<TDb = unknown> {
1801
2143
  reset?(): Promise<void> | void;
1802
2144
  }
1803
2145
  export interface TenantIsolationCheck {
1804
- id: "fabric.tenancy.invocation-scoped-read.v1" | "fabric.tenancy.idempotency-key-scoped.v1" | "fabric.tenancy.events-scoped.v1" | "fabric.tenancy.worker-claim-scoped.v1" | "fabric.tenancy.completion-scoped.v1" | "fabric.tenancy.projection-scoped.v1" | "fabric.tenancy.adapter-invocation-read-scoped.v1" | "fabric.tenancy.adapter-invocation-write-scoped.v1";
2146
+ id: "fabric.tenancy.invocation-scoped-read.v1" | "fabric.tenancy.idempotency-key-scoped.v1" | "fabric.tenancy.events-scoped.v1" | "fabric.tenancy.worker-claim-scoped.v1" | "fabric.tenancy.staged-invocation-scoped.v1" | "fabric.tenancy.completion-scoped.v1" | "fabric.tenancy.projection-scoped.v1" | "fabric.tenancy.adapter-invocation-read-scoped.v1" | "fabric.tenancy.adapter-invocation-write-scoped.v1";
1805
2147
  run(subject: TenantIsolationSubject): Promise<string[]>;
1806
2148
  }
1807
2149
  export interface TenantIsolationResult {
@@ -1814,15 +2156,16 @@ export interface TenantIsolationResult {
1814
2156
  }>;
1815
2157
  }
1816
2158
  /**
1817
- * The suite any deployment of the host must pass before it claims isolation.
2159
+ * The suite a deployment of the host must pass before it claims isolation.
1818
2160
  *
1819
- * Eight checks: an invocation cannot be read, executed, or completed under
2161
+ * Nine checks: an invocation cannot be read, executed, or completed under
1820
2162
  * another tenant's scope; an idempotency key is a tenant's own, so the same
1821
2163
  * key in two tenants is two invocations; a tenant's event stream carries no
1822
2164
  * other tenant's events; a worker scoped to one tenant does not claim another's
1823
- * work; an adapter checkpoint cannot be read or written under another tenant,
1824
- * space, or owning invocation; and, when the subject lends a read, a viewer in
1825
- * the other tenant sees nothing of ours.
2165
+ * work; staged listing and claiming retain tenant and space scope; an adapter
2166
+ * checkpoint cannot be read or written under another tenant, space, or owning
2167
+ * invocation; and, when the subject lends a read, a viewer in the other tenant
2168
+ * sees nothing of ours.
1826
2169
  */
1827
2170
  export declare function tenantIsolationChecks(): readonly TenantIsolationCheck[];
1828
2171
  /** Run every check, returning each result rather than throwing on the first. */