@fabricorg/platform-host 0.4.2 → 0.5.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/CHANGELOG.md +10 -0
- package/README.md +12 -0
- package/dist/index.cjs +261 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -4
- package/dist/index.d.ts +80 -4
- package/dist/index.js +262 -4
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AdapterRegistry, resolveAction, createFabricId, evaluatePolicyDefinitions, aggregatePolicyOutcomes, validateTransition, executeWithAdapterRetry, resolveStateMachine } from '@fabricorg/platform';
|
|
1
|
+
import { AdapterRegistry, resolveAction, createFabricId, assertMutationGovernanceContext, evaluatePolicyDefinitions, aggregatePolicyOutcomes, validateTransition, executeWithAdapterRetry, resolveStateMachine } from '@fabricorg/platform';
|
|
2
2
|
|
|
3
3
|
// src/host.ts
|
|
4
4
|
var DEFAULT_EXTRACT_EVENTS = (data) => {
|
|
@@ -127,6 +127,23 @@ function createGovernedActionHost(options) {
|
|
|
127
127
|
const message = parsed.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
|
|
128
128
|
return fail(invocation, "validation_failed", message);
|
|
129
129
|
}
|
|
130
|
+
if (options.resolveMutationGovernance && !invocation.mutationFootprint) {
|
|
131
|
+
const governanceStore2 = asGovernanceStore(options.store);
|
|
132
|
+
if (!governanceStore2) {
|
|
133
|
+
return fail(invocation, "failed", "The configured mutation-governance resolver requires a governance-capable store.");
|
|
134
|
+
}
|
|
135
|
+
const context = await options.resolveMutationGovernance({
|
|
136
|
+
actionInvocationId,
|
|
137
|
+
actionId: action.actionId,
|
|
138
|
+
tenantId,
|
|
139
|
+
spaceId,
|
|
140
|
+
actorId: invocation.actorId,
|
|
141
|
+
actorType: invocation.actorType,
|
|
142
|
+
parameters: parsed.data
|
|
143
|
+
});
|
|
144
|
+
assertMutationGovernanceContext(context);
|
|
145
|
+
invocation = await governanceStore2.recordMutationGovernance(actionInvocationId, tenantId, spaceId, context);
|
|
146
|
+
}
|
|
130
147
|
if (invocation.actorType === "agent" && options.hitlEvaluator) {
|
|
131
148
|
const hitlParameters = isRecord(parsed.data) ? parsed.data : invocation.parameters;
|
|
132
149
|
const approvalStore = asApprovalStore(options.store);
|
|
@@ -217,6 +234,26 @@ function createGovernedActionHost(options) {
|
|
|
217
234
|
}, `compliance:${aggregate.policyId}`);
|
|
218
235
|
return fail(invocation, "blocked_by_policy", reason);
|
|
219
236
|
}
|
|
237
|
+
if (options.resolvePolicyObligations) {
|
|
238
|
+
const governanceStore2 = asGovernanceStore(options.store);
|
|
239
|
+
if (!governanceStore2) {
|
|
240
|
+
return fail(invocation, "failed", "Policy obligations require a governance-capable store.");
|
|
241
|
+
}
|
|
242
|
+
const existing = await governanceStore2.listPolicyObligations(actionInvocationId, tenantId, spaceId);
|
|
243
|
+
if (existing.length === 0) {
|
|
244
|
+
const createdAt = now();
|
|
245
|
+
const obligations = await options.resolvePolicyObligations(outcomes);
|
|
246
|
+
await governanceStore2.appendPolicyObligations(obligations.map((obligation) => ({
|
|
247
|
+
...obligation,
|
|
248
|
+
status: obligation.status ?? "pending",
|
|
249
|
+
actionInvocationId,
|
|
250
|
+
tenantId,
|
|
251
|
+
spaceId,
|
|
252
|
+
createdAt,
|
|
253
|
+
updatedAt: createdAt
|
|
254
|
+
})));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
220
257
|
const binding = action.stateMachine;
|
|
221
258
|
if (binding) {
|
|
222
259
|
const entityId = binding.getEntityId(parsed.data);
|
|
@@ -372,6 +409,21 @@ function createGovernedActionHost(options) {
|
|
|
372
409
|
output,
|
|
373
410
|
updatedAt: now()
|
|
374
411
|
});
|
|
412
|
+
if (options.extractExecutionAttestation) {
|
|
413
|
+
const attestation = await options.extractExecutionAttestation({
|
|
414
|
+
actionInvocationId,
|
|
415
|
+
adapterInvocationId,
|
|
416
|
+
adapterType: step.adapterType,
|
|
417
|
+
operation: step.operation,
|
|
418
|
+
vendor: adapter.vendor,
|
|
419
|
+
startedAt,
|
|
420
|
+
completedAt: now(),
|
|
421
|
+
output
|
|
422
|
+
});
|
|
423
|
+
if (attestation) {
|
|
424
|
+
await recordExecutionAttestation(actionInvocationId, tenantId, spaceId, attestation, adapterInvocationId);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
375
427
|
await appendEvent(invocation, {
|
|
376
428
|
eventType: "AdapterInvocationSucceeded",
|
|
377
429
|
subjectType: adapterEventSubject.subjectType,
|
|
@@ -398,6 +450,14 @@ function createGovernedActionHost(options) {
|
|
|
398
450
|
await appendEvent(invocation, event, `domain:${index}`, action.version);
|
|
399
451
|
}
|
|
400
452
|
}
|
|
453
|
+
const governanceStore = asGovernanceStore(options.store);
|
|
454
|
+
if (governanceStore && options.resolvePolicyObligations) {
|
|
455
|
+
const obligations = await governanceStore.listPolicyObligations(actionInvocationId, tenantId, spaceId);
|
|
456
|
+
const unsatisfied = obligations.filter((obligation) => (obligation.required ?? true) && obligation.status !== "satisfied" && obligation.status !== "waived");
|
|
457
|
+
if (unsatisfied.length > 0) {
|
|
458
|
+
return fail(invocation, "failed", `Unsatisfied policy obligations: ${unsatisfied.map((item) => item.id).join(", ")}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
401
461
|
const result = withoutPrivateHostFields(data, eventResultFields);
|
|
402
462
|
await options.store.updateActionInvocation(actionInvocationId, tenantId, spaceId, {
|
|
403
463
|
status: "completed",
|
|
@@ -477,6 +537,38 @@ function createGovernedActionHost(options) {
|
|
|
477
537
|
if (!decision.approved) return actionResult(transitioned);
|
|
478
538
|
return executeInvocation(actionInvocationId, tenantId, spaceId, { leaseOwner });
|
|
479
539
|
}
|
|
540
|
+
async function recordExecutionAttestation(actionInvocationId, tenantId, spaceId, attestation, adapterInvocationId) {
|
|
541
|
+
const governanceStore = asGovernanceStore(options.store);
|
|
542
|
+
if (!governanceStore) throw new Error("Execution attestations require a governance-capable store.");
|
|
543
|
+
const invocation = await options.store.getActionInvocation(actionInvocationId, tenantId, spaceId);
|
|
544
|
+
if (!invocation) throw new Error(`ActionInvocation not found: ${actionInvocationId}`);
|
|
545
|
+
const id = lifecycleId("att", actionInvocationId, adapterInvocationId ?? `${attestation.provider}:${attestation.operation}:${attestation.externalOperationId ?? "external"}`);
|
|
546
|
+
await governanceStore.appendExecutionAttestation({
|
|
547
|
+
...attestation,
|
|
548
|
+
id,
|
|
549
|
+
actionInvocationId,
|
|
550
|
+
tenantId,
|
|
551
|
+
spaceId,
|
|
552
|
+
...adapterInvocationId ? { adapterInvocationId } : {},
|
|
553
|
+
recordedAt: now()
|
|
554
|
+
});
|
|
555
|
+
for (const obligationId of attestation.satisfiesObligations ?? []) {
|
|
556
|
+
await governanceStore.updatePolicyObligation(actionInvocationId, obligationId, "satisfied", attestation.evidenceReferences);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async function recordExternalReconciliation(actionInvocationId, tenantId, spaceId, reconciliation) {
|
|
560
|
+
const governanceStore = asGovernanceStore(options.store);
|
|
561
|
+
if (!governanceStore) throw new Error("External reconciliation requires a governance-capable store.");
|
|
562
|
+
const invocation = await options.store.getActionInvocation(actionInvocationId, tenantId, spaceId);
|
|
563
|
+
if (!invocation) throw new Error(`ActionInvocation not found: ${actionInvocationId}`);
|
|
564
|
+
await governanceStore.appendExternalReconciliation({
|
|
565
|
+
...reconciliation,
|
|
566
|
+
id: lifecycleId("rec", actionInvocationId, `${reconciliation.provider}:${reconciliation.attempt}`),
|
|
567
|
+
actionInvocationId,
|
|
568
|
+
tenantId,
|
|
569
|
+
spaceId
|
|
570
|
+
});
|
|
571
|
+
}
|
|
480
572
|
async function appendEvent(invocation, event, deduplicationKey, defaultEventSchemaVersion = 1) {
|
|
481
573
|
const timestamp = now();
|
|
482
574
|
const envelope = {
|
|
@@ -517,7 +609,7 @@ function createGovernedActionHost(options) {
|
|
|
517
609
|
...invocation.hitlRiskTier ? { hitlRiskTier: invocation.hitlRiskTier } : {}
|
|
518
610
|
};
|
|
519
611
|
}
|
|
520
|
-
return { submitAction, executeInvocation, resumeApprovedInvocation };
|
|
612
|
+
return { submitAction, executeInvocation, resumeApprovedInvocation, recordExecutionAttestation, recordExternalReconciliation };
|
|
521
613
|
}
|
|
522
614
|
function actionResult(invocation) {
|
|
523
615
|
return {
|
|
@@ -533,6 +625,10 @@ function asApprovalStore(store) {
|
|
|
533
625
|
const candidate = store;
|
|
534
626
|
return typeof candidate.recordHitlDecision === "function" && typeof candidate.beginApprovalDecision === "function" ? store : void 0;
|
|
535
627
|
}
|
|
628
|
+
function asGovernanceStore(store) {
|
|
629
|
+
const candidate = store;
|
|
630
|
+
return typeof candidate.recordMutationGovernance === "function" && typeof candidate.appendPolicyObligations === "function" && typeof candidate.appendExecutionAttestation === "function" ? store : void 0;
|
|
631
|
+
}
|
|
536
632
|
function numberValue(value) {
|
|
537
633
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
538
634
|
}
|
|
@@ -595,6 +691,9 @@ var MemoryPlatformHostStore = class {
|
|
|
595
691
|
policyEvaluations = [];
|
|
596
692
|
adapterInvocations = [];
|
|
597
693
|
events = [];
|
|
694
|
+
policyObligations = [];
|
|
695
|
+
executionAttestations = [];
|
|
696
|
+
externalReconciliations = [];
|
|
598
697
|
async transaction(run) {
|
|
599
698
|
return run(this.db);
|
|
600
699
|
}
|
|
@@ -663,6 +762,40 @@ var MemoryPlatformHostStore = class {
|
|
|
663
762
|
if (this.policyEvaluations.some((candidate) => candidate.id === record.id)) return;
|
|
664
763
|
this.policyEvaluations.push(record);
|
|
665
764
|
}
|
|
765
|
+
async recordMutationGovernance(actionInvocationId, tenantId, spaceId, context) {
|
|
766
|
+
const record = await this.getActionInvocation(actionInvocationId, tenantId, spaceId);
|
|
767
|
+
if (!record) throw new Error(`ActionInvocation not found: ${actionInvocationId}`);
|
|
768
|
+
record.mutationFootprint = context.footprint;
|
|
769
|
+
record.executionPrincipal = context.executionPrincipal;
|
|
770
|
+
record.updatedAt = /* @__PURE__ */ new Date();
|
|
771
|
+
return record;
|
|
772
|
+
}
|
|
773
|
+
async appendPolicyObligations(records) {
|
|
774
|
+
for (const record of records) {
|
|
775
|
+
if (!this.policyObligations.some((candidate) => candidate.actionInvocationId === record.actionInvocationId && candidate.id === record.id)) {
|
|
776
|
+
this.policyObligations.push(record);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
async listPolicyObligations(actionInvocationId, tenantId, spaceId) {
|
|
781
|
+
return this.policyObligations.filter((record) => record.actionInvocationId === actionInvocationId && record.tenantId === tenantId && record.spaceId === spaceId);
|
|
782
|
+
}
|
|
783
|
+
async updatePolicyObligation(actionInvocationId, obligationId, status, evidenceReferences) {
|
|
784
|
+
const record = this.policyObligations.find((candidate) => candidate.actionInvocationId === actionInvocationId && candidate.id === obligationId);
|
|
785
|
+
if (!record) throw new Error(`Policy obligation not found: ${obligationId}`);
|
|
786
|
+
record.status = status;
|
|
787
|
+
if (evidenceReferences) record.evidenceReferences = evidenceReferences;
|
|
788
|
+
record.updatedAt = /* @__PURE__ */ new Date();
|
|
789
|
+
}
|
|
790
|
+
async appendExecutionAttestation(record) {
|
|
791
|
+
if (!this.executionAttestations.some((candidate) => candidate.id === record.id)) this.executionAttestations.push(record);
|
|
792
|
+
}
|
|
793
|
+
async listExecutionAttestations(actionInvocationId, tenantId, spaceId) {
|
|
794
|
+
return this.executionAttestations.filter((record) => record.actionInvocationId === actionInvocationId && record.tenantId === tenantId && record.spaceId === spaceId);
|
|
795
|
+
}
|
|
796
|
+
async appendExternalReconciliation(record) {
|
|
797
|
+
if (!this.externalReconciliations.some((candidate) => candidate.id === record.id)) this.externalReconciliations.push(record);
|
|
798
|
+
}
|
|
666
799
|
async createAdapterInvocation(record) {
|
|
667
800
|
if (this.adapterInvocations.some((candidate) => candidate.id === record.id)) return;
|
|
668
801
|
this.adapterInvocations.push(record);
|
|
@@ -737,6 +870,7 @@ var PostgresPlatformHostStore = class {
|
|
|
737
870
|
attempt_count integer NOT NULL DEFAULT 0, lease_owner text,
|
|
738
871
|
lease_expires_at timestamptz, hitl_route text, hitl_risk_tier text,
|
|
739
872
|
hitl_reason text, hitl_policy_version text, approval_decision jsonb,
|
|
873
|
+
mutation_footprint jsonb, execution_principal jsonb,
|
|
740
874
|
created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL
|
|
741
875
|
);
|
|
742
876
|
ALTER TABLE fabric_platform.action_invocations
|
|
@@ -748,7 +882,9 @@ var PostgresPlatformHostStore = class {
|
|
|
748
882
|
ADD COLUMN IF NOT EXISTS hitl_risk_tier text,
|
|
749
883
|
ADD COLUMN IF NOT EXISTS hitl_reason text,
|
|
750
884
|
ADD COLUMN IF NOT EXISTS hitl_policy_version text,
|
|
751
|
-
ADD COLUMN IF NOT EXISTS approval_decision jsonb
|
|
885
|
+
ADD COLUMN IF NOT EXISTS approval_decision jsonb,
|
|
886
|
+
ADD COLUMN IF NOT EXISTS mutation_footprint jsonb,
|
|
887
|
+
ADD COLUMN IF NOT EXISTS execution_principal jsonb;
|
|
752
888
|
CREATE UNIQUE INDEX IF NOT EXISTS action_invocations_idempotency_idx
|
|
753
889
|
ON fabric_platform.action_invocations
|
|
754
890
|
(tenant_id, space_id, action_id, idempotency_key)
|
|
@@ -767,6 +903,24 @@ var PostgresPlatformHostStore = class {
|
|
|
767
903
|
input jsonb NOT NULL, output jsonb, error text, attempt integer NOT NULL,
|
|
768
904
|
created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL
|
|
769
905
|
);
|
|
906
|
+
CREATE TABLE IF NOT EXISTS fabric_platform.policy_obligations (
|
|
907
|
+
action_invocation_id text NOT NULL, id text NOT NULL,
|
|
908
|
+
tenant_id text NOT NULL, space_id text NOT NULL, type text NOT NULL,
|
|
909
|
+
status text NOT NULL, required boolean NOT NULL DEFAULT true,
|
|
910
|
+
description text, evidence_references jsonb, metadata jsonb,
|
|
911
|
+
created_at timestamptz NOT NULL, updated_at timestamptz NOT NULL,
|
|
912
|
+
PRIMARY KEY (action_invocation_id,id)
|
|
913
|
+
);
|
|
914
|
+
CREATE TABLE IF NOT EXISTS fabric_platform.execution_attestations (
|
|
915
|
+
id text PRIMARY KEY, action_invocation_id text NOT NULL,
|
|
916
|
+
tenant_id text NOT NULL, space_id text NOT NULL, adapter_invocation_id text,
|
|
917
|
+
attestation jsonb NOT NULL, recorded_at timestamptz NOT NULL
|
|
918
|
+
);
|
|
919
|
+
CREATE TABLE IF NOT EXISTS fabric_platform.external_reconciliations (
|
|
920
|
+
id text PRIMARY KEY, action_invocation_id text NOT NULL,
|
|
921
|
+
tenant_id text NOT NULL, space_id text NOT NULL,
|
|
922
|
+
reconciliation jsonb NOT NULL
|
|
923
|
+
);
|
|
770
924
|
CREATE TABLE IF NOT EXISTS fabric_platform.event_sequences (
|
|
771
925
|
tenant_id text NOT NULL, space_id text NOT NULL, next_sequence bigint NOT NULL,
|
|
772
926
|
PRIMARY KEY (tenant_id, space_id)
|
|
@@ -878,7 +1032,7 @@ var PostgresPlatformHostStore = class {
|
|
|
878
1032
|
status=CASE WHEN $5::boolean THEN 'running' ELSE 'failed' END,
|
|
879
1033
|
error=CASE WHEN $5::boolean THEN NULL ELSE $6 END,
|
|
880
1034
|
lease_owner=CASE WHEN $5::boolean THEN $7 ELSE NULL END,
|
|
881
|
-
lease_expires_at=CASE WHEN $5::boolean THEN $8 ELSE NULL END,
|
|
1035
|
+
lease_expires_at=CASE WHEN $5::boolean THEN $8::timestamptz ELSE NULL END,
|
|
882
1036
|
attempt_count=CASE WHEN $5::boolean THEN GREATEST(attempt_count,1) ELSE attempt_count END,
|
|
883
1037
|
updated_at=$9
|
|
884
1038
|
WHERE id=$1 AND tenant_id=$2 AND space_id=$3 AND status='waiting_for_approval'
|
|
@@ -912,6 +1066,80 @@ var PostgresPlatformHostStore = class {
|
|
|
912
1066
|
]
|
|
913
1067
|
);
|
|
914
1068
|
}
|
|
1069
|
+
async recordMutationGovernance(actionInvocationId, tenantId, spaceId, context) {
|
|
1070
|
+
const result = await this.sql.query(
|
|
1071
|
+
`UPDATE fabric_platform.action_invocations SET mutation_footprint=$4::jsonb,
|
|
1072
|
+
execution_principal=$5::jsonb, updated_at=now()
|
|
1073
|
+
WHERE id=$1 AND tenant_id=$2 AND space_id=$3 RETURNING *`,
|
|
1074
|
+
[actionInvocationId, tenantId, spaceId, JSON.stringify(context.footprint), JSON.stringify(context.executionPrincipal)]
|
|
1075
|
+
);
|
|
1076
|
+
if (!result.rows[0]) throw new Error(`ActionInvocation not found: ${actionInvocationId}`);
|
|
1077
|
+
return toActionRecord(result.rows[0]);
|
|
1078
|
+
}
|
|
1079
|
+
async appendPolicyObligations(records) {
|
|
1080
|
+
for (const record of records) {
|
|
1081
|
+
await this.sql.query(
|
|
1082
|
+
`INSERT INTO fabric_platform.policy_obligations
|
|
1083
|
+
(action_invocation_id,id,tenant_id,space_id,type,status,required,description,evidence_references,metadata,created_at,updated_at)
|
|
1084
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$10::jsonb,$11,$12)
|
|
1085
|
+
ON CONFLICT (action_invocation_id,id) DO NOTHING`,
|
|
1086
|
+
[
|
|
1087
|
+
record.actionInvocationId,
|
|
1088
|
+
record.id,
|
|
1089
|
+
record.tenantId,
|
|
1090
|
+
record.spaceId,
|
|
1091
|
+
record.type,
|
|
1092
|
+
record.status,
|
|
1093
|
+
record.required ?? true,
|
|
1094
|
+
record.description ?? null,
|
|
1095
|
+
record.evidenceReferences ? JSON.stringify(record.evidenceReferences) : null,
|
|
1096
|
+
record.metadata ? JSON.stringify(record.metadata) : null,
|
|
1097
|
+
record.createdAt,
|
|
1098
|
+
record.updatedAt
|
|
1099
|
+
]
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
async listPolicyObligations(actionInvocationId, tenantId, spaceId) {
|
|
1104
|
+
const result = await this.sql.query(
|
|
1105
|
+
`SELECT * FROM fabric_platform.policy_obligations WHERE action_invocation_id=$1 AND tenant_id=$2 AND space_id=$3 ORDER BY created_at,id`,
|
|
1106
|
+
[actionInvocationId, tenantId, spaceId]
|
|
1107
|
+
);
|
|
1108
|
+
return result.rows.map(toObligationRecord);
|
|
1109
|
+
}
|
|
1110
|
+
async updatePolicyObligation(actionInvocationId, obligationId, status, evidenceReferences) {
|
|
1111
|
+
await this.sql.query(
|
|
1112
|
+
`UPDATE fabric_platform.policy_obligations SET status=$3,
|
|
1113
|
+
evidence_references=COALESCE($4::jsonb,evidence_references), updated_at=now()
|
|
1114
|
+
WHERE action_invocation_id=$1 AND id=$2`,
|
|
1115
|
+
[actionInvocationId, obligationId, status, evidenceReferences ? JSON.stringify(evidenceReferences) : null]
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
async appendExecutionAttestation(record) {
|
|
1119
|
+
const { id, actionInvocationId, tenantId, spaceId, adapterInvocationId, recordedAt, ...attestation } = record;
|
|
1120
|
+
await this.sql.query(
|
|
1121
|
+
`INSERT INTO fabric_platform.execution_attestations
|
|
1122
|
+
(id,action_invocation_id,tenant_id,space_id,adapter_invocation_id,attestation,recorded_at)
|
|
1123
|
+
VALUES ($1,$2,$3,$4,$5,$6::jsonb,$7) ON CONFLICT (id) DO NOTHING`,
|
|
1124
|
+
[id, actionInvocationId, tenantId, spaceId, adapterInvocationId ?? null, JSON.stringify(attestation), recordedAt]
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
async listExecutionAttestations(actionInvocationId, tenantId, spaceId) {
|
|
1128
|
+
const result = await this.sql.query(
|
|
1129
|
+
`SELECT * FROM fabric_platform.execution_attestations WHERE action_invocation_id=$1 AND tenant_id=$2 AND space_id=$3 ORDER BY recorded_at,id`,
|
|
1130
|
+
[actionInvocationId, tenantId, spaceId]
|
|
1131
|
+
);
|
|
1132
|
+
return result.rows.map(toAttestationRecord);
|
|
1133
|
+
}
|
|
1134
|
+
async appendExternalReconciliation(record) {
|
|
1135
|
+
const { id, actionInvocationId, tenantId, spaceId, ...reconciliation } = record;
|
|
1136
|
+
await this.sql.query(
|
|
1137
|
+
`INSERT INTO fabric_platform.external_reconciliations
|
|
1138
|
+
(id,action_invocation_id,tenant_id,space_id,reconciliation)
|
|
1139
|
+
VALUES ($1,$2,$3,$4,$5::jsonb) ON CONFLICT (id) DO NOTHING`,
|
|
1140
|
+
[id, actionInvocationId, tenantId, spaceId, JSON.stringify(reconciliation)]
|
|
1141
|
+
);
|
|
1142
|
+
}
|
|
915
1143
|
async createAdapterInvocation(record) {
|
|
916
1144
|
await this.sql.query(
|
|
917
1145
|
`INSERT INTO fabric_platform.adapter_invocations
|
|
@@ -1109,11 +1337,41 @@ function toActionRecord(row) {
|
|
|
1109
1337
|
...row.hitl_reason ? { hitlReason: String(row.hitl_reason) } : {},
|
|
1110
1338
|
...row.hitl_policy_version ? { hitlPolicyVersion: String(row.hitl_policy_version) } : {},
|
|
1111
1339
|
...row.approval_decision ? { approvalDecision: toApprovalDecision(row.approval_decision) } : {},
|
|
1340
|
+
...row.mutation_footprint ? { mutationFootprint: row.mutation_footprint } : {},
|
|
1341
|
+
...row.execution_principal ? { executionPrincipal: row.execution_principal } : {},
|
|
1112
1342
|
...row.error ? { error: String(row.error) } : {},
|
|
1113
1343
|
createdAt: new Date(row.created_at),
|
|
1114
1344
|
updatedAt: new Date(row.updated_at)
|
|
1115
1345
|
};
|
|
1116
1346
|
}
|
|
1347
|
+
function toObligationRecord(row) {
|
|
1348
|
+
return {
|
|
1349
|
+
id: String(row.id),
|
|
1350
|
+
actionInvocationId: String(row.action_invocation_id),
|
|
1351
|
+
tenantId: String(row.tenant_id),
|
|
1352
|
+
spaceId: String(row.space_id),
|
|
1353
|
+
type: String(row.type),
|
|
1354
|
+
status: String(row.status),
|
|
1355
|
+
required: Boolean(row.required),
|
|
1356
|
+
...row.description ? { description: String(row.description) } : {},
|
|
1357
|
+
...row.evidence_references ? { evidenceReferences: row.evidence_references } : {},
|
|
1358
|
+
...row.metadata ? { metadata: row.metadata } : {},
|
|
1359
|
+
createdAt: new Date(row.created_at),
|
|
1360
|
+
updatedAt: new Date(row.updated_at)
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
function toAttestationRecord(row) {
|
|
1364
|
+
const attestation = row.attestation;
|
|
1365
|
+
return {
|
|
1366
|
+
...attestation,
|
|
1367
|
+
id: String(row.id),
|
|
1368
|
+
actionInvocationId: String(row.action_invocation_id),
|
|
1369
|
+
tenantId: String(row.tenant_id),
|
|
1370
|
+
spaceId: String(row.space_id),
|
|
1371
|
+
...row.adapter_invocation_id ? { adapterInvocationId: String(row.adapter_invocation_id) } : {},
|
|
1372
|
+
recordedAt: new Date(row.recorded_at)
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1117
1375
|
function toApprovalDecision(value) {
|
|
1118
1376
|
const decision = value;
|
|
1119
1377
|
return {
|