@fabricorg/platform-host 2.0.1 → 4.0.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.js CHANGED
@@ -1,9 +1,66 @@
1
- import { AdapterRegistry, resolveAction, createFabricId, assertGovernanceRuntimeEvidence, FABRIC_GOVERNANCE_CONTRACT_VERSION, assertMutationGovernanceContext, evaluatePolicyDefinitions, aggregatePolicyOutcomes, validateTransition, executeWithAdapterRetry, resolveStateMachine } from '@fabricorg/platform';
1
+ import { AdapterRegistry, createFabricId, assertGovernanceRuntimeEvidence, FABRIC_GOVERNANCE_CONTRACT_VERSION, assertMutationGovernanceContext, evaluatePolicyDefinitions, aggregatePolicyOutcomes, validateStateMachineTransition, executeWithAdapterRetry } from '@fabricorg/platform';
2
+ import { createHash } from 'crypto';
2
3
 
3
4
  // src/host.ts
4
5
 
5
6
  // src/types.ts
6
7
  var PLATFORM_HOST_CONTRACT_VERSION = 2;
8
+ var IdempotencyConflictError = class extends Error {
9
+ constructor(conflict) {
10
+ super(`Idempotency key "${conflict.idempotencyKey}" conflicts with invocation ${conflict.existingInvocationId}: ${conflict.reasons.join(", ")}`);
11
+ this.conflict = conflict;
12
+ this.name = "IdempotencyConflictError";
13
+ }
14
+ conflict;
15
+ code = "IDEMPOTENCY_CONFLICT";
16
+ };
17
+ var PARAMETER_DIGEST_ALGORITHM = "fabric-canonical-json-sha256-v1";
18
+ function canonicalJson(value) {
19
+ return JSON.stringify(sort(value));
20
+ }
21
+ function sort(value) {
22
+ if (Array.isArray(value)) return value.map(sort);
23
+ if (value && typeof value === "object") {
24
+ return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, child]) => [key, sort(child)]));
25
+ }
26
+ return value;
27
+ }
28
+ function digestParameters(parameters) {
29
+ return createHash("sha256").update(canonicalJson(parameters)).digest("hex");
30
+ }
31
+ var ATTRIBUTE_NAME = /^[a-z][a-z0-9-]*(?:\.[A-Za-z][A-Za-z0-9_-]*)+$/;
32
+ var INVOCATION_SOURCE = /^(?:ui|sdui|api|agent|worker|system|[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)+)$/;
33
+ function normalizeProvenance(input, options) {
34
+ if (!input) return void 0;
35
+ if (!INVOCATION_SOURCE.test(input.source)) throw new Error(`Invalid invocation provenance source: ${input.source}`);
36
+ const maxAttributes = options?.maxAttributes ?? 16;
37
+ const maxValueLength = options?.maxValueLength ?? 256;
38
+ const allowlist = new Set(options?.auditAttributeAllowlist ?? []);
39
+ const validate = (attributes, durable) => {
40
+ if (!attributes) return void 0;
41
+ const entries = Object.entries(attributes);
42
+ if (entries.length > maxAttributes) throw new Error(`Invocation provenance exceeds ${maxAttributes} attributes.`);
43
+ for (const [key, value] of entries) {
44
+ if (!ATTRIBUTE_NAME.test(key)) throw new Error(`Invalid provenance attribute name: ${key}`);
45
+ if (value.length > maxValueLength) throw new Error(`Invocation provenance attribute "${key}" exceeds ${maxValueLength} characters.`);
46
+ if (durable && !allowlist.has(key)) throw new Error(`Audit provenance attribute "${key}" is not allowlisted.`);
47
+ }
48
+ return Object.fromEntries(entries);
49
+ };
50
+ validate(input.traceAttributes, false);
51
+ const redactedAuditAttributes = input.auditAttributes ? options?.redactAuditAttributes?.({ ...input.auditAttributes }) ?? input.auditAttributes : void 0;
52
+ const auditAttributes = validate(redactedAuditAttributes, true);
53
+ options?.onTrace?.({
54
+ ...input,
55
+ ...auditAttributes ? { auditAttributes } : { auditAttributes: void 0 }
56
+ });
57
+ return {
58
+ source: input.source,
59
+ correlationId: input.correlationId,
60
+ ...input.causationId ? { causationId: input.causationId } : {},
61
+ ...auditAttributes ? { auditAttributes } : {}
62
+ };
63
+ }
7
64
 
8
65
  // src/host.ts
