@fabricorg/platform-host 8.1.2 → 8.3.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
@@ -241,6 +249,31 @@ export interface PendingAssetEvent {
241
249
  payload: unknown;
242
250
  eventSchemaVersion?: number;
243
251
  }
252
+ /** JSON preparation committed with the handler before adapter execution. */
253
+ export interface FinalizationCheckpoint {
254
+ protocol: "atomic-after-adapters-v1";
255
+ actionVersion: number;
256
+ steps: {
257
+ adapterType: string;
258
+ operation: string;
259
+ }[];
260
+ params: import("@fabricorg/platform").FinalizationJson;
261
+ handlerData: import("@fabricorg/platform").FinalizationRecord;
262
+ handlerEvents: (Omit<PendingAssetEvent, "payload"> & {
263
+ payload: import("@fabricorg/platform").FinalizationJson;
264
+ })[];
265
+ result: import("@fabricorg/platform").FinalizationRecord;
266
+ stateTransition?: Omit<PendingAssetEvent, "payload"> & {
267
+ payload: import("@fabricorg/platform").FinalizationJson;
268
+ };
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;
276
+ }
244
277
  export interface ActionInvocationRecord {
245
278
  id: string;
246
279
  tenantId: string;
@@ -259,12 +292,16 @@ export interface ActionInvocationRecord {
259
292
  parameterDigestAlgorithm?: "fabric-canonical-json-sha256-v1";
260
293
  idempotencyActorId?: string;
261
294
  idempotencyAuthorizationBindingId?: string;
295
+ /** Host-only identity for a staged child; public submissions cannot set it. */
296
+ stagedOrigin?: StagedInvocationOrigin;
262
297
  provenance?: Omit<InvocationProvenance, "traceAttributes">;
263
298
  authorizationBinding?: AuthorizationBinding;
264
299
  executionReason?: ActionExecutionReason;
265
300
  authorizationReconciliation?: AuthorizationReconciliationOutcome;
266
301
  /** Durable evidence when an adapter returned an ambiguous outcome requiring reconciliation. */
267
302
  adapterReconciliation?: AdapterReconciliationOutcome;
303
+ /** Durable normalized preparation; absent for invocations prepared by older Hosts. */
304
+ finalizationCheckpoint?: FinalizationCheckpoint;
268
305
  /**
269
306
  * Transition Host validated for an idempotent after_adapters action, saved
270
307
  * after handler/result/event validation inside a mutation transaction, or before
@@ -289,6 +326,8 @@ export interface ActionInvocationRecord {
289
326
  authorizationBindingId?: string;
290
327
  /** Worker claims including the first; approval acceptance sets a minimum of 1. */
291
328
  attemptCount: number;
329
+ /** Latest claim durably deferred in pre-effect governance; permits only the immediately next claim. */
330
+ deferredBeforeEffectAttempt?: number;
292
331
  leaseOwner?: string;
293
332
  leaseExpiresAt?: Date;
294
333
  /** Monotonic claim generation used to reject writes from expired workers. */
@@ -456,7 +495,7 @@ export interface AdapterInvocationRecord {
456
495
  createdAt: Date;
457
496
  updatedAt: Date;
458
497
  }
459
- 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"> {}
460
499
  /** Scope of an adapter checkpoint within its owning action invocation. */
461
500
  export interface AdapterInvocationScope {
462
501
  tenantId: string;
@@ -514,6 +553,8 @@ export interface PlatformHostStore<TDb> {
514
553
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
515
554
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
516
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>;
517
558
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
518
559
  appendPolicyEvaluation(record: PolicyEvaluationRecord): Promise<void>;
519
560
  createAdapterInvocation(record: AdapterInvocationRecord): Promise<void>;
@@ -645,6 +686,135 @@ export interface PlatformHostMutationTransaction<TDb> {
645
686
  * governance evidence cannot share the event/verdict transaction.
646
687
  */
647
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[]>;
648
818
  }
649
819
  export type OutboxStatus = "pending" | "published" | "dead_letter";
650
820
  export type EventPayloadClassification = "public" | "internal" | "confidential" | "restricted";
@@ -954,7 +1124,7 @@ export interface ClaimActionInvocationsInput {
954
1124
  * - `claimActionInvocations` must never claim a running invocation without a
955
1125
  * lease, which is what a parked invocation is.
956
1126
  */
957
- export type ActionInvocationPatch = Partial<Pick<ActionInvocationRecord, "status" | "result" | "error" | "authorizationReconciliation" | "adapterReconciliation" | "pendingStateTransition" | "pendingCompletion" | "externalCompletion">>;
1127
+ export type ActionInvocationPatch = Partial<Pick<ActionInvocationRecord, "finalizationCheckpoint" | "deferredBeforeEffectAttempt" | "status" | "result" | "error" | "authorizationReconciliation" | "adapterReconciliation" | "pendingStateTransition" | "pendingCompletion" | "externalCompletion">>;
958
1128
  export interface UpdateLeasedActionInvocationInput {
959
1129
  id: string;
960
1130
  tenantId: string;
@@ -990,7 +1160,7 @@ export interface ActionAuthorizationInput {
990
1160
  actorId: string;
991
1161
  actorType: ActorType;
992
1162
  }
993
- export type KnownActionExecutionReason = "initial" | "approval_resume" | "recovery" | "offline_replay";
1163
+ export type KnownActionExecutionReason = "initial" | "approval_resume" | "recovery" | "offline_replay" | "staged_dispatch";
994
1164
  export type ActionExecutionReason = KnownActionExecutionReason | (string & {});
995
1165
  /**
996
1166
  * Trusted execution-boundary authorization input.
@@ -1131,6 +1301,14 @@ export interface PlatformActionWorkerOptions<TDb> {
1131
1301
  * on every cycle, and run the sweep elsewhere.
1132
1302
  */
1133
1303
  sweepOverdueCompletions?: boolean;
1304
+ /** Dispatch staged descendants each cycle. Defaults to on when a registered action declares them. */
1305
+ dispatchStagedInvocations?: boolean;
1306
+ stagedInvocationBatchSize?: number;
1307
+ stagedInvocationLeaseDurationMs?: number;
1308
+ stagedInvocationLeaseRenewalIntervalMs?: number;
1309
+ stagedInvocationSubmissionTimeoutMs?: number;
1310
+ stagedInvocationMaxAttempts?: number;
1311
+ stagedInvocationRetryDelayMs?: (attempt: number) => number;
1134
1312
  }
1135
1313
  export interface PlatformActionWorkerCycleResult {
1136
1314
  claimed: number;
@@ -1144,6 +1322,38 @@ export interface PlatformActionWorkerCycleResult {
1144
1322
  * The row stays leased and a later claim retries it; excluded from failed.
1145
1323
  */
1146
1324
  deferred?: number;
1325
+ staged?: StagedInvocationDispatchResult;
1326
+ }
1327
+ export interface StagedInvocationDispatchInput {
1328
+ workerId: string;
1329
+ limit?: number;
1330
+ leaseDurationMs?: number;
1331
+ leaseRenewalIntervalMs?: number;
1332
+ submissionTimeoutMs?: number;
1333
+ maxAttempts?: number;
1334
+ retryDelayMs?: (attempt: number) => number;
1335
+ tenantId?: string;
1336
+ spaceId?: string;
1337
+ /**
1338
+ * Receives a failed staged-origin lookup and any error that ends one claimed
1339
+ * row's processing early (a store error from renewal, rotation or
1340
+ * settlement). The sweep counts that row as failed and continues.
1341
+ */
1342
+ onError?: (error: unknown) => void;
1343
+ }
1344
+ /**
1345
+ * Counters for one sweep. Each claimed row is counted once, as dispatched,
1346
+ * rejected or failed. `failed` includes rows settled for retry, rows whose
1347
+ * lease was lost before settlement, and rows whose processing threw;
1348
+ * `deadLettered` counts the subset of `failed` settled as `dead_lettered`.
1349
+ */
1350
+ export interface StagedInvocationDispatchResult {
1351
+ claimed: number;
1352
+ dispatched: number;
1353
+ rejected: number;
1354
+ failed: number;
1355
+ deadLettered: number;
1356
+ cancelled: number;
1147
1357
  }
1148
1358
  /** Origin of error text redacted before Host records or returns it. */
1149
1359
  export interface ErrorTextRedactionContext {
@@ -1179,10 +1389,12 @@ export interface PlatformHostOptions<TDb> {
1179
1389
  adapterCircuitBreakers?: AdapterCircuitBreakerProvider;
1180
1390
  /**
1181
1391
  * Positive integer attempt cap for RetryableExecutionError during governance,
1182
- * before policy-evaluation persistence (default 5). A leased, idempotent action
1183
- * below the cap is left running for worker reclaim after lease expiry.
1184
- * Inline execution, approval resume, exhausted attempts, and non-idempotent
1185
- * actions fail. Policy persistence, obligation listing/resolution/persistence, state-machine checks,
1392
+ * before policy-evaluation persistence (default 5). A leased action below the cap
1393
+ * is left running for worker reclaim after lease expiry. Non-idempotent actions
1394
+ * require a lease-fenced pre-effect deferral marker for the current claim; only
1395
+ * the immediately next claim may proceed using that marker. Inline execution,
1396
+ * approval resume, exhausted attempts, and marker-write failures are not retried.
1397
+ * Policy persistence, obligation listing/resolution/persistence, state-machine checks,
1186
1398
  * the handler transaction, handlers, adapters, and finalization are excluded;
1187
1399
  * their failure/reconciliation/recovery semantics apply, subject to recoveryMaxAttempts.
1188
1400
  */
@@ -1294,6 +1506,12 @@ export interface PlatformHostOptions<TDb> {
1294
1506
  extractEvents?: (data: Record<string, unknown>) => PendingAssetEvent[];
1295
1507
  /** Handler-result fields consumed by extractEvents and omitted from the durable result. */
1296
1508
  eventResultFields?: readonly string[];
1509
+ /**
1510
+ * Optional name registry for hook-returned events from action.finalize.
1511
+ * Checks eventType and subjectType membership; it does not validate handler events,
1512
+ * eventSchemaVersion compatibility, or payload schemas.
1513
+ */
1514
+ eventContracts?: import("@fabricorg/platform").EventContractRegistry;
1297
1515
  redactAdapterInput?: (actionId: ActionId, adapterType: string, operation: string, input: Record<string, unknown>) => Record<string, unknown>;
1298
1516
  /**
1299
1517
  * Redact successful adapter output before row/event persistence and attestation extraction.
@@ -1330,13 +1548,18 @@ export interface PlatformHostOptions<TDb> {
1330
1548
  /** Vendor-neutral lifecycle and metric sink. Telemetry is best effort. */
1331
1549
  telemetry?: PlatformHostTelemetry;
1332
1550
  /**
1333
- * Epoch-millis deadline delivered to every adapter execution context.
1334
- * When omitted, no deadline is set and adapters receive no AbortSignal.
1551
+ * Duration in milliseconds, measured before the step enters adapter retry execution.
1552
+ * A step deadlineMs overrides this duration. When this is omitted or zero and
1553
+ * the step has no deadlineMs, no deadline or AbortSignal is delivered.
1335
1554
  */
1336
1555
  adapterDeadlineMs?: number;
1337
1556
  }
1338
1557
  export interface GovernedActionHost {
1339
1558
  submitAction(input: SubmitActionInput): Promise<SubmitActionResult>;
1559
+ /** Whether this Host's registry contains an action with a non-empty staged-descendant allowlist. */
1560
+ readonly hasStagedInvocationActions?: boolean;
1561
+ /** One bounded lease-fenced sweep of completed parents' staged descendants. */
1562
+ dispatchStagedInvocations?(input: StagedInvocationDispatchInput): Promise<StagedInvocationDispatchResult>;
1340
1563
  executeInvocation(actionInvocationId: string, tenantId: string, spaceId: string, options?: ExecuteInvocationOptions): Promise<ExecuteActionResult>;
1341
1564
  resumeApprovedInvocation(actionInvocationId: string, tenantId: string, spaceId: string, decision: ActionApprovalDecision): Promise<ExecuteActionResult>;
1342
1565
  recordExecutionAttestation(actionInvocationId: string, tenantId: string, spaceId: string, attestation: ExecutionAttestation): Promise<void>;
@@ -1383,9 +1606,10 @@ export interface MemoryPlatformHostTransactionProvider<TDb> {
1383
1606
  snapshot(db: TDb): TDb;
1384
1607
  restore(db: TDb, snapshot: TDb): void;
1385
1608
  }
1386
- export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb> {
1609
+ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
1387
1610
  readonly db: TDb;
1388
1611
  readonly transactionalOutbox: boolean;
1612
+ readonly transactionalStagedInvocations: boolean;
1389
1613
  transactionWithEvents?: <TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>) => Promise<TResult>;
1390
1614
  private adapterCommit;
1391
1615
  private transactionTail;
@@ -1397,6 +1621,7 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1397
1621
  readonly executionAttestations: ExecutionAttestationRecord[];
1398
1622
  readonly externalReconciliations: ExternalReconciliationRecord[];
1399
1623
  readonly outbox: OutboxRecord[];
1624
+ readonly stagedInvocations: StagedInvocationRecord[];
1400
1625
  /**
1401
1626
  * Requeue evidence is append-only audit history, so unlike the ledger
1402
1627
  * arrays above, which tests have always inspected and seeded directly, it
@@ -1408,7 +1633,10 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1408
1633
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
1409
1634
  private runTransactionWithEvents;
1410
1635
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
1636
+ createActionInvocationForStagedLease(input: CreateStagedActionInvocationInput): Promise<CreateStagedActionInvocationResult>;
1637
+ private applyStagedBatch;
1411
1638
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
1639
+ getActionInvocationByStagedOrigin(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
1412
1640
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
1413
1641
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1414
1642
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
@@ -1472,6 +1700,60 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1472
1700
  claimActionInvocations(input: ClaimActionInvocationsInput): Promise<ActionInvocationRecord[]>;
1473
1701
  updateLeasedActionInvocation(input: UpdateLeasedActionInvocationInput): Promise<boolean>;
1474
1702
  renewActionInvocationLease(input: RenewActionInvocationLeaseInput): Promise<boolean>;
1703
+ claimStagedInvocations(input: ClaimStagedInvocationsInput): Promise<StagedInvocationRecord[]>;
1704
+ markStagedInvocationDispatched(input: {
1705
+ id: string;
1706
+ workerId: string;
1707
+ leaseToken: number;
1708
+ childInvocationId: string;
1709
+ now?: Date;
1710
+ }): Promise<boolean>;
1711
+ markStagedInvocationRejected(input: {
1712
+ id: string;
1713
+ workerId: string;
1714
+ leaseToken: number;
1715
+ error: string;
1716
+ now?: Date;
1717
+ }): Promise<boolean>;
1718
+ markStagedInvocationFailed(input: {
1719
+ id: string;
1720
+ workerId: string;
1721
+ leaseToken: number;
1722
+ error: string;
1723
+ availableAt: Date;
1724
+ deadLetter: boolean;
1725
+ childInvocationId?: string;
1726
+ now?: Date;
1727
+ }): Promise<boolean>;
1728
+ renewStagedInvocationLease(input: {
1729
+ id: string;
1730
+ workerId: string;
1731
+ leaseToken: number;
1732
+ leaseDurationMs: number;
1733
+ now?: Date;
1734
+ }): Promise<boolean>;
1735
+ /** Await-free, like the fenced create above, so a create runs wholly before or after it. */
1736
+ rotateStagedInvocationLease(input: {
1737
+ id: string;
1738
+ workerId: string;
1739
+ leaseToken: number;
1740
+ leaseDurationMs: number;
1741
+ now?: Date;
1742
+ }): Promise<number | undefined>;
1743
+ cancelStagedInvocationsForFailedParents(input?: {
1744
+ tenantId?: string;
1745
+ spaceId?: string;
1746
+ now?: Date;
1747
+ }): Promise<number>;
1748
+ listStagedInvocations(input: {
1749
+ tenantId: string;
1750
+ spaceId: string;
1751
+ parentInvocationId?: string;
1752
+ statuses?: readonly StagedInvocationStatus[];
1753
+ }): Promise<StagedInvocationRecord[]>;
1754
+ private leasedStagedInvocation;
1755
+ /** Mirrors PostgreSQL: a missing child id keeps the recorded one; `lastError` is replaced. */
1756
+ private settleStagedInvocation;
1475
1757
  listEvents(tenantId: string, spaceId: string): Promise<AssetEventEnvelope[]>;
1476
1758
  }
1477
1759
  //#endregion
@@ -1539,16 +1821,20 @@ export interface PostgresPlatformHostTransactionProvider<TDb> {
1539
1821
  run<TResult>(run: (context: PostgresPlatformHostTransactionContext<TDb>) => Promise<TResult>): Promise<TResult>;
1540
1822
  }
1541
1823
  /** Durable mutation-pipeline ledger for Databricks Lakebase or standard Postgres. */
1542
- export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb> {
1824
+ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
1543
1825
  readonly db: TDb;
1544
1826
  private readonly sql;
1545
1827
  readonly transactionWithEvents?: AtomicMutationPlatformHostStore<TDb>["transactionWithEvents"];
1546
1828
  readonly transactionalOutbox: boolean;
1829
+ readonly transactionalStagedInvocations: boolean;
1547
1830
  constructor(db: TDb, sql: PlatformHostSqlClient, transactionProvider?: PostgresPlatformHostTransactionProvider<TDb>);
1548
1831
  ensureSchema(): Promise<void>;
1549
1832
  transaction<TResult>(run: (db: TDb) => Promise<TResult>): Promise<TResult>;
1550
1833
  createActionInvocation(input: CreateActionInvocationInput): Promise<ActionInvocationRecord>;
1834
+ createActionInvocationForStagedLease(input: CreateStagedActionInvocationInput): Promise<CreateStagedActionInvocationResult>;
1835
+ stageInvocations(records: readonly StagedInvocationRecord[]): Promise<void>;
1551
1836
  getActionInvocation(id: string, tenantId: string, spaceId: string): Promise<ActionInvocationRecord | undefined>;
1837
+ getActionInvocationByStagedOrigin(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
1552
1838
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
1553
1839
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1554
1840
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
@@ -1599,6 +1885,66 @@ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatfo
1599
1885
  spaceId: string;
1600
1886
  outboxId?: string;
1601
1887
  }): Promise<OutboxRequeueRecord[]>;
1888
+ claimStagedInvocations(input: ClaimStagedInvocationsInput): Promise<StagedInvocationRecord[]>;
1889
+ markStagedInvocationDispatched(input: {
1890
+ id: string;
1891
+ workerId: string;
1892
+ leaseToken: number;
1893
+ childInvocationId: string;
1894
+ now?: Date;
1895
+ }): Promise<boolean>;
1896
+ markStagedInvocationRejected(input: {
1897
+ id: string;
1898
+ workerId: string;
1899
+ leaseToken: number;
1900
+ error: string;
1901
+ now?: Date;
1902
+ }): Promise<boolean>;
1903
+ markStagedInvocationFailed(input: {
1904
+ id: string;
1905
+ workerId: string;
1906
+ leaseToken: number;
1907
+ error: string;
1908
+ availableAt: Date;
1909
+ deadLetter: boolean;
1910
+ childInvocationId?: string;
1911
+ now?: Date;
1912
+ }): Promise<boolean>;
1913
+ renewStagedInvocationLease(input: {
1914
+ id: string;
1915
+ workerId: string;
1916
+ leaseToken: number;
1917
+ leaseDurationMs: number;
1918
+ now?: Date;
1919
+ }): Promise<boolean>;
1920
+ /**
1921
+ * The UPDATE needs a row lock that conflicts with the `FOR SHARE` lock the
1922
+ * fenced child insert takes on this row, so it waits for an in-flight
1923
+ * insert to commit. A fenced insert under the old token that reaches the
1924
+ * row while this rotation is uncommitted waits for it, rechecks the new row
1925
+ * version and inserts nothing (live tests "orders a token rotation after an
1926
+ * in-flight fenced create ..." and "refuses a fenced create under the old
1927
+ * token once a token rotation is ordered before it").
1928
+ */
1929
+ rotateStagedInvocationLease(input: {
1930
+ id: string;
1931
+ workerId: string;
1932
+ leaseToken: number;
1933
+ leaseDurationMs: number;
1934
+ now?: Date;
1935
+ }): Promise<number | undefined>;
1936
+ cancelStagedInvocationsForFailedParents(input?: {
1937
+ tenantId?: string;
1938
+ spaceId?: string;
1939
+ now?: Date;
1940
+ }): Promise<number>;
1941
+ listStagedInvocations(input: {
1942
+ tenantId: string;
1943
+ spaceId: string;
1944
+ parentInvocationId?: string;
1945
+ statuses?: readonly StagedInvocationStatus[];
1946
+ }): Promise<StagedInvocationRecord[]>;
1947
+ private settleStagedInvocation;
1602
1948
  nextEventSequence(tenantId: string, spaceId: string): Promise<number>;
1603
1949
  getEntityStateSource(tenantId: string, spaceId: string, entityType: string, entityId: string): Promise<{
1604
1950
  state: string;
@@ -1732,6 +2078,28 @@ export interface CreateDurableSagaParentLifecycleOptions<TDb> {
1732
2078
  */
1733
2079
  export declare function createDurableSagaParentLifecycle<TDb>(options: CreateDurableSagaParentLifecycleOptions<TDb>): HostSagaParentLifecycle;
1734
2080
  //#endregion
2081
+ //#region src/staged-invocations.d.ts
2082
+ export declare const MAX_STAGED_INVOCATIONS = 32;
2083
+ export declare const MAX_STAGED_INVOCATION_KEY_LENGTH = 128;
2084
+ export declare const MAX_STAGED_INVOCATION_BYTES: number;
2085
+ export declare const MAX_STAGED_INVOCATION_BATCH_BYTES: number;
2086
+ export declare const STAGED_INVOCATION_KEY_PATTERN: RegExp;
2087
+ interface PrepareStagedInvocationBatchInput<TDb> {
2088
+ parent: Pick<ActionInvocationRecord, "id" | "tenantId" | "spaceId" | "actionId">;
2089
+ parentAction: ActionDefinition<TDb>;
2090
+ requests: readonly StagedInvocationRequest[];
2091
+ resolveAction(actionId: ActionId): ActionDefinition<TDb> | undefined;
2092
+ redactParameters?(actionId: ActionId, parameters: Record<string, unknown>): Record<string, unknown>;
2093
+ /** Keys whose parameters already passed the redactor into a durable checkpoint. */
2094
+ redactedKeys?: readonly string[];
2095
+ now: Date;
2096
+ }
2097
+ /** Validate and normalize one immutable batch before its parent transaction commits. */
2098
+ export declare function prepareStagedInvocationBatch<TDb>(input: PrepareStagedInvocationBatchInput<TDb>): StagedInvocationRecord[];
2099
+ export declare function stagedInvocationRequestIdentity(record: StagedInvocationRecord): string;
2100
+ export declare function cloneStagedInvocationRecord(record: StagedInvocationRecord): StagedInvocationRecord;
2101
+ export declare function asStagedInvocationStore<TDb>(store: PlatformHostStore<TDb>): StagedInvocationPlatformHostStore<TDb> | undefined;
2102
+ //#endregion
1735
2103
  //#region src/tenant-isolation.d.ts
1736
2104
  export interface TenantIsolationScope {
1737
2105
  tenantId: string;
@@ -1769,7 +2137,7 @@ export interface TenantIsolationSubject<TDb = unknown> {
1769
2137
  reset?(): Promise<void> | void;
1770
2138
  }
1771
2139
  export interface TenantIsolationCheck {
1772
- 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";
2140
+ 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";
1773
2141
  run(subject: TenantIsolationSubject): Promise<string[]>;
1774
2142
  }
1775
2143
  export interface TenantIsolationResult {
@@ -1782,15 +2150,16 @@ export interface TenantIsolationResult {
1782
2150
  }>;
1783
2151
  }
1784
2152
  /**
1785
- * The suite any deployment of the host must pass before it claims isolation.
2153
+ * The suite a deployment of the host must pass before it claims isolation.
1786
2154
  *
1787
- * Eight checks: an invocation cannot be read, executed, or completed under
2155
+ * Nine checks: an invocation cannot be read, executed, or completed under
1788
2156
  * another tenant's scope; an idempotency key is a tenant's own, so the same
1789
2157
  * key in two tenants is two invocations; a tenant's event stream carries no
1790
2158
  * other tenant's events; a worker scoped to one tenant does not claim another's
1791
- * work; an adapter checkpoint cannot be read or written under another tenant,
1792
- * space, or owning invocation; and, when the subject lends a read, a viewer in
1793
- * the other tenant sees nothing of ours.
2159
+ * work; staged listing and claiming retain tenant and space scope; an adapter
2160
+ * checkpoint cannot be read or written under another tenant, space, or owning
2161
+ * invocation; and, when the subject lends a read, a viewer in the other tenant
2162
+ * sees nothing of ours.
1794
2163
  */
1795
2164
  export declare function tenantIsolationChecks(): readonly TenantIsolationCheck[];
1796
2165
  /** Run every check, returning each result rather than throwing on the first. */