@fabricorg/platform-host 7.2.3 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,9 @@
1
- import { AdapterRegistry, createFabricId, assertGovernanceRuntimeEvidence, FABRIC_GOVERNANCE_CONTRACT_VERSION, assertMutationGovernanceContext, evaluatePolicyDefinitions, aggregatePolicyOutcomes, evaluateStateMachineTransition, executeWithAdapterRetry, assertAdapterOutcomeConsistency } from '@fabricorg/platform';
1
+ import { canonicalJson, AdapterRegistry, assertAdapterCircuitBreakerConfig, AdapterCircuitBreakerRegistry, createFabricId, assertGovernanceRuntimeEvidence, FABRIC_GOVERNANCE_CONTRACT_VERSION, assertMutationGovernanceContext, evaluatePolicyDefinitions, aggregatePolicyOutcomes, evaluateStateMachineTransition, executeWithAdapterRetry, isAdapterCircuitOpenError, assertAdapterOutcomeConsistency } from '@fabricorg/platform';
2
+ export { canonicalJson } from '@fabricorg/platform';
3
+ import { performance } from 'perf_hooks';
2
4
  import { assertAssemblyRuntimeCompatible } from '@fabricorg/assembly';
3
5
  import { createHash } from 'crypto';
6
+ import { types } from 'util';
4
7
 
5
8
  // src/host.ts
6
9
 
@@ -15,6 +18,19 @@ var IdempotencyConflictError = class extends Error {
15
18
  conflict;
16
19
  code = "IDEMPOTENCY_CONFLICT";
17
20
  };
21
+ var ActionAdmissionError = class extends Error {
22
+ constructor(actionId, refusal, refusalCode) {
23
+ super(`Action ${actionId} submission was not admitted (${refusal}${refusalCode ? `: ${refusalCode}` : ""}); nothing was recorded.`);
24
+ this.actionId = actionId;
25
+ this.refusal = refusal;
26
+ this.refusalCode = refusalCode;
27
+ this.name = "ActionAdmissionError";
28
+ }
29
+ actionId;
30
+ refusal;
31
+ refusalCode;
32
+ code = "ACTION_ADMISSION_REFUSED";
33
+ };
18
34
  var ExternalCompletionError = class extends Error {
19
35
  constructor(refusal, message) {
20
36
  super(message);
@@ -28,16 +44,6 @@ var ExternalCompletionError = class extends Error {
28
44
  }
29
45
  };
30
46
  var PARAMETER_DIGEST_ALGORITHM = "fabric-canonical-json-sha256-v1";
31
- function canonicalJson(value) {
32
- return JSON.stringify(sort(value));
33
- }
34
- function sort(value) {
35
- if (Array.isArray(value)) return value.map(sort);
36
- if (value && typeof value === "object") {
37
- return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, child]) => [key, sort(child)]));
38
- }
39
- return value;
40
- }
41
47
  function digestParameters(parameters) {
42
48
  return createHash("sha256").update(canonicalJson(parameters)).digest("hex");
43
49
  }