9
66
  var DEFAULT_EXTRACT_EVENTS = (data) => {
@@ -24,9 +81,15 @@ function createGovernedActionHost(options) {
24
81
  for (const adapter of options.adapters ?? []) adapters.register(adapter);
25
82
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
26
83
  const extractEvents = options.extractEvents ?? DEFAULT_EXTRACT_EVENTS;
27
- const actionResolver = options.resolveAction ?? resolveAction;
84
+ const configuredActionResolver = options.resolveAction ?? options.registry?.resolveAction;
85
+ if (!configuredActionResolver) throw new Error("Platform Host requires an explicit module registry or resolveAction function.");
86
+ const actionResolver = configuredActionResolver;
87
+ const stateMachineResolver = options.registry?.resolveStateMachine ?? (() => void 0);
28
88
  const eventResultFields = options.eventResultFields ?? ["_events"];
29
89
  async function submitAction(input) {
90
+ if (input.executionReason && !["initial", "offline_replay"].includes(input.executionReason)) {
91
+ throw new Error(`Unsupported submitted execution reason: ${input.executionReason}`);
92
+ }
30
93
  const action = actionResolver(input.actionId);
31
94
  if (!action) throw new Error(`Unknown action: ${input.actionId}`);
32
95
  const authorizationInput = toAuthorizationInput(action, input);
@@ -37,8 +100,16 @@ function createGovernedActionHost(options) {
37
100
  throw new Error(`Actor ${input.actorId} is not authorized for action ${input.actionId}`);
38
101
  }
39
102
  const actionInvocationId = createFabricId("act");
40
- const correlationId = input.correlationId ?? createFabricId("corr");
103
+ if (input.correlationId && input.provenance?.correlationId && input.correlationId !== input.provenance.correlationId) throw new Error("Invocation provenance correlationId does not match submission correlationId.");
104
+ if (input.causationId && input.provenance?.causationId && input.causationId !== input.provenance.causationId) throw new Error("Invocation provenance causationId does not match submission causationId.");
105
+ const correlationId = input.correlationId ?? input.provenance?.correlationId ?? createFabricId("corr");
41
106
  const durableParameters = options.redactActionParameters ? options.redactActionParameters(input.actionId, input.parameters) : input.parameters;
107
+ const parameterDigest = digestParameters(input.parameters);
108
+ if (input.authorizationBinding) validateAuthorizationBinding(input, parameterDigest, action.execution?.authorityMoment, now());
109
+ const durableProvenance = normalizeProvenance(
110
+ input.provenance,
111
+ options.provenance
112
+ );
42
113
  const runtimeEvidence = {
43
114
  governanceContractVersion: FABRIC_GOVERNANCE_CONTRACT_VERSION,
44
115
  hostContractVersion: PLATFORM_HOST_CONTRACT_VERSION,
@@ -58,12 +129,41 @@ function createGovernedActionHost(options) {
58
129
  result: {},
59
130
  runtimeEvidence,
60
131
  correlationId,
61
- ...input.causationId ? { causationId: input.causationId } : {},
132
+ ...input.causationId ?? input.provenance?.causationId ? { causationId: input.causationId ?? input.provenance?.causationId } : {},
62
133
  ...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {},
134
+ ...input.idempotencyKey ? {
135
+ parameterDigest,
136
+ parameterDigestAlgorithm: PARAMETER_DIGEST_ALGORITHM,
137
+ idempotencyActorId: input.actorId,
138
+ ...input.authorizationBindingId ? { idempotencyAuthorizationBindingId: input.authorizationBindingId } : {}
139
+ } : {},
140
+ ...durableProvenance ? { provenance: durableProvenance } : {},
141
+ ...input.authorizationBinding ? { authorizationBinding: input.authorizationBinding } : {},
142
+ ...input.executionReason ? { executionReason: input.executionReason } : {},
63
143
  ...input.authorizationBindingId ? { authorizationBindingId: input.authorizationBindingId } : {}
64
144
  });
145
+ if (durableInvocation.id !== actionInvocationId && input.idempotencyKey) {
146
+ const conflict = idempotencyConflict(durableInvocation, {
147
+ actorId: input.actorId,
148
+ authorizationBindingId: input.authorizationBindingId,
149
+ actionVersion: action.version,
150
+ parameterDigest,
151
+ idempotencyKey: input.idempotencyKey
152
+ });
153
+ if (!durableInvocation.parameterDigest) {
154
+ options.idempotency?.onLegacyRecord?.(durableInvocation);
155
+ const enforceableLegacyConflict = conflict && { ...conflict, reasons: conflict.reasons.filter((reason) => reason !== "parameters") };
156
+ if (enforceableLegacyConflict && enforceableLegacyConflict.reasons.length) {
157
+ options.idempotency?.onConflict?.(enforceableLegacyConflict);
158
+ if ((options.idempotency?.conflictMode ?? "enforce") === "enforce") throw new IdempotencyConflictError(enforceableLegacyConflict);
159
+ }
160
+ } else if (conflict) {
161
+ options.idempotency?.onConflict?.(conflict);
162
+ if ((options.idempotency?.conflictMode ?? "enforce") === "enforce") throw new IdempotencyConflictError(conflict);
163
+ }
164
+ }
65
165
  const durableWorkflowId = `action-invocation-${durableInvocation.id}`;
66
- if (durableInvocation.id !== actionInvocationId && isTerminal(durableInvocation.status)) {
166
+ if (durableInvocation.id !== actionInvocationId) {
67
167
  return {
68
168
  actionInvocationId: durableInvocation.id,
69
169
  status: durableInvocation.status,
@@ -71,7 +171,8 @@ function createGovernedActionHost(options) {
71
171
  result: durableInvocation.result,
72
172
  ...durableInvocation.error ? { error: durableInvocation.error } : {},
73
173
  ...durableInvocation.hitlRoute ? { hitlRoute: durableInvocation.hitlRoute } : {},
74
- ...durableInvocation.hitlRiskTier ? { hitlRiskTier: durableInvocation.hitlRiskTier } : {}
174
+ ...durableInvocation.hitlRiskTier ? { hitlRiskTier: durableInvocation.hitlRiskTier } : {},
175
+ ...durableInvocation.authorizationReconciliation ? { reconciliation: durableInvocation.authorizationReconciliation } : {}
75
176
  };
76
177
  }
77
178
  if (options.dispatcher) {
@@ -219,7 +320,7 @@ function createGovernedActionHost(options) {
219
320
  }
220
321
  }
221
322
  const authorizationInput = toAuthorizationInput(action, invocation);
222
- const executionReason = executionReasonOverride ?? (invocation.attemptCount > 1 || resumingRunningInvocation && invocation.attemptCount === 0 ? "recovery" : "initial");
323
+ const executionReason = executionReasonOverride ?? (invocation.attemptCount > 1 || resumingRunningInvocation && invocation.attemptCount === 0 ? "recovery" : invocation.executionReason ?? "initial");
223
324
  if (!await options.authorization.checkEntitlement(authorizationInput)) {
224
325
  return fail(
225
326
  invocation,
@@ -227,7 +328,32 @@ function createGovernedActionHost(options) {
227
328
  `Module "${action.namespace}" is no longer enabled for tenant ${tenantId}`
228
329
  );
229
330
  }
230
- const executionAuthorized = options.authorization.authorizeExecution ? await options.authorization.authorizeExecution({
331
+ const authorityMoment = action.execution?.authorityMoment;
332
+ if (authorityMoment === "capture" && !invocation.authorizationBinding) {
333
+ const reconciliation = {
334
+ kind: "authorization_missing_capture_evidence",
335
+ governingMoment: authorityMoment,
336
+ executionReason,
337
+ ...invocation.provenance ? { provenance: invocation.provenance } : {},
338
+ message: `Action ${action.actionId} requires durable capture-time authorization evidence`
339
+ };
340
+ await options.store.updateActionInvocation(actionInvocationId, tenantId, spaceId, { status: "reconciliation_required", error: reconciliation.message, authorizationReconciliation: reconciliation });
341
+ return { actionInvocationId, status: "reconciliation_required", error: reconciliation.message, reconciliation };
342
+ }
343
+ const bindingExpired = authorityMoment !== "capture" && invocation.authorizationBinding?.expiresAt !== void 0 && Date.parse(invocation.authorizationBinding.expiresAt) <= now().getTime();
344
+ if (bindingExpired) {
345
+ const reconciliation = {
346
+ kind: "authorization_expired",
347
+ governingMoment: authorityMoment ?? "both",
348
+ executionReason,
349
+ ...invocation.authorizationBindingId ? { authorizationBindingId: invocation.authorizationBindingId } : {},
350
+ ...invocation.provenance ? { provenance: invocation.provenance } : {},
351
+ message: `Authorization binding for action ${action.actionId} expired before execution`
352
+ };
353
+ await options.store.updateActionInvocation(actionInvocationId, tenantId, spaceId, { status: "reconciliation_required", error: reconciliation.message, authorizationReconciliation: reconciliation });
354
+ return { actionInvocationId, status: "reconciliation_required", error: reconciliation.message, reconciliation };
355
+ }
356
+ const executionAuthorized = authorityMoment === "capture" ? invocation.authorizationBinding !== void 0 : options.authorization.authorizeExecution ? await options.authorization.authorizeExecution({
231
357
  ...authorizationInput,
232
358
  actionInvocationId,
233
359
  parameters: parsed.data,
@@ -235,11 +361,19 @@ function createGovernedActionHost(options) {
235
361
  executionReason
236
362
  }) : await options.authorization.authorize(authorizationInput);
237
363
  if (!executionAuthorized) {
238
- return fail(
239
- invocation,
240
- "failed",
241
- `Actor ${invocation.actorId} is not authorized to execute action ${action.actionId}`
242
- );
364
+ if (!action.execution) {
365
+ return fail(invocation, "failed", `Actor ${invocation.actorId} is not authorized to execute action ${action.actionId}`);
366
+ }
367
+ const reconciliation = {
368
+ kind: "authorization_denied",
369
+ governingMoment: action.execution?.authorityMoment ?? "both",
370
+ executionReason,
371
+ ...invocation.authorizationBindingId ? { authorizationBindingId: invocation.authorizationBindingId } : {},
372
+ ...invocation.provenance ? { provenance: invocation.provenance } : {},
373
+ message: `Actor ${invocation.actorId} is not authorized to execute action ${action.actionId}`
374
+ };
375
+ await options.store.updateActionInvocation(actionInvocationId, tenantId, spaceId, { status: "reconciliation_required", error: reconciliation.message, authorizationReconciliation: reconciliation });
376
+ return { actionInvocationId, status: "reconciliation_required", error: reconciliation.message, reconciliation };
243
377
  }
244
378
  const definitions = options.resolvePolicies ? await options.resolvePolicies({
245
379
  ...authorizationInput,
@@ -255,6 +389,7 @@ function createGovernedActionHost(options) {
255
389
  parameters: parsed.data,
256
390
  db: options.store.db,
257
391
  services: options.services,
392
+ resolveCodePolicy: options.registry?.resolvePolicy,
258
393
  mode: "execute",
259
394
  now: now()
260
395
  });
@@ -307,10 +442,11 @@ function createGovernedActionHost(options) {
307
442
  spaceId,
308
443
  binding.entityType,
309
444
  entityId
310
- ) ?? initialState(binding.entityType) : initialState(binding.entityType);
445
+ ) ?? initialState(stateMachineResolver(binding.entityType)) : initialState(stateMachineResolver(binding.entityType));
311
446
  const targetState = typeof binding.targetState === "function" ? binding.targetState(parsed.data) : binding.targetState;
312
447
  if (targetState !== "") {
313
- const transition = validateTransition(
448
+ const transition = validateStateMachineTransition(
449
+ stateMachineResolver(binding.entityType),
314
450
  binding.entityType,
315
451
  currentState,
316
452
  targetState,
@@ -338,6 +474,7 @@ function createGovernedActionHost(options) {
338
474
  actorType: invocation.actorType,
339
475
  correlationId: invocation.correlationId,
340
476
  ...invocation.causationId ? { causationId: invocation.causationId } : {},
477
+ ...invocation.provenance ? { provenance: invocation.provenance } : {},
341
478
  db,
342
479
  services: options.services
343
480
  },
@@ -692,7 +829,8 @@ function createGovernedActionHost(options) {
692
829
  occurredAt: timestamp,
693
830
  recordedAt: timestamp,
694
831
  correlationId: invocation.correlationId,
695
- ...invocation.causationId ? { causationId: invocation.causationId } : {}
832
+ ...invocation.causationId ? { causationId: invocation.causationId } : {},
833
+ ...invocation.provenance ? { provenance: { source: invocation.provenance.source, ...invocation.provenance.auditAttributes ? { auditAttributes: invocation.provenance.auditAttributes } : {} } } : {}
696
834
  };
697
835
  if (options.outbox) {
698
836
  const hostLifecycleEvent = (/* @__PURE__ */ new Set(["AdapterInvocationStarted", "AdapterInvocationSucceeded", "AdapterInvocationFailed", "ComplianceBlocked"])).has(envelope.eventType);
@@ -702,9 +840,11 @@ function createGovernedActionHost(options) {
702
840
  return;
703
841
  }
704
842
  const traceContext = options.outbox.traceContext?.(envelope);
843
+ const payloadClassification = options.outbox.classifyPayload(envelope);
705
844
  const metadata = {
706
845
  producerModuleVersion: options.outbox.producerModuleVersion(invocation.actionId, invocation.actionVersion),
707
- payloadClassification: options.outbox.classifyPayload(envelope),
846
+ payloadClassification,
847
+ includeProvenance: options.outbox.includeProvenance?.(envelope, payloadClassification) ?? payloadClassification !== "restricted",
708
848
  ...traceContext ? { traceContext } : {}
709
849
  };
710
850
  if (transaction) {
@@ -743,7 +883,8 @@ function actionResult(invocation) {
743
883
  result: invocation.result,
744
884
  ...invocation.error ? { error: invocation.error } : {},
745
885
  ...invocation.hitlRoute ? { hitlRoute: invocation.hitlRoute } : {},
746
- ...invocation.hitlRiskTier ? { hitlRiskTier: invocation.hitlRiskTier } : {}
886
+ ...invocation.hitlRiskTier ? { hitlRiskTier: invocation.hitlRiskTier } : {},
887
+ ...invocation.authorizationReconciliation ? { reconciliation: invocation.authorizationReconciliation } : {}
747
888
  };
748
889
  }
749
890
  function asApprovalStore(store) {
@@ -791,12 +932,11 @@ function declaredPolicies(policyIds) {
791
932
  codeEvaluatorPolicyId: policyId
792
933
  }));
793
934
  }
794
- function initialState(entityType) {
795
- const machine = resolveStateMachine(entityType);
935
+ function initialState(machine) {
796
936
  return Object.values(machine?.states ?? {}).find((state) => state.stateClass === "initial")?.id ?? "none";
797
937
  }
798
938
  function isTerminal(status) {
799
- return ["completed", "failed", "blocked_by_policy", "validation_failed"].includes(status);
939
+ return ["completed", "failed", "blocked_by_policy", "reconciliation_required", "validation_failed"].includes(status);
800
940
  }
801
941
  function withoutPrivateHostFields(data, eventResultFields) {
802
942
  return Object.fromEntries(
@@ -813,6 +953,30 @@ function lifecycleId(prefix, invocationId, key) {
813
953
  const safeKey = key.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 96);
814
954
  return `${prefix}_${invocationId}_${safeKey}`;
815
955
  }
956
+ function validateAuthorizationBinding(input, parameterDigest, authorityMoment, currentTime = /* @__PURE__ */ new Date()) {
957
+ const binding = input.authorizationBinding;
958
+ if (binding.id !== input.authorizationBindingId && input.authorizationBindingId) throw new Error("Authorization binding ID does not match authorizationBindingId.");
959
+ if (binding.tenantId !== input.tenantId || binding.actorId !== input.actorId || binding.actionId !== input.actionId || binding.parameterDigest !== parameterDigest) {
960
+ throw new Error("Authorization binding does not match the submitted command identity.");
961
+ }
962
+ if (authorityMoment && binding.governingMoment !== authorityMoment) throw new Error("Authorization binding governingMoment does not match the action execution contract.");
963
+ const capturedAt = Date.parse(binding.capturedAt);
964
+ const expiresAt = binding.expiresAt === void 0 ? void 0 : Date.parse(binding.expiresAt);
965
+ if (!Number.isFinite(capturedAt) || expiresAt !== void 0 && !Number.isFinite(expiresAt)) {
966
+ throw new Error("Authorization binding timestamps must be valid ISO-8601 values.");
967
+ }
968
+ if (expiresAt !== void 0 && (expiresAt <= capturedAt || authorityMoment === "capture" && expiresAt <= currentTime.getTime())) {
969
+ throw new Error("Authorization binding must be unexpired at capture and expire after capturedAt.");
970
+ }
971
+ }
972
+ function idempotencyConflict(existing, incoming) {
973
+ const reasons = [];
974
+ if ((existing.idempotencyActorId ?? existing.actorId) !== incoming.actorId) reasons.push("actor");
975
+ if ((existing.idempotencyAuthorizationBindingId ?? existing.authorizationBindingId) !== incoming.authorizationBindingId) reasons.push("authority_binding");
976
+ if (existing.actionVersion !== incoming.actionVersion) reasons.push("action_version");
977
+ if (existing.parameterDigest !== incoming.parameterDigest) reasons.push("parameters");
978
+ return reasons.length ? { code: "IDEMPOTENCY_CONFLICT", idempotencyKey: incoming.idempotencyKey, existingInvocationId: existing.id, reasons } : void 0;
979
+ }
816
980
 
817
981
  // src/outbox.ts
818
982
  function toEnterpriseEventEnvelope(event, metadata) {
@@ -833,6 +997,7 @@ function toEnterpriseEventEnvelope(event, metadata) {
833
997
  producerModuleVersion: metadata.producerModuleVersion,
834
998
  payloadClassification: metadata.payloadClassification,
835
999
  ...metadata.traceContext ? { traceContext: { ...metadata.traceContext } } : {},
1000
+ ...metadata.includeProvenance && event.provenance ? { provenance: event.provenance } : {},
836
1001
  payload: event.payload
837
1002
  };
838
1003
  }
@@ -967,7 +1132,7 @@ var MemoryPlatformHostStore = class {
967
1132
  const record = await this.getActionInvocation(id, tenantId, spaceId);
968
1133
  if (!record) throw new Error(`ActionInvocation not found: ${id}`);
969
1134
  Object.assign(record, patch, { updatedAt: /* @__PURE__ */ new Date() });
970
- if (patch.status === "waiting_for_approval" || patch.status === "completed" || patch.status === "failed" || patch.status === "blocked_by_policy" || patch.status === "validation_failed") {
1135
+ if (patch.status === "waiting_for_approval" || patch.status === "completed" || patch.status === "failed" || patch.status === "blocked_by_policy" || patch.status === "reconciliation_required" || patch.status === "validation_failed") {
971
1136
  delete record.leaseOwner;
972
1137
  delete record.leaseExpiresAt;
973
1138
  }
@@ -1179,6 +1344,9 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1179
1344
  actor_id text NOT NULL, actor_type text NOT NULL, status text NOT NULL,
1180
1345
  parameters jsonb NOT NULL, result jsonb NOT NULL DEFAULT '{}'::jsonb,
1181
1346
  correlation_id text NOT NULL, causation_id text, idempotency_key text,
1347
+ parameter_digest text, parameter_digest_algorithm text, idempotency_actor_id text,
1348
+ idempotency_authorization_binding_id text, invocation_provenance jsonb,
1349
+ authorization_binding jsonb, execution_reason text, authorization_reconciliation jsonb,
1182
1350
  authorization_binding_id text, error text,
1183
1351
  attempt_count integer NOT NULL DEFAULT 0, lease_owner text,
1184
1352
  lease_expires_at timestamptz, hitl_route text, hitl_risk_tier text,
@@ -1188,6 +1356,14 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1188
1356
  );
1189
1357
  ALTER TABLE fabric_platform.action_invocations
1190
1358
  ADD COLUMN IF NOT EXISTS idempotency_key text,
1359
+ ADD COLUMN IF NOT EXISTS parameter_digest text,
1360
+ ADD COLUMN IF NOT EXISTS parameter_digest_algorithm text,
1361
+ ADD COLUMN IF NOT EXISTS idempotency_actor_id text,
1362
+ ADD COLUMN IF NOT EXISTS idempotency_authorization_binding_id text,
1363
+ ADD COLUMN IF NOT EXISTS invocation_provenance jsonb,
1364
+ ADD COLUMN IF NOT EXISTS authorization_binding jsonb,
1365
+ ADD COLUMN IF NOT EXISTS execution_reason text,
1366
+ ADD COLUMN IF NOT EXISTS authorization_reconciliation jsonb,
1191
1367
  ADD COLUMN IF NOT EXISTS authorization_binding_id text,
1192
1368
  ADD COLUMN IF NOT EXISTS attempt_count integer NOT NULL DEFAULT 0,
1193
1369
  ADD COLUMN IF NOT EXISTS lease_owner text,
@@ -1204,6 +1380,12 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1204
1380
  ON fabric_platform.action_invocations
1205
1381
  (tenant_id, space_id, action_id, idempotency_key)
1206
1382
  WHERE idempotency_key IS NOT NULL;
1383
+ CREATE INDEX IF NOT EXISTS action_invocations_parameter_digest_idx
1384
+ ON fabric_platform.action_invocations (tenant_id, parameter_digest)
1385
+ WHERE parameter_digest IS NOT NULL;
1386
+ CREATE INDEX IF NOT EXISTS action_invocations_authority_binding_idx
1387
+ ON fabric_platform.action_invocations (tenant_id, authorization_binding_id)
1388
+ WHERE authorization_binding_id IS NOT NULL;
1207
1389
  CREATE INDEX IF NOT EXISTS action_invocations_worker_idx
1208
1390
  ON fabric_platform.action_invocations (status, lease_expires_at, created_at);
1209
1391
  CREATE TABLE IF NOT EXISTS fabric_platform.policy_evaluations (
@@ -1247,9 +1429,10 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1247
1429
  actor_id text NOT NULL, actor_type text NOT NULL, action_invocation_id text,
1248
1430
  payload jsonb NOT NULL, sequence bigint NOT NULL,
1249
1431
  occurred_at timestamptz NOT NULL, recorded_at timestamptz NOT NULL,
1250
- correlation_id text NOT NULL, causation_id text,
1432
+ correlation_id text NOT NULL, causation_id text, provenance jsonb,
1251
1433
  UNIQUE (tenant_id, space_id, sequence)
1252
1434
  );
1435
+ ALTER TABLE fabric_platform.asset_events ADD COLUMN IF NOT EXISTS provenance jsonb;
1253
1436
  CREATE INDEX IF NOT EXISTS asset_events_subject_idx
1254
1437
  ON fabric_platform.asset_events
1255
1438
  (tenant_id, space_id, subject_type, subject_id, sequence);
@@ -1273,8 +1456,10 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1273
1456
  `INSERT INTO fabric_platform.action_invocations
1274
1457
  (id,tenant_id,space_id,action_id,action_version,actor_id,actor_type,status,
1275
1458
  parameters,result,correlation_id,causation_id,idempotency_key,
1459
+ parameter_digest,parameter_digest_algorithm,idempotency_actor_id,
1460
+ idempotency_authorization_binding_id,invocation_provenance,authorization_binding,execution_reason,
1276
1461
  authorization_binding_id,error,runtime_evidence,created_at,updated_at)
1277
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$10::jsonb,$11,$12,$13,$14,$15,$16::jsonb,$17,$17)
1462
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$10::jsonb,$11,$12,$13,$14,$15,$16,$17,$18::jsonb,$19::jsonb,$20,$21,$22,$23::jsonb,$24,$24)
1278
1463
  ON CONFLICT (tenant_id,space_id,action_id,idempotency_key)
1279
1464
  WHERE idempotency_key IS NOT NULL DO UPDATE SET id=fabric_platform.action_invocations.id
1280
1465
  RETURNING *`,
@@ -1292,6 +1477,13 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1292
1477
  input.correlationId,
1293
1478
  input.causationId ?? null,
1294
1479
  input.idempotencyKey ?? null,
1480
+ input.parameterDigest ?? null,
1481
+ input.parameterDigestAlgorithm ?? null,
1482
+ input.idempotencyActorId ?? null,
1483
+ input.idempotencyAuthorizationBindingId ?? null,
1484
+ input.provenance ? JSON.stringify(input.provenance) : null,
1485
+ input.authorizationBinding ? JSON.stringify(input.authorizationBinding) : null,
1486
+ input.executionReason ?? null,
1295
1487
  input.authorizationBindingId ?? null,
1296
1488
  input.error ?? null,
1297
1489
  input.runtimeEvidence ? JSON.stringify(input.runtimeEvidence) : null,
@@ -1313,9 +1505,10 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1313
1505
  `UPDATE fabric_platform.action_invocations SET
1314
1506
  status=COALESCE($4,status), result=COALESCE($5::jsonb,result),
1315
1507
  error=CASE WHEN $6::boolean THEN $7 ELSE error END,
1316
- lease_owner=CASE WHEN $4 IN ('waiting_for_approval','completed','failed','blocked_by_policy','validation_failed')
1508
+ authorization_reconciliation=CASE WHEN $8::boolean THEN $9::jsonb ELSE authorization_reconciliation END,
1509
+ lease_owner=CASE WHEN $4 IN ('waiting_for_approval','completed','failed','blocked_by_policy','reconciliation_required','validation_failed')
1317
1510
  THEN NULL ELSE lease_owner END,
1318
- lease_expires_at=CASE WHEN $4 IN ('waiting_for_approval','completed','failed','blocked_by_policy','validation_failed')
1511
+ lease_expires_at=CASE WHEN $4 IN ('waiting_for_approval','completed','failed','blocked_by_policy','reconciliation_required','validation_failed')
1319
1512
  THEN NULL ELSE lease_expires_at END,
1320
1513
  updated_at=now()
1321
1514
  WHERE id=$1 AND tenant_id=$2 AND space_id=$3`,
@@ -1326,7 +1519,9 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1326
1519
  patch.status ?? null,
1327
1520
  patch.result === void 0 ? null : JSON.stringify(patch.result),
1328
1521
  Object.hasOwn(patch, "error"),
1329
- patch.error ?? null
1522
+ patch.error ?? null,
1523
+ Object.hasOwn(patch, "authorizationReconciliation"),
1524
+ patch.authorizationReconciliation ? JSON.stringify(patch.authorizationReconciliation) : null
1330
1525
  ]
1331
1526
  );
1332
1527
  }
@@ -1521,8 +1716,8 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1521
1716
  `INSERT INTO fabric_platform.asset_events
1522
1717
  (id,tenant_id,space_id,event_type,event_schema_version,subject_type,subject_id,
1523
1718
  actor_id,actor_type,action_invocation_id,payload,sequence,occurred_at,recorded_at,
1524
- correlation_id,causation_id)
1525
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12,$13,$14,$15,$16)
1719
+ correlation_id,causation_id,provenance)
1720
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12,$13,$14,$15,$16,$17::jsonb)
1526
1721
  ON CONFLICT (id) DO NOTHING`,
1527
1722
  [
1528
1723
  event.id,
@@ -1540,7 +1735,8 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1540
1735
  event.occurredAt,
1541
1736
  event.recordedAt,
1542
1737
  event.correlationId,
1543
- event.causationId ?? null
1738
+ event.causationId ?? null,
1739
+ event.provenance ? JSON.stringify(event.provenance) : null
1544
1740
  ]
1545
1741
  );
1546
1742
  }
@@ -1551,13 +1747,13 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1551
1747
  INSERT INTO fabric_platform.asset_events
1552
1748
  (id,tenant_id,space_id,event_type,event_schema_version,subject_type,subject_id,
1553
1749
  actor_id,actor_type,action_invocation_id,payload,sequence,occurred_at,recorded_at,
1554
- correlation_id,causation_id)
1555
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12,$13,$14,$15,$16)
1750
+ correlation_id,causation_id,provenance)
1751
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11::jsonb,$12,$13,$14,$15,$16,$17::jsonb)
1556
1752
  ON CONFLICT (id) DO NOTHING RETURNING id
1557
1753
  )
1558
1754
  INSERT INTO fabric_platform.event_outbox
1559
1755
  (id,tenant_id,space_id,event,status,attempt_count,available_at,created_at)
1560
- SELECT $1,$2,$3,$17::jsonb,'pending',0,$14,$14 FROM inserted_event
1756
+ SELECT $1,$2,$3,$18::jsonb,'pending',0,$14,$14 FROM inserted_event
1561
1757
  ON CONFLICT (id) DO NOTHING`,
1562
1758
  [
1563
1759
  event.id,
@@ -1576,6 +1772,7 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
1576
1772
  event.recordedAt,
1577
1773
  event.correlationId,
1578
1774
  event.causationId ?? null,
1775
+ event.provenance ? JSON.stringify(event.provenance) : null,
1579
1776
  JSON.stringify(envelope)
1580
1777
  ]
1581
1778
  );
@@ -1736,6 +1933,14 @@ function toActionRecord(row) {
1736
1933
  correlationId: String(row.correlation_id),
1737
1934
  ...row.causation_id ? { causationId: String(row.causation_id) } : {},
1738
1935
  ...row.idempotency_key ? { idempotencyKey: String(row.idempotency_key) } : {},
1936
+ ...row.parameter_digest ? { parameterDigest: String(row.parameter_digest) } : {},
1937
+ ...row.parameter_digest_algorithm ? { parameterDigestAlgorithm: String(row.parameter_digest_algorithm) } : {},
1938
+ ...row.idempotency_actor_id ? { idempotencyActorId: String(row.idempotency_actor_id) } : {},
1939
+ ...row.idempotency_authorization_binding_id ? { idempotencyAuthorizationBindingId: String(row.idempotency_authorization_binding_id) } : {},
1940
+ ...row.invocation_provenance ? { provenance: row.invocation_provenance } : {},
1941
+ ...row.authorization_binding ? { authorizationBinding: row.authorization_binding } : {},
1942
+ ...row.execution_reason ? { executionReason: String(row.execution_reason) } : {},
1943
+ ...row.authorization_reconciliation ? { authorizationReconciliation: row.authorization_reconciliation } : {},
1739
1944
  ...row.authorization_binding_id ? { authorizationBindingId: String(row.authorization_binding_id) } : {},
1740
1945
  attemptCount: Number(row.attempt_count ?? 0),
1741
1946
  ...row.lease_owner ? { leaseOwner: String(row.lease_owner) } : {},
@@ -1809,7 +2014,8 @@ function toEventRecord(row) {
1809
2014
  occurredAt: new Date(row.occurred_at),
1810
2015
  recordedAt: new Date(row.recorded_at),
1811
2016
  correlationId: String(row.correlation_id),
1812
- ...row.causation_id ? { causationId: String(row.causation_id) } : {}
2017
+ ...row.causation_id ? { causationId: String(row.causation_id) } : {},
2018
+ ...row.provenance ? { provenance: row.provenance } : {}
1813
2019
  };
1814
2020
  }
1815
2021
  function toOutboxRecord(row) {
@@ -1830,6 +2036,7 @@ function toOutboxRecord(row) {
1830
2036
  ...event.actionInvocationId ? { actionInvocationId: String(event.actionInvocationId) } : {},
1831
2037
  correlationId: String(event.correlationId),
1832
2038
  ...event.causationId ? { causationId: String(event.causationId) } : {},
2039
+ ...event.provenance ? { provenance: event.provenance } : {},
1833
2040
  occurredAt: new Date(event.occurredAt),
1834
2041
  recordedAt: new Date(event.recordedAt),
1835
2042
  producerModuleVersion: String(event.producerModuleVersion),
@@ -1916,6 +2123,6 @@ async function abortableDelay(milliseconds, signal) {
1916
2123
  });
1917
2124
  }
1918
2125
 
1919
- export { MemoryPlatformHostStore, PLATFORM_HOST_CONTRACT_VERSION, PostgresPlatformHostStore, cloneOutboxRecord, createGovernedActionHost, createStoreBackedActionDispatcher, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, toEnterpriseEventEnvelope };
2126
+ export { IdempotencyConflictError, MemoryPlatformHostStore, PARAMETER_DIGEST_ALGORITHM, PLATFORM_HOST_CONTRACT_VERSION, PostgresPlatformHostStore, canonicalJson, cloneOutboxRecord, createGovernedActionHost, createStoreBackedActionDispatcher, digestParameters, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, toEnterpriseEventEnvelope };
1920
2127
  //# sourceMappingURL=index.js.map
1921
2128
  //# sourceMappingURL=index.js.map