@fabricorg/platform-host 8.4.0 → 8.6.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, StagedInvocationRequest, canonicalJson } from "@fabricorg/platform";
1
+ import { ActionDefinition, ActionId, ActionStatus, ActorType, AdapterCircuitBreakerProvider, AdapterImplementation, AgentActionPolicyContext, AgentActionPolicyDecision, 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. */
@@ -183,7 +183,7 @@ export interface IdempotencyConflict {
183
183
  code: "IDEMPOTENCY_CONFLICT";
184
184
  idempotencyKey: string;
185
185
  existingInvocationId: string;
186
- reasons: Array<"actor" | "authority_binding" | "action_version" | "parameters">;
186
+ reasons: Array<"actor" | "authority_binding" | "action_version" | "parameters" | "agent_proposal">;
187
187
  }
188
188
  export declare class IdempotencyConflictError extends Error {
189
189
  readonly conflict: IdempotencyConflict;
@@ -251,7 +251,7 @@ export interface PendingAssetEvent {
251
251
  }
252
252
  /** JSON preparation committed with the handler before adapter execution. */
253
253
  export interface FinalizationCheckpoint {
254
- protocol: "atomic-after-adapters-v1";
254
+ protocol: "atomic-after-adapters-v1" | "atomic-after-adapters-v2";
255
255
  actionVersion: number;
256
256
  steps: {
257
257
  adapterType: string;
@@ -259,6 +259,8 @@ export interface FinalizationCheckpoint {
259
259
  }[];
260
260
  params: import("@fabricorg/platform").FinalizationJson;
261
261
  handlerData: import("@fabricorg/platform").FinalizationRecord;
262
+ /** Private adapter input preparation; retained until settlement, including reconciliation. */
263
+ adapterPreparation?: import("@fabricorg/platform").FinalizationRecord;
262
264
  handlerEvents: (Omit<PendingAssetEvent, "payload"> & {
263
265
  payload: import("@fabricorg/platform").FinalizationJson;
264
266
  })[];
@@ -274,7 +276,21 @@ export interface StagedInvocationOrigin {
274
276
  parentInvocationId: string;
275
277
  stageKey: string;
276
278
  }
279
+ export interface AgentProposal {
280
+ confidence?: number;
281
+ riskTier?: AgentActionRiskTier;
282
+ agentSessionId?: string;
283
+ agentRunId?: string;
284
+ reason?: string;
285
+ evidence?: ApprovalEvidence;
286
+ }
287
+ export interface HostHitlEvaluatorContext extends AgentActionPolicyContext {
288
+ actionInvocationId: string;
289
+ /** The durable (post-redaction) invocation record as read before evaluation. */
290
+ invocation: Readonly<ActionInvocationRecord>;
291
+ }
277
292
  export interface ActionInvocationRecord {
293
+ readonly agentProposal?: AgentProposal;
278
294
  id: string;
279
295
  tenantId: string;
280
296
  spaceId: string;
@@ -337,6 +353,11 @@ export interface ActionInvocationRecord {
337
353
  hitlReason?: string;
338
354
  hitlPolicyVersion?: string;
339
355
  approvalDecision?: PersistedActionApprovalDecision;
356
+ approvalExpiresAt?: Date;
357
+ approvalExpiry?: {
358
+ expiresAt: Date;
359
+ expiredAt: Date;
360
+ };
340
361
  mutationFootprint?: MutationFootprint;
341
362
  executionPrincipal?: ExecutionPrincipal;
342
363
  runtimeEvidence?: GovernanceRuntimeEvidence;
@@ -359,14 +380,50 @@ export interface AuthorizationReconciliationOutcome {
359
380
  provenance?: Omit<InvocationProvenance, "traceAttributes">;
360
381
  message: string;
361
382
  }
383
+ export interface HostHitlDecision extends AgentActionPolicyDecision {
384
+ /** Absolute approval deadline; a valid Date, including a deadline already due. */
385
+ approvalExpiresAt?: Date;
386
+ }
362
387
  export interface HitlDecisionEvidence {
388
+ approvalExpiresAt?: Date;
363
389
  route: AgentActionRoute;
364
390
  riskTier: AgentActionRiskTier;
365
391
  reason: string;
366
392
  policyVersion?: string;
367
393
  evaluatedAt: Date;
368
394
  }
395
+ export type ApprovalEvidenceValue = null | boolean | number | string | ApprovalEvidenceValue[] | {
396
+ [key: string]: ApprovalEvidenceValue;
397
+ };
398
+ export type ApprovalEvidence = {
399
+ [key: string]: ApprovalEvidenceValue;
400
+ };
401
+ /** Maximum serialized UTF-8 evidence size. */
402
+ export declare const MAX_APPROVAL_EVIDENCE_BYTES: number;
403
+ export declare const APPROVAL_REQUESTED_EVENT: "ApprovalRequested";
404
+ export declare const APPROVAL_DECIDED_EVENT: "ApprovalDecided";
405
+ export declare const APPROVAL_EXPIRED_EVENT: "ApprovalExpired";
406
+ export interface ApprovalRequestedPayload {
407
+ route: AgentActionRoute;
408
+ riskTier: AgentActionRiskTier;
409
+ reason: string;
410
+ expiresAt?: string;
411
+ }
412
+ export interface ApprovalDecidedPayload {
413
+ approved: boolean;
414
+ approverId: string;
415
+ approverType: ActorType;
416
+ reason?: string;
417
+ decidedAt: string;
418
+ evidencePresent: boolean;
419
+ }
420
+ export interface ApprovalExpiredPayload {
421
+ expiresAt: string;
422
+ expiredAt: string;
423
+ }
369
424
  export interface ActionApprovalDecision {
425
+ /** Verification references only; credentials and tokens must not be included. */
426
+ evidence?: ApprovalEvidence;
370
427
  approved: boolean;
371
428
  approverId: string;
372
429
  approverType: ActorType;
@@ -625,6 +682,8 @@ export interface PlatformHostStore<TDb> {
625
682
  * back when `run` throws.
626
683
  */
627
684
  export interface PlatformHostMutationTransaction<TDb> {
685
+ /** Conditional SQL approval write on the transaction connection; lifecycle events are appended by the caller in this unit of work. */
686
+ beginApprovalDecision?(input: Omit<BeginApprovalDecisionInput, "lifecycleEvent">): Promise<ApprovalDecisionTransitionResult>;
628
687
  readonly db: TDb;
629
688
  /**
630
689
  * Read and lock an invocation until this transaction completes.
@@ -1059,7 +1118,34 @@ export interface OutboxRequeueAuthorizationInput {
1059
1118
  export interface AtomicMutationPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
1060
1119
  transactionWithEvents<TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>): Promise<TResult>;
1061
1120
  }
1121
+ export interface ApprovalLifecycleEvent {
1122
+ event: AssetEventEnvelope;
1123
+ metadata: OutboxEventMetadata;
1124
+ }
1125
+ /** Called within the transition transaction. Must be pure and return audit-safe data. Sequence is assigned by the store. */
1126
+ export type ApprovalLifecycleEventFactory = (invocation: ActionInvocationRecord, kind: "requested" | "decided" | "expired") => ApprovalLifecycleEvent;
1127
+ export interface ExpireDueApprovalsInput {
1128
+ tenantId?: string;
1129
+ spaceId?: string;
1130
+ now?: Date;
1131
+ limit?: number;
1132
+ }
1133
+ export interface ExpireDueApprovalsStoreInput extends ExpireDueApprovalsInput {
1134
+ now: Date;
1135
+ lifecycleEvent?: ApprovalLifecycleEventFactory;
1136
+ }
1137
+ export interface ParkApprovalInput {
1138
+ approvalExpiresAt?: Date;
1139
+ actionInvocationId: string;
1140
+ tenantId: string;
1141
+ spaceId: string;
1142
+ now: Date;
1143
+ leaseOwner?: string;
1144
+ leaseToken?: number;
1145
+ lifecycleEvent?: ApprovalLifecycleEventFactory;
1146
+ }
1062
1147
  export interface BeginApprovalDecisionInput {
1148
+ lifecycleEvent?: ApprovalLifecycleEventFactory;
1063
1149
  actionInvocationId: string;
1064
1150
  tenantId: string;
1065
1151
  spaceId: string;
@@ -1074,7 +1160,37 @@ export interface ApprovalDecisionTransitionResult {
1074
1160
  }
1075
1161
  /** Optional store capability required only when agent HITL or approval resume is used. */
1076
1162
  export interface ApprovalPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
1163
+ /** True only when approval transitions and lifecycle outbox events share a transaction. */
1164
+ readonly transactionalApprovalLifecycle?: boolean;
1165
+ /**
1166
+ * Conditionally park an executable invocation only with its matching lease owner/generation
1167
+ * (when leased) and no prior approval decision. Persist the deadline supplied in input.
1168
+ * Serialize with beginApprovalDecision and expireDueApprovals so each waiting row settles
1169
+ * exactly once across concurrent decisions and sweeps; losing transitions report applied: false.
1170
+ * When lifecycleEvent is supplied, its event/outbox writes and the state change must commit
1171
+ * in the same atomic unit of work, or roll back together.
1172
+ */
1173
+ parkApproval?(input: ParkApprovalInput): Promise<ApprovalDecisionTransitionResult>;
1174
+ /**
1175
+ * Atomically claim and expire scoped waiting rows whose input.now >= approvalExpiresAt.
1176
+ * Set failed with approvalExpiry and no approvalDecision. Each waiting row must settle
1177
+ * exactly once across concurrent sweeps and beginApprovalDecision calls; return only rows
1178
+ * this sweep settled (competing decision transitions report applied: false).
1179
+ * When lifecycleEvent is supplied, the events/outbox writes and state changes must commit
1180
+ * in the same atomic unit of work, or roll back together.
1181
+ */
1182
+ expireDueApprovals?(input: ExpireDueApprovalsStoreInput): Promise<ActionInvocationRecord[]>;
1077
1183
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1184
+ /**
1185
+ * Atomically decide a waiting row, comparing the stored deadline with input.now inside
1186
+ * the serialized transition. A decision at or after that deadline (input.now >=
1187
+ * approvalExpiresAt) must never be applied: expire the row to failed with approvalExpiry
1188
+ * and no approvalDecision instead. The caller's decision.decidedAt does not extend it.
1189
+ * Each waiting row must settle exactly once across concurrent decisions and expiry sweeps;
1190
+ * a losing transition reports applied: false. With lifecycleEvent, the selected decided
1191
+ * or expired event/outbox writes and state change must commit in the same atomic unit
1192
+ * of work, or roll back together. Required when implementing parkApproval/expireDueApprovals.
1193
+ */
1078
1194
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
1079
1195
  }
1080
1196
  /** Optional durable capabilities used when external-governance enrichment is configured. */
@@ -1124,7 +1240,7 @@ export interface ClaimActionInvocationsInput {
1124
1240
  * - `claimActionInvocations` must never claim a running invocation without a
1125
1241
  * lease, which is what a parked invocation is.
1126
1242
  */
1127
- export type ActionInvocationPatch = Partial<Pick<ActionInvocationRecord, "finalizationCheckpoint" | "deferredBeforeEffectAttempt" | "status" | "result" | "error" | "authorizationReconciliation" | "adapterReconciliation" | "pendingStateTransition" | "pendingCompletion" | "externalCompletion">>;
1243
+ export type ActionInvocationPatch = Partial<Pick<ActionInvocationRecord, "approvalExpiresAt" | "approvalExpiry" | "approvalDecision" | "leaseOwner" | "leaseExpiresAt" | "leaseToken" | "attemptCount" | "finalizationCheckpoint" | "deferredBeforeEffectAttempt" | "status" | "result" | "error" | "authorizationReconciliation" | "adapterReconciliation" | "pendingStateTransition" | "pendingCompletion" | "externalCompletion">>;
1128
1244
  export interface UpdateLeasedActionInvocationInput {
1129
1245
  id: string;
1130
1246
  tenantId: string;
@@ -1233,9 +1349,12 @@ export interface PlatformActionDispatcher {
1233
1349
  * `provenance` and `authorizationBinding` are also read once there and
1234
1350
  * replaced by Host's own copies. For any other action Host uses those three
1235
1351
  * as the caller's objects, read where and as often as before (README,
1236
- * "Submission snapshot").
1352
+ * "Submission snapshot"). `agentProposal` is read once and deeply copied before
1353
+ * the first await for all actions, independently of `admitSubmission`.
1237
1354
  */
1238
1355
  export interface SubmitActionInput {
1356
+ /** Read once and deeply copied before the first await, regardless of admission. */
1357
+ agentProposal?: AgentProposal;
1239
1358
  actionId: ActionId;
1240
1359
  parameters: Record<string, unknown>;
1241
1360
  tenantId: string;
@@ -1322,6 +1441,8 @@ export interface PlatformActionWorkerCycleResult {
1322
1441
  failed: number;
1323
1442
  /** Present when claimed agent work was durably parked instead of failing. */
1324
1443
  waitingForApproval?: number;
1444
+ /** Due approval waits settled to failed during this cycle; present when positive. */
1445
+ expiredApprovals?: number;
1325
1446
  /**
1326
1447
  * Present only when > 0: claimed invocations whose execution threw a
1327
1448
  * RetryableExecutionError (counted by error type, not by where it was thrown).
@@ -1446,7 +1567,11 @@ export interface PlatformHostOptions<TDb> {
1446
1567
  shouldPublish?(event: AssetEventEnvelope): boolean;
1447
1568
  };
1448
1569
  /** Optional vertical-owned evaluator. When absent, agent behavior is unchanged. */
1449
- hitlEvaluator?: AgentActionPolicyEvaluator;
1570
+ hitlEvaluator?: (context: HostHitlEvaluatorContext) => HostHitlDecision | Promise<HostHitlDecision>;
1571
+ /** Positive integer milliseconds, used when the evaluator supplies no absolute deadline. */
1572
+ approvalDefaultTtlMs?: number;
1573
+ /** Atomic approval lifecycle events and outbox writes; defaults to false. */
1574
+ approvalLifecycleEvents?: boolean;
1450
1575
  /** Durable evaluator/ruleset version recorded with every HITL decision. */
1451
1576
  hitlPolicyVersion?: string;
1452
1577
  /** Optional portable resolver; provider-specific implementations belong in integration packages. */
@@ -1499,6 +1624,8 @@ export interface PlatformHostOptions<TDb> {
1499
1624
  * copy, after the check admitted the submission.
1500
1625
  */
1501
1626
  redactActionParameters?: (actionId: ActionId, parameters: Record<string, unknown>) => Record<string, unknown>;
1627
+ /** Runs beside parameter redaction; output is validated and copied before persistence. */
1628
+ redactAgentProposal?: (actionId: ActionId, proposal: AgentProposal) => AgentProposal;
1502
1629
  /**
1503
1630
  * The bound on an action's `admitSubmission` check. An answer that
1504
1631
  * settles this many milliseconds or more after the check was called,
@@ -1560,7 +1687,15 @@ export interface PlatformHostOptions<TDb> {
1560
1687
  */
1561
1688
  adapterDeadlineMs?: number;
1562
1689
  }
1690
+ export interface ApprovalExpiryHost {
1691
+ expireDueApprovals(input?: ExpireDueApprovalsInput): Promise<{
1692
+ expired: string[];
1693
+ }>;
1694
+ }
1563
1695
  export interface GovernedActionHost {
1696
+ expireDueApprovals?(input?: ExpireDueApprovalsInput): Promise<{
1697
+ expired: string[];
1698
+ }>;
1564
1699
  submitAction(input: SubmitActionInput): Promise<SubmitActionResult>;
1565
1700
  /** Whether this Host's registry contains an action with a non-empty staged-descendant allowlist. */
1566
1701
  readonly hasStagedInvocationActions?: boolean;
@@ -1605,7 +1740,7 @@ export interface OutboxRequeueHost {
1605
1740
  }
1606
1741
  //#endregion
1607
1742
  //#region src/host.d.ts
1608
- export declare function createGovernedActionHost<TDb>(options: PlatformHostOptions<TDb>): GovernedActionHost & OutboxRequeueHost;
1743
+ export declare function createGovernedActionHost<TDb>(options: PlatformHostOptions<TDb>): GovernedActionHost & OutboxRequeueHost & ApprovalExpiryHost;
1609
1744
  //#endregion
1610
1745
  //#region src/memory-store.d.ts
1611
1746
  export interface MemoryPlatformHostTransactionProvider<TDb> {
@@ -1615,6 +1750,8 @@ export interface MemoryPlatformHostTransactionProvider<TDb> {
1615
1750
  export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
1616
1751
  readonly db: TDb;
1617
1752
  readonly transactionalOutbox: boolean;
1753
+ readonly transactionalApprovalLifecycle: boolean;
1754
+ private readonly approvalTransactionProvider;
1618
1755
  readonly transactionalStagedInvocations: boolean;
1619
1756
  transactionWithEvents?: <TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>) => Promise<TResult>;
1620
1757
  private adapterCommit;
@@ -1645,7 +1782,10 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1645
1782
  getActionInvocationByStagedOrigin(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
1646
1783
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
1647
1784
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1785
+ private runApprovalTransaction;
1786
+ parkApproval(input: ParkApprovalInput): Promise<ApprovalDecisionTransitionResult>;
1648
1787
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
1788
+ expireDueApprovals(input: ExpireDueApprovalsStoreInput): Promise<ActionInvocationRecord[]>;
1649
1789
  appendPolicyEvaluation(record: PolicyEvaluationRecord): Promise<void>;
1650
1790
  recordMutationGovernance(actionInvocationId: string, tenantId: string, spaceId: string, context: Pick<ActionInvocationRecord, "mutationFootprint" | "executionPrincipal"> & {
1651
1791
  footprint: NonNullable<ActionInvocationRecord["mutationFootprint"]>;
@@ -1832,6 +1972,7 @@ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatfo
1832
1972
  private readonly sql;
1833
1973
  readonly transactionWithEvents?: AtomicMutationPlatformHostStore<TDb>["transactionWithEvents"];
1834
1974
  readonly transactionalOutbox: boolean;
1975
+ readonly transactionalApprovalLifecycle: boolean;
1835
1976
  readonly transactionalStagedInvocations: boolean;
1836
1977
  constructor(db: TDb, sql: PlatformHostSqlClient, transactionProvider?: PostgresPlatformHostTransactionProvider<TDb>);
1837
1978
  ensureSchema(): Promise<void>;
@@ -1843,6 +1984,8 @@ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatfo
1843
1984
  getActionInvocationByStagedOrigin(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
1844
1985
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
1845
1986
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1987
+ parkApproval(input: ParkApprovalInput): Promise<ApprovalDecisionTransitionResult>;
1988
+ expireDueApprovals(input: ExpireDueApprovalsStoreInput): Promise<ActionInvocationRecord[]>;
1846
1989
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
1847
1990
  appendPolicyEvaluation(record: PolicyEvaluationRecord): Promise<void>;
1848
1991
  recordMutationGovernance(actionInvocationId: string, tenantId: string, spaceId: string, context: {
package/dist/index.d.ts 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, StagedInvocationRequest, canonicalJson } from "@fabricorg/platform";
1
+ import { ActionDefinition, ActionId, ActionStatus, ActorType, AdapterCircuitBreakerProvider, AdapterImplementation, AgentActionPolicyContext, AgentActionPolicyDecision, 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. */
@@ -183,7 +183,7 @@ export interface IdempotencyConflict {
183
183
  code: "IDEMPOTENCY_CONFLICT";
184
184
  idempotencyKey: string;
185
185
  existingInvocationId: string;
186
- reasons: Array<"actor" | "authority_binding" | "action_version" | "parameters">;
186
+ reasons: Array<"actor" | "authority_binding" | "action_version" | "parameters" | "agent_proposal">;
187
187
  }
188
188
  export declare class IdempotencyConflictError extends Error {
189
189
  readonly conflict: IdempotencyConflict;
@@ -251,7 +251,7 @@ export interface PendingAssetEvent {
251
251
  }
252
252
  /** JSON preparation committed with the handler before adapter execution. */
253
253
  export interface FinalizationCheckpoint {
254
- protocol: "atomic-after-adapters-v1";
254
+ protocol: "atomic-after-adapters-v1" | "atomic-after-adapters-v2";
255
255
  actionVersion: number;
256
256
  steps: {
257
257
  adapterType: string;
@@ -259,6 +259,8 @@ export interface FinalizationCheckpoint {
259
259
  }[];
260
260
  params: import("@fabricorg/platform").FinalizationJson;
261
261
  handlerData: import("@fabricorg/platform").FinalizationRecord;
262
+ /** Private adapter input preparation; retained until settlement, including reconciliation. */
263
+ adapterPreparation?: import("@fabricorg/platform").FinalizationRecord;
262
264
  handlerEvents: (Omit<PendingAssetEvent, "payload"> & {
263
265
  payload: import("@fabricorg/platform").FinalizationJson;
264
266
  })[];
@@ -274,7 +276,21 @@ export interface StagedInvocationOrigin {
274
276
  parentInvocationId: string;
275
277
  stageKey: string;
276
278
  }
279
+ export interface AgentProposal {
280
+ confidence?: number;
281
+ riskTier?: AgentActionRiskTier;
282
+ agentSessionId?: string;
283
+ agentRunId?: string;
284
+ reason?: string;
285
+ evidence?: ApprovalEvidence;
286
+ }
287
+ export interface HostHitlEvaluatorContext extends AgentActionPolicyContext {
288
+ actionInvocationId: string;
289
+ /** The durable (post-redaction) invocation record as read before evaluation. */
290
+ invocation: Readonly<ActionInvocationRecord>;
291
+ }
277
292
  export interface ActionInvocationRecord {
293
+ readonly agentProposal?: AgentProposal;
278
294
  id: string;
279
295
  tenantId: string;
280
296
  spaceId: string;
@@ -337,6 +353,11 @@ export interface ActionInvocationRecord {
337
353
  hitlReason?: string;
338
354
  hitlPolicyVersion?: string;
339
355
  approvalDecision?: PersistedActionApprovalDecision;
356
+ approvalExpiresAt?: Date;
357
+ approvalExpiry?: {
358
+ expiresAt: Date;
359
+ expiredAt: Date;
360
+ };
340
361
  mutationFootprint?: MutationFootprint;
341
362
  executionPrincipal?: ExecutionPrincipal;
342
363
  runtimeEvidence?: GovernanceRuntimeEvidence;
@@ -359,14 +380,50 @@ export interface AuthorizationReconciliationOutcome {
359
380
  provenance?: Omit<InvocationProvenance, "traceAttributes">;
360
381
  message: string;
361
382
  }
383
+ export interface HostHitlDecision extends AgentActionPolicyDecision {
384
+ /** Absolute approval deadline; a valid Date, including a deadline already due. */
385
+ approvalExpiresAt?: Date;
386
+ }
362
387
  export interface HitlDecisionEvidence {
388
+ approvalExpiresAt?: Date;
363
389
  route: AgentActionRoute;
364
390
  riskTier: AgentActionRiskTier;
365
391
  reason: string;
366
392
  policyVersion?: string;
367
393
  evaluatedAt: Date;
368
394
  }
395
+ export type ApprovalEvidenceValue = null | boolean | number | string | ApprovalEvidenceValue[] | {
396
+ [key: string]: ApprovalEvidenceValue;
397
+ };
398
+ export type ApprovalEvidence = {
399
+ [key: string]: ApprovalEvidenceValue;
400
+ };
401
+ /** Maximum serialized UTF-8 evidence size. */
402
+ export declare const MAX_APPROVAL_EVIDENCE_BYTES: number;
403
+ export declare const APPROVAL_REQUESTED_EVENT: "ApprovalRequested";
404
+ export declare const APPROVAL_DECIDED_EVENT: "ApprovalDecided";
405
+ export declare const APPROVAL_EXPIRED_EVENT: "ApprovalExpired";
406
+ export interface ApprovalRequestedPayload {
407
+ route: AgentActionRoute;
408
+ riskTier: AgentActionRiskTier;
409
+ reason: string;
410
+ expiresAt?: string;
411
+ }
412
+ export interface ApprovalDecidedPayload {
413
+ approved: boolean;
414
+ approverId: string;
415
+ approverType: ActorType;
416
+ reason?: string;
417
+ decidedAt: string;
418
+ evidencePresent: boolean;
419
+ }
420
+ export interface ApprovalExpiredPayload {
421
+ expiresAt: string;
422
+ expiredAt: string;
423
+ }
369
424
  export interface ActionApprovalDecision {
425
+ /** Verification references only; credentials and tokens must not be included. */
426
+ evidence?: ApprovalEvidence;
370
427
  approved: boolean;
371
428
  approverId: string;
372
429
  approverType: ActorType;
@@ -625,6 +682,8 @@ export interface PlatformHostStore<TDb> {
625
682
  * back when `run` throws.
626
683
  */
627
684
  export interface PlatformHostMutationTransaction<TDb> {
685
+ /** Conditional SQL approval write on the transaction connection; lifecycle events are appended by the caller in this unit of work. */
686
+ beginApprovalDecision?(input: Omit<BeginApprovalDecisionInput, "lifecycleEvent">): Promise<ApprovalDecisionTransitionResult>;
628
687
  readonly db: TDb;
629
688
  /**
630
689
  * Read and lock an invocation until this transaction completes.
@@ -1059,7 +1118,34 @@ export interface OutboxRequeueAuthorizationInput {
1059
1118
  export interface AtomicMutationPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
1060
1119
  transactionWithEvents<TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>): Promise<TResult>;
1061
1120
  }
1121
+ export interface ApprovalLifecycleEvent {
1122
+ event: AssetEventEnvelope;
1123
+ metadata: OutboxEventMetadata;
1124
+ }
1125
+ /** Called within the transition transaction. Must be pure and return audit-safe data. Sequence is assigned by the store. */
1126
+ export type ApprovalLifecycleEventFactory = (invocation: ActionInvocationRecord, kind: "requested" | "decided" | "expired") => ApprovalLifecycleEvent;
1127
+ export interface ExpireDueApprovalsInput {
1128
+ tenantId?: string;
1129
+ spaceId?: string;
1130
+ now?: Date;
1131
+ limit?: number;
1132
+ }
1133
+ export interface ExpireDueApprovalsStoreInput extends ExpireDueApprovalsInput {
1134
+ now: Date;
1135
+ lifecycleEvent?: ApprovalLifecycleEventFactory;
1136
+ }
1137
+ export interface ParkApprovalInput {
1138
+ approvalExpiresAt?: Date;
1139
+ actionInvocationId: string;
1140
+ tenantId: string;
1141
+ spaceId: string;
1142
+ now: Date;
1143
+ leaseOwner?: string;
1144
+ leaseToken?: number;
1145
+ lifecycleEvent?: ApprovalLifecycleEventFactory;
1146
+ }
1062
1147
  export interface BeginApprovalDecisionInput {
1148
+ lifecycleEvent?: ApprovalLifecycleEventFactory;
1063
1149
  actionInvocationId: string;
1064
1150
  tenantId: string;
1065
1151
  spaceId: string;
@@ -1074,7 +1160,37 @@ export interface ApprovalDecisionTransitionResult {
1074
1160
  }
1075
1161
  /** Optional store capability required only when agent HITL or approval resume is used. */
1076
1162
  export interface ApprovalPlatformHostStore<TDb> extends PlatformHostStore<TDb> {
1163
+ /** True only when approval transitions and lifecycle outbox events share a transaction. */
1164
+ readonly transactionalApprovalLifecycle?: boolean;
1165
+ /**
1166
+ * Conditionally park an executable invocation only with its matching lease owner/generation
1167
+ * (when leased) and no prior approval decision. Persist the deadline supplied in input.
1168
+ * Serialize with beginApprovalDecision and expireDueApprovals so each waiting row settles
1169
+ * exactly once across concurrent decisions and sweeps; losing transitions report applied: false.
1170
+ * When lifecycleEvent is supplied, its event/outbox writes and the state change must commit
1171
+ * in the same atomic unit of work, or roll back together.
1172
+ */
1173
+ parkApproval?(input: ParkApprovalInput): Promise<ApprovalDecisionTransitionResult>;
1174
+ /**
1175
+ * Atomically claim and expire scoped waiting rows whose input.now >= approvalExpiresAt.
1176
+ * Set failed with approvalExpiry and no approvalDecision. Each waiting row must settle
1177
+ * exactly once across concurrent sweeps and beginApprovalDecision calls; return only rows
1178
+ * this sweep settled (competing decision transitions report applied: false).
1179
+ * When lifecycleEvent is supplied, the events/outbox writes and state changes must commit
1180
+ * in the same atomic unit of work, or roll back together.
1181
+ */
1182
+ expireDueApprovals?(input: ExpireDueApprovalsStoreInput): Promise<ActionInvocationRecord[]>;
1077
1183
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1184
+ /**
1185
+ * Atomically decide a waiting row, comparing the stored deadline with input.now inside
1186
+ * the serialized transition. A decision at or after that deadline (input.now >=
1187
+ * approvalExpiresAt) must never be applied: expire the row to failed with approvalExpiry
1188
+ * and no approvalDecision instead. The caller's decision.decidedAt does not extend it.
1189
+ * Each waiting row must settle exactly once across concurrent decisions and expiry sweeps;
1190
+ * a losing transition reports applied: false. With lifecycleEvent, the selected decided
1191
+ * or expired event/outbox writes and state change must commit in the same atomic unit
1192
+ * of work, or roll back together. Required when implementing parkApproval/expireDueApprovals.
1193
+ */
1078
1194
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
1079
1195
  }
1080
1196
  /** Optional durable capabilities used when external-governance enrichment is configured. */
@@ -1124,7 +1240,7 @@ export interface ClaimActionInvocationsInput {
1124
1240
  * - `claimActionInvocations` must never claim a running invocation without a
1125
1241
  * lease, which is what a parked invocation is.
1126
1242
  */
1127
- export type ActionInvocationPatch = Partial<Pick<ActionInvocationRecord, "finalizationCheckpoint" | "deferredBeforeEffectAttempt" | "status" | "result" | "error" | "authorizationReconciliation" | "adapterReconciliation" | "pendingStateTransition" | "pendingCompletion" | "externalCompletion">>;
1243
+ export type ActionInvocationPatch = Partial<Pick<ActionInvocationRecord, "approvalExpiresAt" | "approvalExpiry" | "approvalDecision" | "leaseOwner" | "leaseExpiresAt" | "leaseToken" | "attemptCount" | "finalizationCheckpoint" | "deferredBeforeEffectAttempt" | "status" | "result" | "error" | "authorizationReconciliation" | "adapterReconciliation" | "pendingStateTransition" | "pendingCompletion" | "externalCompletion">>;
1128
1244
  export interface UpdateLeasedActionInvocationInput {
1129
1245
  id: string;
1130
1246
  tenantId: string;
@@ -1233,9 +1349,12 @@ export interface PlatformActionDispatcher {
1233
1349
  * `provenance` and `authorizationBinding` are also read once there and
1234
1350
  * replaced by Host's own copies. For any other action Host uses those three
1235
1351
  * as the caller's objects, read where and as often as before (README,
1236
- * "Submission snapshot").
1352
+ * "Submission snapshot"). `agentProposal` is read once and deeply copied before
1353
+ * the first await for all actions, independently of `admitSubmission`.
1237
1354
  */
1238
1355
  export interface SubmitActionInput {
1356
+ /** Read once and deeply copied before the first await, regardless of admission. */
1357
+ agentProposal?: AgentProposal;
1239
1358
  actionId: ActionId;
1240
1359
  parameters: Record<string, unknown>;
1241
1360
  tenantId: string;
@@ -1322,6 +1441,8 @@ export interface PlatformActionWorkerCycleResult {
1322
1441
  failed: number;
1323
1442
  /** Present when claimed agent work was durably parked instead of failing. */
1324
1443
  waitingForApproval?: number;
1444
+ /** Due approval waits settled to failed during this cycle; present when positive. */
1445
+ expiredApprovals?: number;
1325
1446
  /**
1326
1447
  * Present only when > 0: claimed invocations whose execution threw a
1327
1448
  * RetryableExecutionError (counted by error type, not by where it was thrown).
@@ -1446,7 +1567,11 @@ export interface PlatformHostOptions<TDb> {
1446
1567
  shouldPublish?(event: AssetEventEnvelope): boolean;
1447
1568
  };
1448
1569
  /** Optional vertical-owned evaluator. When absent, agent behavior is unchanged. */
1449
- hitlEvaluator?: AgentActionPolicyEvaluator;
1570
+ hitlEvaluator?: (context: HostHitlEvaluatorContext) => HostHitlDecision | Promise<HostHitlDecision>;
1571
+ /** Positive integer milliseconds, used when the evaluator supplies no absolute deadline. */
1572
+ approvalDefaultTtlMs?: number;
1573
+ /** Atomic approval lifecycle events and outbox writes; defaults to false. */
1574
+ approvalLifecycleEvents?: boolean;
1450
1575
  /** Durable evaluator/ruleset version recorded with every HITL decision. */
1451
1576
  hitlPolicyVersion?: string;
1452
1577
  /** Optional portable resolver; provider-specific implementations belong in integration packages. */
@@ -1499,6 +1624,8 @@ export interface PlatformHostOptions<TDb> {
1499
1624
  * copy, after the check admitted the submission.
1500
1625
  */
1501
1626
  redactActionParameters?: (actionId: ActionId, parameters: Record<string, unknown>) => Record<string, unknown>;
1627
+ /** Runs beside parameter redaction; output is validated and copied before persistence. */
1628
+ redactAgentProposal?: (actionId: ActionId, proposal: AgentProposal) => AgentProposal;
1502
1629
  /**
1503
1630
  * The bound on an action's `admitSubmission` check. An answer that
1504
1631
  * settles this many milliseconds or more after the check was called,
@@ -1560,7 +1687,15 @@ export interface PlatformHostOptions<TDb> {
1560
1687
  */
1561
1688
  adapterDeadlineMs?: number;
1562
1689
  }
1690
+ export interface ApprovalExpiryHost {
1691
+ expireDueApprovals(input?: ExpireDueApprovalsInput): Promise<{
1692
+ expired: string[];
1693
+ }>;
1694
+ }
1563
1695
  export interface GovernedActionHost {
1696
+ expireDueApprovals?(input?: ExpireDueApprovalsInput): Promise<{
1697
+ expired: string[];
1698
+ }>;
1564
1699
  submitAction(input: SubmitActionInput): Promise<SubmitActionResult>;
1565
1700
  /** Whether this Host's registry contains an action with a non-empty staged-descendant allowlist. */
1566
1701
  readonly hasStagedInvocationActions?: boolean;
@@ -1605,7 +1740,7 @@ export interface OutboxRequeueHost {
1605
1740
  }
1606
1741
  //#endregion
1607
1742
  //#region src/host.d.ts
1608
- export declare function createGovernedActionHost<TDb>(options: PlatformHostOptions<TDb>): GovernedActionHost & OutboxRequeueHost;
1743
+ export declare function createGovernedActionHost<TDb>(options: PlatformHostOptions<TDb>): GovernedActionHost & OutboxRequeueHost & ApprovalExpiryHost;
1609
1744
  //#endregion
1610
1745
  //#region src/memory-store.d.ts
1611
1746
  export interface MemoryPlatformHostTransactionProvider<TDb> {
@@ -1615,6 +1750,8 @@ export interface MemoryPlatformHostTransactionProvider<TDb> {
1615
1750
  export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatformHostStore<TDb>, ApprovalPlatformHostStore<TDb>, GovernancePlatformHostStore<TDb>, OutboxPlatformHostStore<TDb>, OutboxRequeuePlatformHostStore<TDb>, StagedInvocationPlatformHostStore<TDb> {
1616
1751
  readonly db: TDb;
1617
1752
  readonly transactionalOutbox: boolean;
1753
+ readonly transactionalApprovalLifecycle: boolean;
1754
+ private readonly approvalTransactionProvider;
1618
1755
  readonly transactionalStagedInvocations: boolean;
1619
1756
  transactionWithEvents?: <TResult>(run: (transaction: PlatformHostMutationTransaction<TDb>) => Promise<TResult>) => Promise<TResult>;
1620
1757
  private adapterCommit;
@@ -1645,7 +1782,10 @@ export declare class MemoryPlatformHostStore<TDb> implements RecoverablePlatform
1645
1782
  getActionInvocationByStagedOrigin(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
1646
1783
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
1647
1784
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1785
+ private runApprovalTransaction;
1786
+ parkApproval(input: ParkApprovalInput): Promise<ApprovalDecisionTransitionResult>;
1648
1787
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
1788
+ expireDueApprovals(input: ExpireDueApprovalsStoreInput): Promise<ActionInvocationRecord[]>;
1649
1789
  appendPolicyEvaluation(record: PolicyEvaluationRecord): Promise<void>;
1650
1790
  recordMutationGovernance(actionInvocationId: string, tenantId: string, spaceId: string, context: Pick<ActionInvocationRecord, "mutationFootprint" | "executionPrincipal"> & {
1651
1791
  footprint: NonNullable<ActionInvocationRecord["mutationFootprint"]>;
@@ -1832,6 +1972,7 @@ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatfo
1832
1972
  private readonly sql;
1833
1973
  readonly transactionWithEvents?: AtomicMutationPlatformHostStore<TDb>["transactionWithEvents"];
1834
1974
  readonly transactionalOutbox: boolean;
1975
+ readonly transactionalApprovalLifecycle: boolean;
1835
1976
  readonly transactionalStagedInvocations: boolean;
1836
1977
  constructor(db: TDb, sql: PlatformHostSqlClient, transactionProvider?: PostgresPlatformHostTransactionProvider<TDb>);
1837
1978
  ensureSchema(): Promise<void>;
@@ -1843,6 +1984,8 @@ export declare class PostgresPlatformHostStore<TDb> implements RecoverablePlatfo
1843
1984
  getActionInvocationByStagedOrigin(tenantId: string, spaceId: string, origin: StagedInvocationOrigin): Promise<ActionInvocationRecord | undefined>;
1844
1985
  updateActionInvocation(id: string, tenantId: string, spaceId: string, patch: ActionInvocationPatch): Promise<void>;
1845
1986
  recordHitlDecision(actionInvocationId: string, tenantId: string, spaceId: string, decision: HitlDecisionEvidence): Promise<ActionInvocationRecord>;
1987
+ parkApproval(input: ParkApprovalInput): Promise<ApprovalDecisionTransitionResult>;
1988
+ expireDueApprovals(input: ExpireDueApprovalsStoreInput): Promise<ActionInvocationRecord[]>;
1846
1989
  beginApprovalDecision(input: BeginApprovalDecisionInput): Promise<ApprovalDecisionTransitionResult>;
1847
1990
  appendPolicyEvaluation(record: PolicyEvaluationRecord): Promise<void>;
1848
1991
  recordMutationGovernance(actionInvocationId: string, tenantId: string, spaceId: string, context: {