@@ -82,6 +88,7 @@ var PLATFORM_HOST_METRIC_NAMES = {
82
88
  workerCyclesCompleted: "fabric.platform.worker.cycles.completed",
83
89
  workerCyclesFailed: "fabric.platform.worker.cycles.failed",
84
90
  invocationSubmitted: "fabric.platform.invocation.submitted",
91
+ invocationAdmissionRefused: "fabric.platform.invocation.admission_refused",
85
92
  invocationExecutionStarted: "fabric.platform.invocation.execution_started",
86
93
  invocationLeaseClaimed: "fabric.platform.invocation.lease_claimed",
87
94
  invocationCompleted: "fabric.platform.invocation.completed",
@@ -97,6 +104,8 @@ var PLATFORM_HOST_METRIC_NAMES = {
97
104
  outboxPublished: "fabric.platform.outbox.published",
98
105
  outboxFailed: "fabric.platform.outbox.failed",
99
106
  outboxDeadLettered: "fabric.platform.outbox.dead_lettered",
107
+ outboxRequeued: "fabric.platform.outbox.requeued",
108
+ outboxRequeueRefused: "fabric.platform.outbox.requeue_refused",
100
109
  invocationBacklog: "fabric.platform.invocation.backlog",
101
110
  invocationRunning: "fabric.platform.invocation.running",
102
111
  invocationExpiredLeases: "fabric.platform.invocation.expired_leases",
@@ -496,6 +505,340 @@ function createExternalCompletionLifecycle(deps) {
496
505
  }
497
506
  return { completeExternalInvocation, reconcileOverdueCompletions, recordCompletionReconciliation };
498
507
  }
508
+ var SubmissionUnreadableError = class extends Error {
509
+ name = "SubmissionUnreadableError";
510
+ constructor() {
511
+ super("Platform Host submission could not be read as plain data; nothing was recorded.");
512
+ }
513
+ };
514
+ var NotJsonData = class extends Error {
515
+ };
516
+ function copyJsonData(value) {
517
+ try {
518
+ return { ok: true, value: copy(value, /* @__PURE__ */ new Set()) };
519
+ } catch {
520
+ return { ok: false };
521
+ }
522
+ }
523
+ function copy(value, ancestors) {
524
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
525
+ if (typeof value === "number") {
526
+ if (!Number.isFinite(value)) throw new NotJsonData();
527
+ return value === 0 ? 0 : value;
528
+ }
529
+ if (typeof value !== "object") throw new NotJsonData();
530
+ if (types.isProxy(value)) throw new NotJsonData();
531
+ if (ancestors.has(value)) throw new NotJsonData();
532
+ const prototype = Reflect.getPrototypeOf(value);
533
+ ancestors.add(value);
534
+ try {
535
+ if (Array.isArray(value)) {
536
+ if (prototype !== Array.prototype) throw new NotJsonData();
537
+ const length = value.length;
538
+ const out2 = [];
539
+ for (let index = 0; index < length; index += 1) {
540
+ const descriptor = Reflect.getOwnPropertyDescriptor(value, String(index));
541
+ if (descriptor === void 0) throw new NotJsonData();
542
+ const item = readOnce(value, descriptor);
543
+ if (item === void 0) throw new NotJsonData();
544
+ out2.push(copy(item, ancestors));
545
+ }
546
+ return out2;
547
+ }
548
+ if (prototype !== Object.prototype && prototype !== null) throw new NotJsonData();
549
+ const out = {};
550
+ for (const key of Reflect.ownKeys(value)) {
551
+ if (typeof key !== "string") continue;
552
+ const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
553
+ if (descriptor === void 0 || descriptor.enumerable !== true) continue;
554
+ const item = readOnce(value, descriptor);
555
+ if (item === void 0) continue;
556
+ Reflect.defineProperty(out, key, { value: copy(item, ancestors), enumerable: true, writable: true, configurable: true });
557
+ }
558
+ return out;
559
+ } finally {
560
+ ancestors.delete(value);
561
+ }
562
+ }
563
+ function readOnce(target, descriptor) {
564
+ if ("value" in descriptor) return descriptor.value;
565
+ return descriptor.get ? Reflect.apply(descriptor.get, target, []) : void 0;
566
+ }
567
+ function snapshotSubmission(input) {
568
+ if (input === null || typeof input !== "object") throw new SubmissionUnreadableError();
569
+ const scalar = (key) => primitiveOrAbsent(readField(input, key));
570
+ const actionId = scalar("actionId");
571
+ const tenantId = scalar("tenantId");
572
+ const spaceId = scalar("spaceId");
573
+ const actorId = scalar("actorId");
574
+ const actorType = scalar("actorType");
575
+ const correlationId = scalar("correlationId");
576
+ const causationId = scalar("causationId");
577
+ const executionReason = scalar("executionReason");
578
+ const idempotencyKey = scalar("idempotencyKey");
579
+ const authorizationBindingId = scalar("authorizationBindingId");
580
+ const scalars = {
581
+ actionId,
582
+ tenantId,
583
+ spaceId,
584
+ actorId,
585
+ actorType,
586
+ ...correlationId != null ? { correlationId } : {},
587
+ ...causationId != null ? { causationId } : {},
588
+ ...executionReason != null ? { executionReason } : {},
589
+ ...idempotencyKey != null ? { idempotencyKey } : {},
590
+ ...authorizationBindingId != null ? { authorizationBindingId } : {}
591
+ };
592
+ const live = { ...scalars, parametersArePrivate: false };
593
+ Object.defineProperties(live, {
594
+ parameters: { get: () => input.parameters, enumerable: true },
595
+ provenance: { get: () => input.provenance, enumerable: true },
596
+ authorizationBinding: { get: () => input.authorizationBinding, enumerable: true }
597
+ });
598
+ return Object.freeze(live);
599
+ }
600
+ var PROVENANCE_SCALARS = ["source", "correlationId", "causationId"];
601
+ var PROVENANCE_ATTRIBUTES = ["traceAttributes", "auditAttributes"];
602
+ var BINDING_FIELDS = ["id", "tenantId", "actorId", "actionId", "parameterDigest", "resourceScope", "capturedAt", "expiresAt", "governingMoment"];
603
+ function privateSubmission(snapshot) {
604
+ const parameters = readField(snapshot, "parameters");
605
+ const provenanceRead = readField(snapshot, "provenance");
606
+ const bindingRead = readField(snapshot, "authorizationBinding");
607
+ const copied = copyJsonData(parameters);
608
+ const provenance = provenanceRead ? copyProvenance(provenanceRead) : void 0;
609
+ const authorizationBinding = bindingRead ? copyBinding(bindingRead) : void 0;
610
+ return Object.freeze({
611
+ actionId: snapshot.actionId,
612
+ tenantId: snapshot.tenantId,
613
+ spaceId: snapshot.spaceId,
614
+ actorId: snapshot.actorId,
615
+ actorType: snapshot.actorType,
616
+ ...snapshot.correlationId !== void 0 ? { correlationId: snapshot.correlationId } : {},
617
+ ...snapshot.causationId !== void 0 ? { causationId: snapshot.causationId } : {},
618
+ ...snapshot.executionReason !== void 0 ? { executionReason: snapshot.executionReason } : {},
619
+ ...snapshot.idempotencyKey !== void 0 ? { idempotencyKey: snapshot.idempotencyKey } : {},
620
+ ...snapshot.authorizationBindingId !== void 0 ? { authorizationBindingId: snapshot.authorizationBindingId } : {},
621
+ ...provenance ? { provenance } : {},
622
+ ...authorizationBinding ? { authorizationBinding } : {},
623
+ parameters: copied.ok ? copied.value : parameters,
624
+ parametersArePrivate: copied.ok
625
+ });
626
+ }
627
+ function readField(target, key) {
628
+ try {
629
+ return target[key];
630
+ } catch {
631
+ throw new SubmissionUnreadableError();
632
+ }
633
+ }
634
+ function primitiveOrAbsent(value) {
635
+ if (value !== null && (typeof value === "object" || typeof value === "function" || typeof value === "symbol")) {
636
+ throw new SubmissionUnreadableError();
637
+ }
638
+ return value;
639
+ }
640
+ function objectOf(value) {
641
+ if (value === null || typeof value !== "object") throw new SubmissionUnreadableError();
642
+ return value;
643
+ }
644
+ function copyFields(source, fields) {
645
+ const out = {};
646
+ for (const field of fields) {
647
+ const value = primitiveOrAbsent(readField(source, field));
648
+ if (value !== void 0) out[field] = value;
649
+ }
650
+ return out;
651
+ }
652
+ function copyAttributes(source, field) {
653
+ const value = readField(source, field);
654
+ if (value === void 0 || value === null) return void 0;
655
+ const map = objectOf(value);
656
+ let entries;
657
+ try {
658
+ entries = Object.entries(map);
659
+ } catch {
660
+ throw new SubmissionUnreadableError();
661
+ }
662
+ const out = {};
663
+ for (const [key, item] of entries) {
664
+ Reflect.defineProperty(out, key, { value: primitiveOrAbsent(item), enumerable: true, writable: true, configurable: true });
665
+ }
666
+ return out;
667
+ }
668
+ function copyProvenance(value) {
669
+ const source = objectOf(value);
670
+ const out = copyFields(source, PROVENANCE_SCALARS);
671
+ for (const field of PROVENANCE_ATTRIBUTES) {
672
+ const attributes = copyAttributes(source, field);
673
+ if (attributes !== void 0) out[field] = attributes;
674
+ }
675
+ return out;
676
+ }
677
+ function copyBinding(value) {
678
+ return copyFields(objectOf(value), BINDING_FIELDS);
679
+ }
680
+ function parametersCopy(snapshot) {
681
+ if (!snapshot.parametersArePrivate) throw new SubmissionUnreadableError();
682
+ const copied = copyJsonData(snapshot.parameters);
683
+ if (!copied.ok) throw new SubmissionUnreadableError();
684
+ return copied.value;
685
+ }
686
+ function liveSubmissionView(snapshot, input) {
687
+ const view = {
688
+ actionId: snapshot.actionId,
689
+ tenantId: snapshot.tenantId,
690
+ spaceId: snapshot.spaceId,
691
+ actorId: snapshot.actorId,
692
+ actorType: snapshot.actorType,
693
+ ...snapshot.correlationId !== void 0 ? { correlationId: snapshot.correlationId } : {},
694
+ ...snapshot.causationId !== void 0 ? { causationId: snapshot.causationId } : {},
695
+ ...snapshot.executionReason !== void 0 ? { executionReason: snapshot.executionReason } : {},
696
+ ...snapshot.idempotencyKey !== void 0 ? { idempotencyKey: snapshot.idempotencyKey } : {},
697
+ ...snapshot.authorizationBindingId !== void 0 ? { authorizationBindingId: snapshot.authorizationBindingId } : {}
698
+ };
699
+ const caller = input;
700
+ for (const field of ["parameters", "provenance", "authorizationBinding"]) {
701
+ Object.defineProperty(view, field, {
702
+ get: () => caller[field],
703
+ set: (value) => {
704
+ caller[field] = value;
705
+ },
706
+ enumerable: true,
707
+ configurable: true
708
+ });
709
+ }
710
+ return view;
711
+ }
712
+ function submissionView(snapshot) {
713
+ const fresh = (value) => {
714
+ if (value === void 0) return void 0;
715
+ const copied = copyJsonData(value);
716
+ if (!copied.ok) throw new SubmissionUnreadableError();
717
+ return copied.value;
718
+ };
719
+ const provenance = fresh(snapshot.provenance);
720
+ const authorizationBinding = fresh(snapshot.authorizationBinding);
721
+ return {
722
+ actionId: snapshot.actionId,
723
+ parameters: parametersCopy(snapshot),
724
+ tenantId: snapshot.tenantId,
725
+ spaceId: snapshot.spaceId,
726
+ actorId: snapshot.actorId,
727
+ actorType: snapshot.actorType,
728
+ ...snapshot.correlationId !== void 0 ? { correlationId: snapshot.correlationId } : {},
729
+ ...snapshot.causationId !== void 0 ? { causationId: snapshot.causationId } : {},
730
+ ...provenance ? { provenance } : {},
731
+ ...snapshot.executionReason !== void 0 ? { executionReason: snapshot.executionReason } : {},
732
+ ...snapshot.idempotencyKey !== void 0 ? { idempotencyKey: snapshot.idempotencyKey } : {},
733
+ ...snapshot.authorizationBindingId !== void 0 ? { authorizationBindingId: snapshot.authorizationBindingId } : {},
734
+ ...authorizationBinding ? { authorizationBinding } : {}
735
+ };
736
+ }
737
+ var MAX_REASON_LENGTH = 1e3;
738
+ function asOutboxRequeueStore(store) {
739
+ const candidate = store;
740
+ return typeof candidate.requeueOutboxDeadLetter === "function" && typeof candidate.getOutboxRecord === "function" && typeof candidate.listOutboxRequeues === "function" ? store : void 0;
741
+ }
742
+ function createOutboxRequeue(deps) {
743
+ return async function requeueOutboxDeadLetter(input) {
744
+ for (const field of ["outboxId", "tenantId", "spaceId", "actorId", "actorType"]) {
745
+ if (typeof input[field] !== "string" || input[field].trim() === "") {
746
+ throw new Error(`Outbox requeue requires ${field}.`);
747
+ }
748
+ }
749
+ if (typeof input.reason !== "string" || input.reason.trim() === "") {
750
+ throw new Error("Outbox requeue requires a reason.");
751
+ }
752
+ if (input.reason.length > MAX_REASON_LENGTH) {
753
+ throw new Error(`Outbox requeue reason must be at most ${MAX_REASON_LENGTH} characters.`);
754
+ }
755
+ if (input.availableAt !== void 0 && !(input.availableAt instanceof Date && Number.isFinite(input.availableAt.getTime()))) {
756
+ throw new Error("Outbox requeue availableAt must be a valid Date.");
757
+ }
758
+ const store = asOutboxRequeueStore(deps.store);
759
+ if (!store) throw new Error("Outbox requeue requires a store with outbox requeue support.");
760
+ const refuse = (refusal, message, currentStatus) => {
761
+ emitPlatformHostTelemetry(deps.telemetry, {
762
+ kind: "event",
763
+ name: "outbox.requeue_refused",
764
+ metricName: PLATFORM_HOST_METRIC_NAMES.outboxRequeueRefused,
765
+ occurredAt: deps.now(),
766
+ tenantId: input.tenantId,
767
+ spaceId: input.spaceId,
768
+ attributes: { refusal }
769
+ });
770
+ return { status: "refused", refusal, message, ...currentStatus ? { currentStatus } : {} };
771
+ };
772
+ const record = await store.getOutboxRecord(input.outboxId, input.tenantId, input.spaceId);
773
+ const authorization = deps.authorization;
774
+ const hasAuthorizer = typeof authorization.authorizeOutboxRequeue === "function";
775
+ const authorized = hasAuthorizer ? await authorization.authorizeOutboxRequeue({
776
+ tenantId: input.tenantId,
777
+ spaceId: input.spaceId,
778
+ actorId: input.actorId,
779
+ actorType: input.actorType,
780
+ outboxId: input.outboxId,
781
+ reason: input.reason,
782
+ ...record ? {
783
+ record: {
784
+ eventId: record.event.eventId,
785
+ eventType: record.event.eventType,
786
+ payloadClassification: record.event.payloadClassification,
787
+ status: record.status,
788
+ attemptCount: record.attemptCount
789
+ }
790
+ } : {}
791
+ }) : false;
792
+ if (authorized !== true) {
793
+ return refuse(
794
+ "not_authorized",
795
+ hasAuthorizer ? `Actor ${input.actorId} is not authorized to requeue outbox records in this scope.` : "Outbox requeue is refused because no authorizeOutboxRequeue is configured."
796
+ );
797
+ }
798
+ if (!record) return refuse("not_found", `Outbox record not found: ${input.outboxId}`);
799
+ if (record.status !== "dead_letter") {
800
+ return refuse("not_dead_letter", `Outbox record ${input.outboxId} is ${record.status}, not dead_letter.`, record.status);
801
+ }
802
+ const requeuedAt = deps.now();
803
+ const result = await store.requeueOutboxDeadLetter({
804
+ id: input.outboxId,
805
+ tenantId: input.tenantId,
806
+ spaceId: input.spaceId,
807
+ requeueId: createFabricId("orq"),
808
+ actorId: input.actorId,
809
+ actorType: input.actorType,
810
+ reason: input.reason,
811
+ requeuedAt,
812
+ availableAt: input.availableAt ?? requeuedAt,
813
+ expectedLeaseToken: record.leaseToken ?? 0
814
+ });
815
+ if (!result.applied) {
816
+ if (result.refusal === "not_found") return refuse("not_found", `Outbox record not found: ${input.outboxId}`);
817
+ if (result.refusal === "stale") {
818
+ return refuse(
819
+ "stale",
820
+ `Outbox record ${input.outboxId} changed after it was authorized; read and authorize it again.`,
821
+ result.currentStatus
822
+ );
823
+ }
824
+ return refuse(
825
+ "not_dead_letter",
826
+ `Outbox record ${input.outboxId} is ${result.currentStatus ?? "no longer dead_letter"}, not dead_letter.`,
827
+ result.currentStatus
828
+ );
829
+ }
830
+ emitPlatformHostTelemetry(deps.telemetry, {
831
+ kind: "event",
832
+ name: "outbox.requeued",
833
+ metricName: PLATFORM_HOST_METRIC_NAMES.outboxRequeued,
834
+ occurredAt: requeuedAt,
835
+ tenantId: input.tenantId,
836
+ spaceId: input.spaceId,
837
+ attributes: { priorAttempt: result.requeue.priorAttemptCount }
838
+ });
839
+ return { status: "requeued", record: result.record, requeue: result.requeue };
840
+ };
841
+ }
499
842
 
500
843
  // src/host.ts
501
844
  var DEFAULT_EXTRACT_EVENTS = (data) => {
@@ -505,6 +848,12 @@ var DEFAULT_EXTRACT_EVENTS = (data) => {
505
848
  var RecoverableFinalizationError = class extends Error {
506
849
  name = "RecoverableFinalizationError";
507
850
  };
851
+ var DEFAULT_ADMISSION_TIMEOUT_MS = 1e4;
852
+ var MAX_TIMER_MS = 2147483647;
853
+ var monotonicNow = performance.now.bind(performance);
854
+ var ADMISSION_FAILED = Object.freeze({ kind: "failed" });
855
+ var ADMISSION_TIMED_OUT = Object.freeze({ kind: "timed_out" });
856
+ var ADMISSION_REFUSAL_CODE = /^[a-z][A-Za-z0-9_.:-]{0,127}$/;
508
857
  function createGovernedActionHost(options) {
509
858
  if (options.composition) {
510
859
  if (!options.registry?.orderedModules) {
@@ -526,45 +875,63 @@ function createGovernedActionHost(options) {
526
875
  }
527
876
  }
528
877
  const adapters = new AdapterRegistry();
529
- for (const adapter of options.adapters ?? []) adapters.register(adapter);
878
+ for (const adapter of options.adapters ?? []) {
879
+ if (adapter.circuitBreaker) assertAdapterCircuitBreakerConfig(adapter.circuitBreaker);
880
+ adapters.register(adapter);
881
+ }
530
882
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
883
+ const circuitBreakers = options.adapterCircuitBreakers ?? new AdapterCircuitBreakerRegistry({ now: () => now().getTime() });
531
884
  const extractEvents = options.extractEvents ?? DEFAULT_EXTRACT_EVENTS;
532
885
  const configuredActionResolver = options.resolveAction ?? options.registry?.resolveAction;
533
886
  if (!configuredActionResolver) throw new Error("Platform Host requires an explicit module registry or resolveAction function.");
534
887
  const actionResolver = configuredActionResolver;
535
888
  const stateMachineResolver = options.registry?.resolveStateMachine ?? (() => void 0);
536
889
  const eventResultFields = options.eventResultFields ?? ["_events"];
890
+ const admissionTimeoutMs = options.admissionTimeoutMs ?? DEFAULT_ADMISSION_TIMEOUT_MS;
891
+ if (typeof admissionTimeoutMs !== "number" || !Number.isInteger(admissionTimeoutMs) || admissionTimeoutMs < 1 || admissionTimeoutMs > MAX_TIMER_MS) {
892
+ throw new Error(`admissionTimeoutMs must be a whole number of milliseconds from 1 to ${MAX_TIMER_MS}.`);
893
+ }
537
894
  async function submitAction(input) {
538
- if (input.executionReason && !["initial", "offline_replay"].includes(input.executionReason)) {
539
- throw new Error(`Unsupported submitted execution reason: ${input.executionReason}`);
895
+ const read = snapshotSubmission(input);
896
+ if (read.executionReason && !["initial", "offline_replay"].includes(read.executionReason)) {
897
+ throw new Error(`Unsupported submitted execution reason: ${read.executionReason}`);
540
898
  }
541
- const action = actionResolver(input.actionId);
542
- if (!action) throw new Error(`Unknown action: ${input.actionId}`);
543
- if (action.execution?.connectivity === "online-required" && input.executionReason === "offline_replay") {
544
- throw new Error(`Action ${input.actionId} is online-required and cannot be submitted as an offline replay.`);
899
+ const action = actionResolver(read.actionId);
900
+ if (!action) throw new Error(`Unknown action: ${read.actionId}`);
901
+ const declaresAdmission = action.admitSubmission !== void 0;
902
+ const submission = declaresAdmission ? privateSubmission(read) : read;
903
+ if (action.execution?.connectivity === "online-required" && submission.executionReason === "offline_replay") {
904
+ throw new Error(`Action ${submission.actionId} is online-required and cannot be submitted as an offline replay.`);
545
905
  }
546
- if (action.execution?.sensitivity === "restricted" && Object.keys(input.provenance?.auditAttributes ?? {}).length > 0) {
547
- throw new Error(`Action ${input.actionId} declares restricted sensitivity; provenance audit attributes must not be durably recorded.`);
906
+ if (action.execution?.sensitivity === "restricted" && Object.keys(submission.provenance?.auditAttributes ?? {}).length > 0) {
907
+ throw new Error(`Action ${submission.actionId} declares restricted sensitivity; provenance audit attributes must not be durably recorded.`);
548
908
  }
549
- const authorizationInput = toAuthorizationInput(action, input);
909
+ const authorizationInput = toAuthorizationInput(action, submission);
550
910
  if (!await options.authorization.checkEntitlement(authorizationInput)) {
551
- throw new Error(`Module "${action.namespace}" is not enabled for tenant ${input.tenantId}`);
911
+ throw new Error(`Module "${action.namespace}" is not enabled for tenant ${submission.tenantId}`);
552
912
  }
553
913
  if (!await options.authorization.authorize(authorizationInput)) {
554
- throw new Error(`Actor ${input.actorId} is not authorized for action ${input.actionId}`);
914
+ throw new Error(`Actor ${submission.actorId} is not authorized for action ${submission.actionId}`);
915
+ }
916
+ if (declaresAdmission && (!submission.parametersArePrivate || !isRecord(submission.parameters))) {
917
+ refuseAdmission(action, submission, "invalid_parameters");
555
918
  }
556
919
  const actionInvocationId = createFabricId("act");
557
- if (input.correlationId && input.provenance?.correlationId && input.correlationId !== input.provenance.correlationId) throw new Error("Invocation provenance correlationId does not match submission correlationId.");
558
- if (input.causationId && input.provenance?.causationId && input.causationId !== input.provenance.causationId) throw new Error("Invocation provenance causationId does not match submission causationId.");
559
- const correlationId = input.correlationId ?? input.provenance?.correlationId ?? createFabricId("corr");
560
- const durableParameters = options.redactActionParameters ? options.redactActionParameters(input.actionId, input.parameters) : input.parameters;
561
- const parameterDigest = digestParameters(input.parameters);
562
- if (input.authorizationBinding) validateAuthorizationBinding(input, parameterDigest, action.execution?.authorityMoment, now());
920
+ if (submission.correlationId && submission.provenance?.correlationId && submission.correlationId !== submission.provenance.correlationId) throw new Error("Invocation provenance correlationId does not match submission correlationId.");
921
+ if (submission.causationId && submission.provenance?.causationId && submission.causationId !== submission.provenance.causationId) throw new Error("Invocation provenance causationId does not match submission causationId.");
922
+ const correlationId = submission.correlationId ?? submission.provenance?.correlationId ?? createFabricId("corr");
923
+ const prepare = () => {
924
+ const durableParameters2 = options.redactActionParameters ? options.redactActionParameters(submission.actionId, submission.parameters) : submission.parameters;
925
+ const parameterDigest2 = digestParameters(submission.parameters);
926
+ if (submission.authorizationBinding) validateAuthorizationBinding(submission, parameterDigest2, action.execution?.authorityMoment, now());
927
+ return { durableParameters: durableParameters2, parameterDigest: parameterDigest2 };
928
+ };
929
+ const early = declaresAdmission ? void 0 : prepare();
563
930
  const durableProvenance = normalizeProvenance(
564
- input.provenance,
931
+ submission.provenance,
565
932
  options.provenance
566
933
  );
567
- const initiatingReleaseDigest = await options.composition?.resolveInitiatingReleaseDigest?.(input);
934
+ const initiatingReleaseDigest = await options.composition?.resolveInitiatingReleaseDigest?.(declaresAdmission ? submissionView(submission) : liveSubmissionView(submission, input));
568
935
  const runtimeEvidence = {
569
936
  governanceContractVersion: FABRIC_GOVERNANCE_CONTRACT_VERSION,
570
937
  hostContractVersion: PLATFORM_HOST_CONTRACT_VERSION,
@@ -573,48 +940,51 @@ function createGovernedActionHost(options) {
573
940
  ...initiatingReleaseDigest ? { initiatingReleaseDigest } : {}
574
941
  };
575
942
  assertGovernanceRuntimeEvidence(runtimeEvidence);
943
+ const admission = declaresAdmission ? await admitSubmission(action, submission) : void 0;
944
+ const { durableParameters, parameterDigest } = early ?? prepare();
576
945
  const durableInvocation = await options.store.createActionInvocation({
577
946
  id: actionInvocationId,
578
- tenantId: input.tenantId,
579
- spaceId: input.spaceId,
580
- actionId: input.actionId,
947
+ tenantId: submission.tenantId,
948
+ spaceId: submission.spaceId,
949
+ actionId: submission.actionId,
581
950
  actionVersion: action.version,
582
- actorId: input.actorId,
583
- actorType: input.actorType,
951
+ actorId: submission.actorId,
952
+ actorType: submission.actorType,
584
953
  status: "pending",
585
954
  parameters: durableParameters,
586
955
  result: {},
587
956
  runtimeEvidence,
588
957
  correlationId,
589
- ...input.causationId ?? input.provenance?.causationId ? { causationId: input.causationId ?? input.provenance?.causationId } : {},
590
- ...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {},
591
- ...input.idempotencyKey ? {
958
+ ...submission.causationId ?? submission.provenance?.causationId ? { causationId: submission.causationId ?? submission.provenance?.causationId } : {},
959
+ ...submission.idempotencyKey ? { idempotencyKey: submission.idempotencyKey } : {},
960
+ ...submission.idempotencyKey ? {
592
961
  parameterDigest,
593
962
  parameterDigestAlgorithm: PARAMETER_DIGEST_ALGORITHM,
594
- idempotencyActorId: input.actorId,
595
- ...input.authorizationBindingId ? { idempotencyAuthorizationBindingId: input.authorizationBindingId } : {}
963
+ idempotencyActorId: submission.actorId,
964
+ ...submission.authorizationBindingId ? { idempotencyAuthorizationBindingId: submission.authorizationBindingId } : {}
596
965
  } : {},
597
966
  ...durableProvenance ? { provenance: durableProvenance } : {},
598
- ...input.authorizationBinding ? { authorizationBinding: input.authorizationBinding } : {},
599
- ...input.executionReason ? { executionReason: input.executionReason } : {},
600
- ...input.authorizationBindingId ? { authorizationBindingId: input.authorizationBindingId } : {}
967
+ ...submission.authorizationBinding ? { authorizationBinding: submission.authorizationBinding } : {},
968
+ ...submission.executionReason ? { executionReason: submission.executionReason } : {},
969
+ ...submission.authorizationBindingId ? { authorizationBindingId: submission.authorizationBindingId } : {},
970
+ ...admission ? { admittedAt: admission.admittedAt } : {}
601
971
  });
602
972
  emitPlatformHostTelemetry(options.telemetry, {
603
973
  kind: "event",
604
974
  name: "invocation.submitted",
605
975
  metricName: PLATFORM_HOST_METRIC_NAMES.invocationSubmitted,
606
976
  occurredAt: now(),
607
- tenantId: input.tenantId,
608
- spaceId: input.spaceId,
977
+ tenantId: submission.tenantId,
978
+ spaceId: submission.spaceId,
609
979
  attributes: { actionVersion: action.version }
610
980
  });
611
- if (durableInvocation.id !== actionInvocationId && input.idempotencyKey) {
981
+ if (durableInvocation.id !== actionInvocationId && submission.idempotencyKey) {
612
982
  const conflict = idempotencyConflict(durableInvocation, {
613
- actorId: input.actorId,
614
- authorizationBindingId: input.authorizationBindingId,
983
+ actorId: submission.actorId,
984
+ authorizationBindingId: submission.authorizationBindingId,
615
985
  actionVersion: action.version,
616
986
  parameterDigest,
617
- idempotencyKey: input.idempotencyKey
987
+ idempotencyKey: submission.idempotencyKey
618
988
  });
619
989
  if (!durableInvocation.parameterDigest) {
620
990
  options.idempotency?.onLegacyRecord?.(durableInvocation);
@@ -634,8 +1004,8 @@ function createGovernedActionHost(options) {
634
1004
  const dispatched = await options.dispatcher.dispatch({
635
1005
  actionInvocationId: durableInvocation.id,
636
1006
  actionId: durableInvocation.actionId,
637
- tenantId: input.tenantId,
638
- spaceId: input.spaceId,
1007
+ tenantId: submission.tenantId,
1008
+ spaceId: submission.spaceId,
639
1009
  workflowId: durableWorkflowId
640
1010
  });
641
1011
  return withConsistency({
@@ -645,7 +1015,7 @@ function createGovernedActionHost(options) {
645
1015
  ...dispatched.runId ? { runId: dispatched.runId } : {},
646
1016
  ...durableInvocation.hitlRoute ? { hitlRoute: durableInvocation.hitlRoute } : {},
647
1017
  ...durableInvocation.hitlRiskTier ? { hitlRiskTier: durableInvocation.hitlRiskTier } : {}
648
- }, input.actionId);
1018
+ }, submission.actionId);
649
1019
  }
650
1020
  return withConsistency({
651
1021
  actionInvocationId: durableInvocation.id,
@@ -658,15 +1028,15 @@ function createGovernedActionHost(options) {
658
1028
  ...durableInvocation.authorizationReconciliation ? { reconciliation: durableInvocation.authorizationReconciliation } : {},
659
1029
  ...durableInvocation.adapterReconciliation ? { adapterReconciliation: durableInvocation.adapterReconciliation } : {},
660
1030
  ...durableInvocation.pendingCompletion ? { pendingCompletion: durableInvocation.pendingCompletion } : {}
661
- }, input.actionId);
1031
+ }, submission.actionId);
662
1032
  }
663
1033
  if (options.dispatcher) {
664
1034
  try {
665
1035
  const dispatched = await options.dispatcher.dispatch({
666
1036
  actionInvocationId: durableInvocation.id,
667
1037
  actionId: durableInvocation.actionId,
668
- tenantId: input.tenantId,
669
- spaceId: input.spaceId,
1038
+ tenantId: submission.tenantId,
1039
+ spaceId: submission.spaceId,
670
1040
  workflowId: durableWorkflowId
671
1041
  });
672
1042
  return withConsistency({
@@ -676,17 +1046,78 @@ function createGovernedActionHost(options) {
676
1046
  ...dispatched.runId ? { runId: dispatched.runId } : {},
677
1047
  ...durableInvocation.hitlRoute ? { hitlRoute: durableInvocation.hitlRoute } : {},
678
1048
  ...durableInvocation.hitlRiskTier ? { hitlRiskTier: durableInvocation.hitlRiskTier } : {}
679
- }, input.actionId);
1049
+ }, submission.actionId);
680
1050
  } catch (error) {
681
1051
  throw error;
682
1052
  }
683
1053
  }
684
1054
  const executed = await executeInvocation(
685
1055
  durableInvocation.id,
686
- input.tenantId,
687
- input.spaceId
1056
+ submission.tenantId,
1057
+ submission.spaceId
688
1058
  );
689
- return { ...withConsistency(executed, input.actionId), workflowId: durableWorkflowId };
1059
+ return { ...withConsistency(executed, submission.actionId), workflowId: durableWorkflowId };
1060
+ }
1061
+ async function admitSubmission(action, submission) {
1062
+ const refuse = (refusal, refusalCode) => refuseAdmission(action, submission, refusal, refusalCode);
1063
+ const check = action.admitSubmission;
1064
+ if (typeof check !== "function") return refuse("failed");
1065
+ const checked = parametersCopy(submission);
1066
+ const controller = new AbortController();
1067
+ let timer;
1068
+ const deadline = new Promise((resolve) => {
1069
+ timer = setTimeout(() => {
1070
+ controller.abort();
1071
+ resolve(ADMISSION_TIMED_OUT);
1072
+ }, admissionTimeoutMs);
1073
+ });
1074
+ const startedAt = monotonicNow();
1075
+ const settledInTime = () => monotonicNow() - startedAt < admissionTimeoutMs;
1076
+ let answered;
1077
+ try {
1078
+ answered = Promise.resolve(check(Object.freeze({
1079
+ actionId: action.actionId,
1080
+ tenantId: submission.tenantId,
1081
+ spaceId: submission.spaceId,
1082
+ actorId: submission.actorId,
1083
+ actorType: submission.actorType,
1084
+ parameters: checked,
1085
+ signal: controller.signal
1086
+ }))).then(
1087
+ (value) => settledInTime() ? { kind: "answered", value } : ADMISSION_TIMED_OUT,
1088
+ () => settledInTime() ? ADMISSION_FAILED : ADMISSION_TIMED_OUT
1089
+ );
1090
+ } catch {
1091
+ answered = Promise.resolve(settledInTime() ? ADMISSION_FAILED : ADMISSION_TIMED_OUT);
1092
+ }
1093
+ let outcome;
1094
+ try {
1095
+ outcome = await Promise.race([answered, deadline]);
1096
+ } finally {
1097
+ clearTimeout(timer);
1098
+ }
1099
+ if (outcome.kind === "timed_out") {
1100
+ controller.abort();
1101
+ return refuse("timed_out");
1102
+ }
1103
+ if (outcome.kind === "failed") return refuse("failed");
1104
+ const admitted = ownDataProperty(outcome.value, "admitted");
1105
+ if (admitted === true) return { admittedAt: now() };
1106
+ const code = ownDataProperty(outcome.value, "code");
1107
+ if (admitted === false && typeof code === "string" && ADMISSION_REFUSAL_CODE.test(code)) return refuse("refused", code);
1108
+ return refuse("invalid_decision");
1109
+ }
1110
+ function refuseAdmission(action, submission, refusal, refusalCode) {
1111
+ emitPlatformHostTelemetry(options.telemetry, {
1112
+ kind: "event",
1113
+ name: "invocation.admission_refused",
1114
+ metricName: PLATFORM_HOST_METRIC_NAMES.invocationAdmissionRefused,
1115
+ occurredAt: now(),
1116
+ tenantId: submission.tenantId,
1117
+ spaceId: submission.spaceId,
1118
+ attributes: { actionVersion: action.version, refusal }
1119
+ });
1120
+ throw new ActionAdmissionError(action.actionId, refusal, refusalCode);
690
1121
  }
691
1122
  async function executeInvocation(actionInvocationId, tenantId, spaceId, executionOptions = {}, executionReasonOverride) {
692
1123
  const loadedInvocation = await options.store.getActionInvocation(
@@ -715,6 +1146,13 @@ function createGovernedActionHost(options) {
715
1146
  if (!action) {
716
1147
  return fail2(invocation, "failed", `Unknown action: ${invocation.actionId}`);
717
1148
  }
1149
+ if (action.admitSubmission !== void 0 && !isAdmissionTime(invocation.admittedAt)) {
1150
+ return fail2(
1151
+ invocation,
1152
+ "failed",
1153
+ `Invocation of ${action.actionId} was not admitted at submission; an action that declares admitSubmission runs only on invocations its check admitted.`
1154
+ );
1155
+ }
718
1156
  if (action.kind === "saga") {
719
1157
  return fail2(
720
1158
  invocation,
@@ -882,6 +1320,10 @@ function createGovernedActionHost(options) {
882
1320
  }
883
1321
  const definitions = options.resolvePolicies ? await options.resolvePolicies({
884
1322
  ...authorizationInput,
1323
+ actionInvocationId,
1324
+ parameters: parsed.data,
1325
+ invocation,
1326
+ executionReason,
885
1327
  declaredPolicyIds: action.policies ?? []
886
1328
  }) : declaredPolicies(action.policies ?? []);
887
1329
  const outcomes = await evaluatePolicyDefinitions(definitions, {
@@ -992,6 +1434,7 @@ function createGovernedActionHost(options) {
992
1434
  correlationId: invocation.correlationId,
993
1435
  ...invocation.causationId ? { causationId: invocation.causationId } : {},
994
1436
  ...invocation.provenance ? { provenance: invocation.provenance } : {},
1437
+ ...isAdmissionTime(invocation.admittedAt) ? { admittedAt: invocation.admittedAt } : {},
995
1438
  db,
996
1439
  services: options.services
997
1440
  },
@@ -1119,14 +1562,20 @@ function createGovernedActionHost(options) {
1119
1562
  );
1120
1563
  adapterDeadlineTimer.unref?.();
1121
1564
  }
1565
+ const circuitBreaker = circuitBreakers.forAdapter(adapter);
1122
1566
  const result2 = await executeWithAdapterRetry({
1567
+ ...circuitBreaker ? { circuitBreaker } : {},
1123
1568
  policy: step.retryPolicy ?? adapter.retryPolicy,
1124
1569
  defaultIdempotent: adapter.idempotent,
1125
- execute: async (attempt, maxAttempts) => {
1570
+ // Every await before the call happens here, before breaker
1571
+ // admission, so nothing awaits between admission and the adapter.
1572
+ prepareAttempt: async (attempt) => {
1126
1573
  await options.store.updateAdapterInvocation(adapterInvocationId, {
1127
1574
  attempt,
1128
1575
  updatedAt: now()
1129
1576
  });
1577
+ },
1578
+ execute: (attempt, maxAttempts) => {
1130
1579
  return adapter.execute(input, {
1131
1580
  tenantId,
1132
1581
  spaceId,
@@ -1144,6 +1593,7 @@ function createGovernedActionHost(options) {
1144
1593
  getError: (result3) => result3.error,
1145
1594
  classifyOutcome: (result3) => result3.outcome ?? "transient_failure",
1146
1595
  classifyThrownError: (error) => {
1596
+ if (isAdapterCircuitOpenError(error)) return "transient_failure";
1147
1597
  if (adapterController?.signal.aborted) return "timeout";
1148
1598
  const message = error instanceof Error ? error.message : String(error);
1149
1599
  if (/timeout|deadline|timed?\s*out/i.test(message)) return "timeout";
@@ -1321,6 +1771,19 @@ function createGovernedActionHost(options) {
1321
1771
  error: message,
1322
1772
  updatedAt: now()
1323
1773
  });
1774
+ if (isAdapterCircuitOpenError(error)) {
1775
+ await appendEvent(invocation, {
1776
+ eventType: "AdapterInvocationFailed",
1777
+ subjectType: adapterEventSubject.subjectType,
1778
+ subjectId: adapterEventSubject.subjectId,
1779
+ payload: {
1780
+ adapterType: step.adapterType,
1781
+ operation: step.operation,
1782
+ reason: "circuit_open",
1783
+ ...error.retryAfterMs !== void 0 ? { retryAfterMs: error.retryAfterMs } : {}
1784
+ }
1785
+ }, `adapter:${stepIndex}:failed`);
1786
+ }
1324
1787
  return fail2(invocation, "failed", message);
1325
1788
  } finally {
1326
1789
  if (adapterDeadlineTimer) clearTimeout(adapterDeadlineTimer);
@@ -1574,6 +2037,8 @@ function createGovernedActionHost(options) {
1574
2037
  producerModuleVersion: options.outbox.producerModuleVersion(invocation.actionId, invocation.actionVersion),
1575
2038
  payloadClassification,
1576
2039
  includeProvenance: options.outbox.includeProvenance?.(envelope, payloadClassification) ?? payloadClassification !== "restricted",
2040
+ // Actor identity is excluded for every classification unless the application opts in.
2041
+ includeActor: options.outbox.includeActor?.(envelope, payloadClassification) === true,
1577
2042
  ...traceContext ? { traceContext } : {}
1578
2043
  };
1579
2044
  if (transaction) {
@@ -1643,7 +2108,13 @@ function createGovernedActionHost(options) {
1643
2108
  recordExecutionAttestation,
1644
2109
  recordExternalReconciliation,
1645
2110
  completeExternalInvocation: externalCompletion.completeExternalInvocation,
1646
- reconcileOverdueCompletions: externalCompletion.reconcileOverdueCompletions
2111
+ reconcileOverdueCompletions: externalCompletion.reconcileOverdueCompletions,
2112
+ requeueOutboxDeadLetter: createOutboxRequeue({
2113
+ store: options.store,
2114
+ authorization: options.authorization,
2115
+ telemetry: options.telemetry,
2116
+ now
2117
+ })
1647
2118
  };
1648
2119
  }
1649
2120
  function actionResult(invocation) {
@@ -1705,6 +2176,18 @@ function isTerminal(status) {
1705
2176
  function isRecord(value) {
1706
2177
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
1707
2178
  }
2179
+ function isAdmissionTime(value) {
2180
+ return value instanceof Date && Number.isFinite(value.getTime());
2181
+ }
2182
+ function ownDataProperty(value, key) {
2183
+ if (value === null || typeof value !== "object") return void 0;
2184
+ try {
2185
+ const descriptor = Reflect.getOwnPropertyDescriptor(value, key);
2186
+ return descriptor && "value" in descriptor ? descriptor.value : void 0;
2187
+ } catch {
2188
+ return void 0;
2189
+ }
2190
+ }
1708
2191
  function validateAuthorizationBinding(input, parameterDigest, authorityMoment, currentTime = /* @__PURE__ */ new Date()) {
1709
2192
  const binding = input.authorizationBinding;
1710
2193
  if (binding.id !== input.authorizationBindingId && input.authorizationBindingId) throw new Error("Authorization binding ID does not match authorizationBindingId.");
@@ -1744,6 +2227,9 @@ function toEnterpriseEventEnvelope(event, metadata) {
1744
2227
  ...event.actionInvocationId ? { actionInvocationId: event.actionInvocationId } : {},
1745
2228
  correlationId: event.correlationId,
1746
2229
  ...event.causationId ? { causationId: event.causationId } : {},
2230
+ // Opt-in only: without it the keys are absent, so existing publishers see an unchanged envelope.
2231
+ ...includesActor(metadata) && event.actorId ? { actorId: event.actorId } : {},
2232
+ ...includesActor(metadata) && event.actorType ? { actorType: event.actorType } : {},
1747
2233
  occurredAt: event.occurredAt,
1748
2234
  recordedAt: event.recordedAt,
1749
2235
  producerModuleVersion: metadata.producerModuleVersion,
@@ -1755,6 +2241,12 @@ function toEnterpriseEventEnvelope(event, metadata) {
1755
2241
  payload: event.payload
1756
2242
  };
1757
2243
  }
2244
+ function outboxActorDisclosure(metadata) {
2245
+ return includesActor(metadata) ? "included" : "suppressed";
2246
+ }
2247
+ function includesActor(metadata) {
2248
+ return metadata.includeActor === true;
2249
+ }
1758
2250
  async function runOutboxRelayCycle(options) {
1759
2251
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
1760
2252
  const maxAttempts = options.maxAttempts ?? 10;
@@ -1789,7 +2281,7 @@ async function runOutboxRelayCycle(options) {
1789
2281
  try {
1790
2282
  await publishWithTimeout(
1791
2283
  options.publisher,
1792
- record.event,
2284
+ record,
1793
2285
  options.publishTimeoutMs ?? 1e4
1794
2286
  );
1795
2287
  await stopHeartbeat();
@@ -1851,7 +2343,7 @@ async function runOutboxRelayCycle(options) {
1851
2343
  });
1852
2344
  return result;
1853
2345
  }
1854
- async function publishWithTimeout(publisher, event, timeoutMs) {
2346
+ async function publishWithTimeout(publisher, record, timeoutMs) {
1855
2347
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
1856
2348
  throw new Error("publishTimeoutMs must be a positive finite number");
1857
2349
  }
@@ -1865,7 +2357,11 @@ async function publishWithTimeout(publisher, event, timeoutMs) {
1865
2357
  }, timeoutMs);
1866
2358
  });
1867
2359
  try {
1868
- await Promise.race([publisher.publish(event, { signal: controller.signal }), timeout]);
2360
+ const context = {
2361
+ signal: controller.signal,
2362
+ actorDisclosure: record.actorDisclosure ?? "legacy"
2363
+ };
2364
+ await Promise.race([publisher.publish(record.event, context), timeout]);
1869
2365
  } finally {
1870
2366
  if (timer) clearTimeout(timer);
1871
2367
  }
@@ -1895,9 +2391,23 @@ function startOutboxHeartbeat(options, record, leaseDurationMs) {
1895
2391
  };
1896
2392
  }
1897
2393
  function cloneOutboxRecord(record) {
2394
+ const event = {
2395
+ ...record.event,
2396
+ occurredAt: new Date(record.event.occurredAt),
2397
+ recordedAt: new Date(record.event.recordedAt),
2398
+ ...record.event.traceContext ? { traceContext: { ...record.event.traceContext } } : {}
2399
+ };
2400
+ if (record.actorDisclosure !== "included") {
2401
+ delete event.actorId;
2402
+ delete event.actorType;
2403
+ }
1898
2404
  return {
1899
2405
  ...record,
1900
- event: { ...record.event, ...record.event.traceContext ? { traceContext: { ...record.event.traceContext } } : {} }
2406
+ availableAt: new Date(record.availableAt),
2407
+ createdAt: new Date(record.createdAt),
2408
+ ...record.leaseExpiresAt ? { leaseExpiresAt: new Date(record.leaseExpiresAt) } : {},
2409
+ ...record.publishedAt ? { publishedAt: new Date(record.publishedAt) } : {},
2410
+ event
1901
2411
  };
1902
2412
  }
1903
2413
  function emitRelayTelemetry(telemetry, name, occurredAt, workerId, attributes) {
@@ -1929,6 +2439,13 @@ var MemoryPlatformHostStore = class {
1929
2439
  executionAttestations = [];
1930
2440
  externalReconciliations = [];
1931
2441
  outbox = [];
2442
+ /**
2443
+ * Requeue evidence is append-only audit history, so unlike the ledger
2444
+ * arrays above, which tests have always inspected and seeded directly, it
2445
+ * is not public: `listOutboxRequeues` returns copies with their own Dates. TypeScript
2446
+ * `private`, as for the store's other internals, is a compile-time guard.
2447
+ */
2448
+ outboxRequeues = [];
1932
2449
  async transaction(run) {
1933
2450
  return run(this.db);
1934
2451
  }
@@ -2129,6 +2646,7 @@ var MemoryPlatformHostStore = class {
2129
2646
  tenantId: event.tenantId,
2130
2647
  spaceId: event.spaceId,
2131
2648
  event: toEnterpriseEventEnvelope(event, metadata),
2649
+ actorDisclosure: outboxActorDisclosure(metadata),
2132
2650
  status: "pending",
2133
2651
  attemptCount: 0,
2134
2652
  availableAt: createdAt,
@@ -2170,6 +2688,42 @@ var MemoryPlatformHostStore = class {
2170
2688
  async listOutbox(input = {}) {
2171
2689
  return this.outbox.filter((record) => (!input.tenantId || record.tenantId === input.tenantId) && (!input.spaceId || record.spaceId === input.spaceId) && (!input.statuses?.length || input.statuses.includes(record.status))).map(cloneOutboxRecord);
2172
2690
  }
2691
+ async getOutboxRecord(id, tenantId, spaceId) {
2692
+ const record = this.outbox.find((candidate) => candidate.id === id && candidate.tenantId === tenantId && candidate.spaceId === spaceId);
2693
+ return record ? cloneOutboxRecord(record) : void 0;
2694
+ }
2695
+ async requeueOutboxDeadLetter(input) {
2696
+ const record = this.outbox.find((candidate) => candidate.id === input.id && candidate.tenantId === input.tenantId && candidate.spaceId === input.spaceId);
2697
+ if (!record) return { applied: false, refusal: "not_found" };
2698
+ if (record.status !== "dead_letter") return { applied: false, refusal: "not_dead_letter", currentStatus: record.status };
2699
+ if ((record.leaseToken ?? 0) !== input.expectedLeaseToken) return { applied: false, refusal: "stale", currentStatus: record.status };
2700
+ const requeue = {
2701
+ id: input.requeueId,
2702
+ outboxId: record.id,
2703
+ tenantId: record.tenantId,
2704
+ spaceId: record.spaceId,
2705
+ eventId: record.event.eventId,
2706
+ actorId: input.actorId,
2707
+ actorType: input.actorType,
2708
+ reason: input.reason,
2709
+ requeuedAt: new Date(input.requeuedAt),
2710
+ availableAt: new Date(input.availableAt),
2711
+ priorAttemptCount: record.attemptCount,
2712
+ ...record.lastError !== void 0 ? { priorLastError: record.lastError } : {},
2713
+ priorAvailableAt: new Date(record.availableAt),
2714
+ priorLeaseToken: record.leaseToken ?? 0
2715
+ };
2716
+ record.status = "pending";
2717
+ record.attemptCount = 0;
2718
+ record.availableAt = new Date(input.availableAt);
2719
+ delete record.leaseOwner;
2720
+ delete record.leaseExpiresAt;
2721
+ this.outboxRequeues.push(requeue);
2722
+ return { applied: true, record: cloneOutboxRecord(record), requeue: cloneOutboxRequeue(requeue) };
2723
+ }
2724
+ async listOutboxRequeues(input) {
2725
+ return this.outboxRequeues.filter((requeue) => requeue.tenantId === input.tenantId && requeue.spaceId === input.spaceId && (input.outboxId === void 0 || requeue.outboxId === input.outboxId)).sort((left, right) => left.requeuedAt.getTime() - right.requeuedAt.getTime() || left.id.localeCompare(right.id)).map(cloneOutboxRequeue);
2726
+ }
2173
2727
  requireLeasedOutbox(id, workerId, leaseToken) {
2174
2728
  const record = this.outbox.find((candidate) => candidate.id === id);
2175
2729
  if (!record || record.leaseOwner !== workerId || (record.leaseToken ?? 0) !== (leaseToken ?? 0)) {
@@ -2251,6 +2805,14 @@ var MemoryPlatformHostStore = class {
2251
2805
  return this.events.filter((event) => event.tenantId === tenantId && event.spaceId === spaceId).sort((left, right) => left.sequence - right.sequence);
2252
2806
  }
2253
2807
  };
2808
+ function cloneOutboxRequeue(requeue) {
2809
+ return {
2810
+ ...requeue,
2811
+ requeuedAt: new Date(requeue.requeuedAt),
2812
+ availableAt: new Date(requeue.availableAt),
2813
+ priorAvailableAt: new Date(requeue.priorAvailableAt)
2814
+ };
2815
+ }
2254
2816
  async function applyPostgresMigrations(client, migrations) {
2255
2817
  const ordered = validateMigrations(migrations);
2256
2818
  const statements = ordered.map((migration) => migrationStatement(migration));
@@ -2492,6 +3054,43 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
2492
3054
  ON fabric_platform.action_invocations (completion_due_at)
2493
3055
  WHERE completion_due_at IS NOT NULL;
2494
3056
  `
3057
+ }, {
3058
+ version: 4,
3059
+ name: "outbox_actor_disclosure",
3060
+ // Nullable with no default, so rows written before this migration stay NULL and read back
3061
+ // as legacy. NOT VALID skips scanning existing rows; the check still applies to every
3062
+ // insert and update from here on.
3063
+ sql: `
3064
+ ALTER TABLE fabric_platform.event_outbox
3065
+ ADD COLUMN actor_disclosure text;
3066
+ ALTER TABLE fabric_platform.event_outbox
3067
+ ADD CONSTRAINT event_outbox_actor_disclosure_check
3068
+ CHECK (actor_disclosure IN ('included', 'suppressed')) NOT VALID;
3069
+ `
3070
+ }, {
3071
+ version: 5,
3072
+ name: "outbox_requeue",
3073
+ sql: `
3074
+ CREATE TABLE fabric_platform.event_outbox_requeues (
3075
+ id text PRIMARY KEY, outbox_id text NOT NULL,
3076
+ tenant_id text NOT NULL, space_id text NOT NULL, event_id text NOT NULL,
3077
+ actor_id text NOT NULL, actor_type text NOT NULL, reason text NOT NULL,
3078
+ requeued_at timestamptz NOT NULL, available_at timestamptz NOT NULL,
3079
+ prior_attempt_count integer NOT NULL, prior_last_error text,
3080
+ prior_available_at timestamptz NOT NULL, prior_lease_token bigint NOT NULL
3081
+ );
3082
+ CREATE INDEX event_outbox_requeues_record_idx
3083
+ ON fabric_platform.event_outbox_requeues (tenant_id, space_id, outbox_id, requeued_at);
3084
+ `
3085
+ }, {
3086
+ version: 6,
3087
+ name: "invocation_admission",
3088
+ // Nullable with no default: rows written before this migration, and
3089
+ // rows of actions that declare no admission check, stay NULL.
3090
+ sql: `
3091
+ ALTER TABLE fabric_platform.action_invocations
3092
+ ADD COLUMN admitted_at timestamptz;
3093
+ `
2495
3094
  }]);
2496
3095
  }
2497
3096
  async transaction(run) {
@@ -2499,44 +3098,59 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
2499
3098
  }
2500
3099
  async createActionInvocation(input) {
2501
3100
  const now = /* @__PURE__ */ new Date();
2502
- const result = await this.sql.query(
2503
- `INSERT INTO fabric_platform.action_invocations
2504
- (id,tenant_id,space_id,action_id,action_version,actor_id,actor_type,status,
2505
- parameters,result,correlation_id,causation_id,idempotency_key,
2506
- parameter_digest,parameter_digest_algorithm,idempotency_actor_id,
2507
- idempotency_authorization_binding_id,invocation_provenance,authorization_binding,execution_reason,
2508
- authorization_binding_id,error,runtime_evidence,created_at,updated_at)
2509
- 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)
2510
- ON CONFLICT (tenant_id,space_id,action_id,idempotency_key)
2511
- WHERE idempotency_key IS NOT NULL DO UPDATE SET id=fabric_platform.action_invocations.id
2512
- RETURNING *`,
2513
- [
2514
- input.id,
2515
- input.tenantId,
2516
- input.spaceId,
2517
- input.actionId,
2518
- input.actionVersion,
2519
- input.actorId,
2520
- input.actorType,
2521
- input.status,
2522
- JSON.stringify(input.parameters),
2523
- JSON.stringify(input.result),
2524
- input.correlationId,
2525
- input.causationId ?? null,
2526
- input.idempotencyKey ?? null,
2527
- input.parameterDigest ?? null,
2528
- input.parameterDigestAlgorithm ?? null,
2529
- input.idempotencyActorId ?? null,
2530
- input.idempotencyAuthorizationBindingId ?? null,
2531
- input.provenance ? JSON.stringify(input.provenance) : null,
2532
- input.authorizationBinding ? JSON.stringify(input.authorizationBinding) : null,
2533
- input.executionReason ?? null,
2534
- input.authorizationBindingId ?? null,
2535
- input.error ?? null,
2536
- input.runtimeEvidence ? JSON.stringify(input.runtimeEvidence) : null,
2537
- now
2538
- ]
2539
- );
3101
+ const admitted = input.admittedAt !== void 0;
3102
+ const admittedColumn = admitted ? "admitted_at," : "";
3103
+ const admittedValue = admitted ? "$25," : "";
3104
+ let result;
3105
+ try {
3106
+ result = await this.sql.query(
3107
+ `INSERT INTO fabric_platform.action_invocations
3108
+ (id,tenant_id,space_id,action_id,action_version,actor_id,actor_type,status,
3109
+ parameters,result,correlation_id,causation_id,idempotency_key,
3110
+ parameter_digest,parameter_digest_algorithm,idempotency_actor_id,
3111
+ idempotency_authorization_binding_id,invocation_provenance,authorization_binding,execution_reason,
3112
+ authorization_binding_id,error,runtime_evidence,${admittedColumn}created_at,updated_at)
3113
+ 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,${admittedValue}$24,$24)
3114
+ ON CONFLICT (tenant_id,space_id,action_id,idempotency_key)
3115
+ WHERE idempotency_key IS NOT NULL DO UPDATE SET id=fabric_platform.action_invocations.id
3116
+ RETURNING *`,
3117
+ [
3118
+ input.id,
3119
+ input.tenantId,
3120
+ input.spaceId,
3121
+ input.actionId,
3122
+ input.actionVersion,
3123
+ input.actorId,
3124
+ input.actorType,
3125
+ input.status,
3126
+ JSON.stringify(input.parameters),
3127
+ JSON.stringify(input.result),
3128
+ input.correlationId,
3129
+ input.causationId ?? null,
3130
+ input.idempotencyKey ?? null,
3131
+ input.parameterDigest ?? null,
3132
+ input.parameterDigestAlgorithm ?? null,
3133
+ input.idempotencyActorId ?? null,
3134
+ input.idempotencyAuthorizationBindingId ?? null,
3135
+ input.provenance ? JSON.stringify(input.provenance) : null,
3136
+ input.authorizationBinding ? JSON.stringify(input.authorizationBinding) : null,
3137
+ input.executionReason ?? null,
3138
+ input.authorizationBindingId ?? null,
3139
+ input.error ?? null,
3140
+ input.runtimeEvidence ? JSON.stringify(input.runtimeEvidence) : null,
3141
+ now,
3142
+ ...admitted ? [input.admittedAt] : []
3143
+ ]
3144
+ );
3145
+ } catch (error) {
3146
+ if (admitted && error?.code === "42703") {
3147
+ throw new Error(
3148
+ "An admitted invocation needs the action_invocations.admitted_at column; run ensureSchema() to apply PostgreSQL migration 6 (invocation_admission) before submitting actions that declare admitSubmission.",
3149
+ { cause: error }
3150
+ );
3151
+ }
3152
+ throw error;
3153
+ }
2540
3154
  return toActionRecord(result.rows[0]);
2541
3155
  }
2542
3156
  async getActionInvocation(id, tenantId, spaceId) {
@@ -2811,8 +3425,8 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
2811
3425
  ON CONFLICT (id) DO NOTHING RETURNING id
2812
3426
  )
2813
3427
  INSERT INTO fabric_platform.event_outbox
2814
- (id,tenant_id,space_id,event,status,attempt_count,available_at,created_at)
2815
- SELECT $1,$2,$3,$18::jsonb,'pending',0,$14,$14 FROM inserted_event
3428
+ (id,tenant_id,space_id,event,actor_disclosure,status,attempt_count,available_at,created_at)
3429
+ SELECT $1,$2,$3,$18::jsonb,$20,'pending',0,$14,$14 FROM inserted_event
2816
3430
  ON CONFLICT (id) DO NOTHING`,
2817
3431
  [
2818
3432
  event.id,
@@ -2833,7 +3447,8 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
2833
3447
  event.causationId ?? null,
2834
3448
  event.provenance ? JSON.stringify(event.provenance) : null,
2835
3449
  JSON.stringify(envelope),
2836
- event.consistency ?? null
3450
+ event.consistency ?? null,
3451
+ outboxActorDisclosure(metadata)
2837
3452
  ]
2838
3453
  );
2839
3454
  }
@@ -2901,6 +3516,90 @@ var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
2901
3516
  );
2902
3517
  return result.rows.map(toOutboxRecord);
2903
3518
  }
3519
+ async getOutboxRecord(id, tenantId, spaceId) {
3520
+ const result = await this.sql.query(
3521
+ `SELECT * FROM fabric_platform.event_outbox
3522
+ WHERE id=$1 AND tenant_id=$2 AND space_id=$3`,
3523
+ [id, tenantId, spaceId]
3524
+ );
3525
+ return result.rows[0] ? toOutboxRecord(result.rows[0]) : void 0;
3526
+ }
3527
+ async requeueOutboxDeadLetter(input) {
3528
+ const result = await this.sql.query(
3529
+ `WITH prior AS (
3530
+ SELECT id, tenant_id, space_id, event->>'eventId' AS event_id,
3531
+ attempt_count, last_error, available_at, lease_token
3532
+ FROM fabric_platform.event_outbox
3533
+ WHERE id=$1 AND tenant_id=$2 AND space_id=$3 AND status='dead_letter'
3534
+ AND lease_token=$10
3535
+ FOR UPDATE
3536
+ ), requeued AS (
3537
+ UPDATE fabric_platform.event_outbox AS item
3538
+ SET status='pending', attempt_count=0, available_at=$9,
3539
+ lease_owner=NULL, lease_expires_at=NULL
3540
+ FROM prior WHERE item.id=prior.id
3541
+ RETURNING item.*
3542
+ ), evidence AS (
3543
+ INSERT INTO fabric_platform.event_outbox_requeues
3544
+ (id,outbox_id,tenant_id,space_id,event_id,actor_id,actor_type,reason,
3545
+ requeued_at,available_at,prior_attempt_count,prior_last_error,
3546
+ prior_available_at,prior_lease_token)
3547
+ SELECT $4,prior.id,prior.tenant_id,prior.space_id,prior.event_id,$5,$6,$7,
3548
+ $8,$9,prior.attempt_count,prior.last_error,prior.available_at,prior.lease_token
3549
+ FROM prior JOIN requeued ON requeued.id=prior.id
3550
+ RETURNING *
3551
+ )
3552
+ SELECT requeued.*,
3553
+ evidence.id AS requeue_id, evidence.outbox_id AS requeue_outbox_id,
3554
+ evidence.tenant_id AS requeue_tenant_id, evidence.space_id AS requeue_space_id,
3555
+ evidence.event_id AS requeue_event_id, evidence.actor_id AS requeue_actor_id,
3556
+ evidence.actor_type AS requeue_actor_type, evidence.reason AS requeue_reason,
3557
+ evidence.requeued_at AS requeue_requeued_at, evidence.available_at AS requeue_available_at,
3558
+ evidence.prior_attempt_count AS requeue_prior_attempt_count,
3559
+ evidence.prior_last_error AS requeue_prior_last_error,
3560
+ evidence.prior_available_at AS requeue_prior_available_at,
3561
+ evidence.prior_lease_token AS requeue_prior_lease_token
3562
+ FROM requeued JOIN evidence ON evidence.outbox_id=requeued.id`,
3563
+ [
3564
+ input.id,
3565
+ input.tenantId,
3566
+ input.spaceId,
3567
+ input.requeueId,
3568
+ input.actorId,
3569
+ input.actorType,
3570
+ input.reason,
3571
+ input.requeuedAt,
3572
+ input.availableAt,
3573
+ input.expectedLeaseToken
3574
+ ]
3575
+ );
3576
+ const row = result.rows[0];
3577
+ if (row) {
3578
+ return {
3579
+ applied: true,
3580
+ record: toOutboxRecord(row),
3581
+ requeue: toOutboxRequeueRecord(row, "requeue_")
3582
+ };
3583
+ }
3584
+ const current = await this.sql.query(
3585
+ `SELECT status FROM fabric_platform.event_outbox
3586
+ WHERE id=$1 AND tenant_id=$2 AND space_id=$3`,
3587
+ [input.id, input.tenantId, input.spaceId]
3588
+ );
3589
+ const found = current.rows[0];
3590
+ if (!found) return { applied: false, refusal: "not_found" };
3591
+ const status = found.status;
3592
+ return status === "dead_letter" ? { applied: false, refusal: "stale", currentStatus: status } : { applied: false, refusal: "not_dead_letter", currentStatus: status };
3593
+ }
3594
+ async listOutboxRequeues(input) {
3595
+ const result = await this.sql.query(
3596
+ `SELECT * FROM fabric_platform.event_outbox_requeues
3597
+ WHERE tenant_id=$1 AND space_id=$2 AND ($3::text IS NULL OR outbox_id=$3)
3598
+ ORDER BY requeued_at,id`,
3599
+ [input.tenantId, input.spaceId, input.outboxId ?? null]
3600
+ );
3601
+ return result.rows.map((row) => toOutboxRequeueRecord(row, ""));
3602
+ }
2904
3603
  async nextEventSequence(tenantId, spaceId) {
2905
3604
  const result = await this.sql.query(
2906
3605
  `INSERT INTO fabric_platform.event_sequences (tenant_id,space_id,next_sequence)
@@ -3173,6 +3872,7 @@ function toActionRecord(row) {
3173
3872
  ...row.mutation_footprint ? { mutationFootprint: row.mutation_footprint } : {},
3174
3873
  ...row.execution_principal ? { executionPrincipal: row.execution_principal } : {},
3175
3874
  ...row.runtime_evidence ? { runtimeEvidence: row.runtime_evidence } : {},
3875
+ ...row.admitted_at ? { admittedAt: new Date(row.admitted_at) } : {},
3176
3876
  ...row.error ? { error: String(row.error) } : {},
3177
3877
  createdAt: new Date(row.created_at),
3178
3878
  updatedAt: new Date(row.updated_at)
@@ -3241,10 +3941,13 @@ function toEventRecord(row) {
3241
3941
  }
3242
3942
  function toOutboxRecord(row) {
3243
3943
  const event = row.event;
3944
+ const actorDisclosure = row.actor_disclosure === "included" || row.actor_disclosure === "suppressed" ? row.actor_disclosure : void 0;
3945
+ const actorIncluded = actorDisclosure === "included";
3244
3946
  return {
3245
3947
  id: String(row.id),
3246
3948
  tenantId: String(row.tenant_id),
3247
3949
  spaceId: String(row.space_id),
3950
+ ...actorDisclosure ? { actorDisclosure } : {},
3248
3951
  event: {
3249
3952
  eventId: String(event.eventId),
3250
3953
  eventType: String(event.eventType),
@@ -3257,6 +3960,8 @@ function toOutboxRecord(row) {
3257
3960
  ...event.actionInvocationId ? { actionInvocationId: String(event.actionInvocationId) } : {},
3258
3961
  correlationId: String(event.correlationId),
3259
3962
  ...event.causationId ? { causationId: String(event.causationId) } : {},
3963
+ ...actorIncluded && event.actorId ? { actorId: String(event.actorId) } : {},
3964
+ ...actorIncluded && event.actorType ? { actorType: String(event.actorType) } : {},
3260
3965
  ...event.provenance ? { provenance: event.provenance } : {},
3261
3966
  ...event.consistency ? { consistency: event.consistency } : {},
3262
3967
  occurredAt: new Date(event.occurredAt),
@@ -3277,6 +3982,26 @@ function toOutboxRecord(row) {
3277
3982
  ...row.published_at ? { publishedAt: new Date(row.published_at) } : {}
3278
3983
  };
3279
3984
  }
3985
+ function toOutboxRequeueRecord(row, prefix) {
3986
+ const field = (name) => row[`${prefix}${name}`];
3987
+ const priorLastError = field("prior_last_error");
3988
+ return {
3989
+ id: String(field("id")),
3990
+ outboxId: String(field("outbox_id")),
3991
+ tenantId: String(field("tenant_id")),
3992
+ spaceId: String(field("space_id")),
3993
+ eventId: String(field("event_id")),
3994
+ actorId: String(field("actor_id")),
3995
+ actorType: String(field("actor_type")),
3996
+ reason: String(field("reason")),
3997
+ requeuedAt: new Date(field("requeued_at")),
3998
+ availableAt: new Date(field("available_at")),
3999
+ priorAttemptCount: Number(field("prior_attempt_count")),
4000
+ ...priorLastError !== null && priorLastError !== void 0 ? { priorLastError: String(priorLastError) } : {},
4001
+ priorAvailableAt: new Date(field("prior_available_at")),
4002
+ priorLeaseToken: Number(field("prior_lease_token"))
4003
+ };
4004
+ }
3280
4005
 
3281
4006
  // src/worker.ts
3282
4007
  var DEFAULT_BATCH_SIZE = 10;
@@ -3737,6 +4462,6 @@ async function runTenantIsolationChecks(subject) {
3737
4462
  return { passed: checks.every((check) => check.status !== "failed"), checks };
3738
4463
  }
3739
4464
 
3740
- export { ExternalCompletionError, IdempotencyConflictError, MemoryPlatformHostStore, PARAMETER_DIGEST_ALGORITHM, PLATFORM_HOST_CONTRACT_VERSION, PLATFORM_HOST_HEALTH_CONTRACT_VERSION, PLATFORM_HOST_METRIC_NAMES, PostgresPlatformHostStore, applyPostgresMigrations, canonicalJson, cloneOutboxRecord, createDurableSagaParentLifecycle, createGovernedActionHost, createStoreBackedActionDispatcher, digestParameters, emitPlatformHostTelemetry, getPlatformHostHealthSnapshot, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, runTenantIsolationChecks, tenantIsolationChecks, toEnterpriseEventEnvelope };
4465
+ export { ActionAdmissionError, ExternalCompletionError, IdempotencyConflictError, MemoryPlatformHostStore, PARAMETER_DIGEST_ALGORITHM, PLATFORM_HOST_CONTRACT_VERSION, PLATFORM_HOST_HEALTH_CONTRACT_VERSION, PLATFORM_HOST_METRIC_NAMES, PostgresPlatformHostStore, applyPostgresMigrations, cloneOutboxRecord, createDurableSagaParentLifecycle, createGovernedActionHost, createStoreBackedActionDispatcher, digestParameters, emitPlatformHostTelemetry, getPlatformHostHealthSnapshot, outboxActorDisclosure, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, runTenantIsolationChecks, tenantIsolationChecks, toEnterpriseEventEnvelope };
3741
4466
  //# sourceMappingURL=index.js.map
3742
4467
  //# sourceMappingURL=index.js.map