@fabricorg/platform-host 0.6.0 → 1.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/CHANGELOG.md +33 -0
- package/README.md +66 -2
- package/dist/index.cjs +138 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -3
- package/dist/index.d.ts +65 -3
- package/dist/index.js +138 -43
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @fabricorg/platform-host
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fdd3e69: Add provider-neutral external mutation footprints, delegated principals, enforceable obligations,
|
|
8
|
+
execution attestations, and reconciliation evidence with durable host-store support.
|
|
9
|
+
- 714ad06: Add enforceable Fabric-family compatibility controls and durable runtime contract-version evidence.
|
|
10
|
+
- 82aa9c8: Add parameter-aware execution-time authorization, opaque durable admission bindings, and an
|
|
11
|
+
application-bound PostgreSQL unit of work for atomic domain/event persistence and recoverable
|
|
12
|
+
after-adapter finalization.
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies [bc7d3ab]
|
|
17
|
+
- Updated dependencies [fdd3e69]
|
|
18
|
+
- Updated dependencies [714ad06]
|
|
19
|
+
- Updated dependencies [43c5770]
|
|
20
|
+
- Updated dependencies [986636b]
|
|
21
|
+
- @fabricorg/platform@0.10.0
|
|
22
|
+
|
|
23
|
+
## 0.7.0 — 2026-07-27
|
|
24
|
+
|
|
25
|
+
- Add parameter-aware execution authorization immediately before policies and mutation execution,
|
|
26
|
+
including approval resume and interrupted-work recovery.
|
|
27
|
+
- Persist an opaque authorization binding with each invocation so applications can revalidate the
|
|
28
|
+
exact admission without retaining credentials or caller proofs.
|
|
29
|
+
- Add an optional transaction-scoped mutation unit of work that commits domain writes and
|
|
30
|
+
before-adapter events atomically.
|
|
31
|
+
- Atomically append `after_adapters` completion events with invocation completion when the store
|
|
32
|
+
provides the unit-of-work capability; finalization failures remain recoverable and do not repeat
|
|
33
|
+
succeeded adapter checkpoints.
|
|
34
|
+
- Advance the durable Host lifecycle contract to version 2.
|
|
35
|
+
|
|
3
36
|
## 0.6.0
|
|
4
37
|
|
|
5
38
|
- Persist governance, Host, provider-bridge, package, and policy-ruleset generations with every new invocation.
|
package/README.md
CHANGED
|
@@ -3,9 +3,23 @@
|
|
|
3
3
|
The canonical host for the `@fabricorg/platform` mutation pipeline.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
pnpm add @fabricorg/platform@^0.9.0 @fabricorg/platform-host@^0.
|
|
6
|
+
pnpm add @fabricorg/platform@^0.9.0 @fabricorg/platform-host@^0.7.0
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
+
## AI agent integration boundary
|
|
10
|
+
|
|
11
|
+
Application and vertical owners install this package. Hermes, Fabric Harness, and other external agent
|
|
12
|
+
runtimes do not import Platform Host merely to call a deployed application. They connect to the
|
|
13
|
+
application's authenticated REST or MCP gateway, discover only the actions allowed by their
|
|
14
|
+
registration, and submit stable idempotent commands. The gateway derives tenant and actor identity and
|
|
15
|
+
calls `submitAction()`; an agent must never call handlers, adapters, workflow internals, or database
|
|
16
|
+
writes directly.
|
|
17
|
+
|
|
18
|
+
See the [Platform Host 0.7 agent integration
|
|
19
|
+
guide](https://platform.fabric.pro/docs/platform/reference/platform-host) for application wiring,
|
|
20
|
+
execution-time authorization, the PostgreSQL transaction binder, recovery semantics, and the external
|
|
21
|
+
gateway contract.
|
|
22
|
+
|
|
9
23
|
Vertical packages register `FabricModule` definitions. Host applications provide tenant authorization,
|
|
10
24
|
module entitlements, persistence, projections, and an optional durable dispatcher. The package owns the
|
|
11
25
|
ordering and lifecycle invariant:
|
|
@@ -36,6 +50,56 @@ adapter that already reached `succeeded`, and event, policy, and adapter writes
|
|
|
36
50
|
stale action that was not declared idempotent fails terminally for manual reconciliation instead of
|
|
37
51
|
silently rerunning unknown side effects.
|
|
38
52
|
|
|
53
|
+
## Execution authorization
|
|
54
|
+
|
|
55
|
+
Submission authorization proves that a caller may create an invocation. Applications that delegate
|
|
56
|
+
resource-scoped authority should also configure `authorizeExecution`. The Host calls it with the
|
|
57
|
+
schema-parsed durable parameters, canonical invocation, opaque `authorizationBindingId`, and an
|
|
58
|
+
`executionReason` of `initial`, `approval_resume`, or `recovery` immediately before policies and
|
|
59
|
+
mutation code run.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const host = createGovernedActionHost({
|
|
63
|
+
store,
|
|
64
|
+
authorization: {
|
|
65
|
+
checkEntitlement,
|
|
66
|
+
authorize: authorizeSubmission,
|
|
67
|
+
authorizeExecution: async ({ invocation, parameters, executionReason }) =>
|
|
68
|
+
admissionStore.authorize({
|
|
69
|
+
admissionId: invocation.authorizationBindingId,
|
|
70
|
+
parameters,
|
|
71
|
+
executionReason,
|
|
72
|
+
}),
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
When `authorizeExecution` is absent, the Host reuses `authorize` at the execution boundary. A
|
|
78
|
+
completed idempotent replay returns its existing result without creating or executing a new
|
|
79
|
+
mutation.
|
|
80
|
+
|
|
81
|
+
External gateways should persist a compact admission record bound to the tenant, registration, actor,
|
|
82
|
+
action, canonical parameter hash, resource scope, and expiry, then pass its opaque non-secret identifier
|
|
83
|
+
as `authorizationBindingId` to `submitAction()`. Credentials and signed principal proofs do not belong
|
|
84
|
+
in action parameters or workflow history.
|
|
85
|
+
|
|
86
|
+
## Atomic mutation unit of work
|
|
87
|
+
|
|
88
|
+
Production stores can implement `AtomicMutationPlatformHostStore.transactionWithEvents`. The
|
|
89
|
+
transaction-scoped `db`, event sequence, event append, event listing, and invocation update methods
|
|
90
|
+
must all use the same database transaction.
|
|
91
|
+
|
|
92
|
+
`PostgresPlatformHostStore` enables this capability when constructed with a
|
|
93
|
+
`PostgresPlatformHostTransactionProvider`. The application owns that narrow binder because only the
|
|
94
|
+
application knows how its `TDb` is rebound to the transaction's SQL client.
|
|
95
|
+
|
|
96
|
+
- `before_adapters`: handler domain writes and declared domain events commit or roll back together.
|
|
97
|
+
- `after_adapters`: completion events and invocation completion commit together. A finalization
|
|
98
|
+
failure leaves the invocation recoverable; succeeded adapter checkpoints are not repeated.
|
|
99
|
+
|
|
100
|
+
Stores without this additive capability retain the legacy boundary for compatibility and must not
|
|
101
|
+
claim atomic domain-write/event persistence.
|
|
102
|
+
|
|
39
103
|
## Agent HITL
|
|
40
104
|
|
|
41
105
|
Hosts can inject a vertical-owned `hitlEvaluator`. It runs only for `actorType: "agent"`, after schema
|
|
@@ -107,7 +171,7 @@ explain which contract and provider adapter governed a mutation after dependenci
|
|
|
107
171
|
const host = createGovernedActionHost({
|
|
108
172
|
// ...
|
|
109
173
|
runtimeEvidence: {
|
|
110
|
-
hostPackageVersion: "0.
|
|
174
|
+
hostPackageVersion: "0.7.0",
|
|
111
175
|
policyRulesetVersion: "gtm-rules.v8",
|
|
112
176
|
providerBridge: { name: "@fabric-harness/databricks", version: "1" },
|
|
113
177
|
},
|
package/dist/index.cjs
CHANGED
|
@@ -5,13 +5,16 @@ var platform = require('@fabricorg/platform');
|
|
|
5
5
|
// src/host.ts
|
|
6
6
|
|
|
7
7
|
// src/types.ts
|
|
8
|
-
var PLATFORM_HOST_CONTRACT_VERSION =
|
|
8
|
+
var PLATFORM_HOST_CONTRACT_VERSION = 2;
|
|
9
9
|
|
|
10
10
|
// src/host.ts
|
|
11
11
|
var DEFAULT_EXTRACT_EVENTS = (data) => {
|
|
12
12
|
const value = data._events;
|
|
13
13
|
return Array.isArray(value) ? value : [];
|
|
14
14
|
};
|
|
15
|
+
var RecoverableFinalizationError = class extends Error {
|
|
16
|
+
name = "RecoverableFinalizationError";
|
|
17
|
+
};
|
|
15
18
|
function createGovernedActionHost(options) {
|
|
16
19
|
const adapters = new platform.AdapterRegistry();
|
|
17
20
|
for (const adapter of options.adapters ?? []) adapters.register(adapter);
|
|
@@ -52,7 +55,8 @@ function createGovernedActionHost(options) {
|
|
|
52
55
|
runtimeEvidence,
|
|
53
56
|
correlationId,
|
|
54
57
|
...input.causationId ? { causationId: input.causationId } : {},
|
|
55
|
-
...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {}
|
|
58
|
+
...input.idempotencyKey ? { idempotencyKey: input.idempotencyKey } : {},
|
|
59
|
+
...input.authorizationBindingId ? { authorizationBindingId: input.authorizationBindingId } : {}
|
|
56
60
|
});
|
|
57
61
|
const durableWorkflowId = `action-invocation-${durableInvocation.id}`;
|
|
58
62
|
if (durableInvocation.id !== actionInvocationId && isTerminal(durableInvocation.status)) {
|
|
@@ -100,7 +104,7 @@ function createGovernedActionHost(options) {
|
|
|
100
104
|
);
|
|
101
105
|
return { ...executed, workflowId: durableWorkflowId };
|
|
102
106
|
}
|
|
103
|
-
async function executeInvocation(actionInvocationId, tenantId, spaceId, executionOptions = {}) {
|
|
107
|
+
async function executeInvocation(actionInvocationId, tenantId, spaceId, executionOptions = {}, executionReasonOverride) {
|
|
104
108
|
const loadedInvocation = await options.store.getActionInvocation(
|
|
105
109
|
actionInvocationId,
|
|
106
110
|
tenantId,
|
|
@@ -109,6 +113,7 @@ function createGovernedActionHost(options) {
|
|
|
109
113
|
if (!loadedInvocation) {
|
|
110
114
|
throw new Error(`ActionInvocation not found: ${actionInvocationId}`);
|
|
111
115
|
}
|
|
116
|
+
const resumingRunningInvocation = loadedInvocation.status === "running";
|
|
112
117
|
let invocation = loadedInvocation;
|
|
113
118
|
if (isTerminal(invocation.status) || invocation.status === "waiting_for_approval") {
|
|
114
119
|
return actionResult(invocation);
|
|
@@ -210,6 +215,28 @@ function createGovernedActionHost(options) {
|
|
|
210
215
|
}
|
|
211
216
|
}
|
|
212
217
|
const authorizationInput = toAuthorizationInput(action, invocation);
|
|
218
|
+
const executionReason = executionReasonOverride ?? (invocation.attemptCount > 1 || resumingRunningInvocation && invocation.attemptCount === 0 ? "recovery" : "initial");
|
|
219
|
+
if (!await options.authorization.checkEntitlement(authorizationInput)) {
|
|
220
|
+
return fail(
|
|
221
|
+
invocation,
|
|
222
|
+
"failed",
|
|
223
|
+
`Module "${action.namespace}" is no longer enabled for tenant ${tenantId}`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
const executionAuthorized = options.authorization.authorizeExecution ? await options.authorization.authorizeExecution({
|
|
227
|
+
...authorizationInput,
|
|
228
|
+
actionInvocationId,
|
|
229
|
+
parameters: parsed.data,
|
|
230
|
+
invocation,
|
|
231
|
+
executionReason
|
|
232
|
+
}) : await options.authorization.authorize(authorizationInput);
|
|
233
|
+
if (!executionAuthorized) {
|
|
234
|
+
return fail(
|
|
235
|
+
invocation,
|
|
236
|
+
"failed",
|
|
237
|
+
`Actor ${invocation.actorId} is not authorized to execute action ${action.actionId}`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
213
240
|
const definitions = options.resolvePolicies ? await options.resolvePolicies({
|
|
214
241
|
...authorizationInput,
|
|
215
242
|
declaredPolicyIds: action.policies ?? []
|
|
@@ -294,8 +321,8 @@ function createGovernedActionHost(options) {
|
|
|
294
321
|
let data;
|
|
295
322
|
let domainEvents = [];
|
|
296
323
|
try {
|
|
297
|
-
const
|
|
298
|
-
|
|
324
|
+
const runHandler = async (db, transaction) => {
|
|
325
|
+
const handlerResult = action.handler ? await action.handler(
|
|
299
326
|
{
|
|
300
327
|
actionInvocationId,
|
|
301
328
|
tenantId,
|
|
@@ -308,28 +335,39 @@ function createGovernedActionHost(options) {
|
|
|
308
335
|
services: options.services
|
|
309
336
|
},
|
|
310
337
|
parsed.data
|
|
311
|
-
) :
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
(event) => !action.emitsEvents.includes(event.eventType)
|
|
320
|
-
);
|
|
321
|
-
if (undeclaredEvent) {
|
|
322
|
-
return fail(
|
|
323
|
-
invocation,
|
|
324
|
-
"failed",
|
|
325
|
-
`${action.actionId} emitted undeclared event type ${undeclaredEvent.eventType}`
|
|
338
|
+
) : { success: true, data: {} };
|
|
339
|
+
if (!handlerResult.success) {
|
|
340
|
+
throw new Error(handlerResult.error ?? "Action handler failed");
|
|
341
|
+
}
|
|
342
|
+
const handlerData = handlerResult.data ?? {};
|
|
343
|
+
const events = extractEvents(handlerData);
|
|
344
|
+
const undeclaredEvent = events.find(
|
|
345
|
+
(event) => !action.emitsEvents.includes(event.eventType)
|
|
326
346
|
);
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
347
|
+
if (undeclaredEvent) {
|
|
348
|
+
throw new Error(
|
|
349
|
+
`${action.actionId} emitted undeclared event type ${undeclaredEvent.eventType}`
|
|
350
|
+
);
|
|
331
351
|
}
|
|
332
|
-
|
|
352
|
+
if (action.eventPhase !== "after_adapters") {
|
|
353
|
+
for (const [index, event] of events.entries()) {
|
|
354
|
+
await appendEvent(
|
|
355
|
+
invocation,
|
|
356
|
+
event,
|
|
357
|
+
`domain:${index}`,
|
|
358
|
+
action.version,
|
|
359
|
+
transaction
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return { data: handlerData, domainEvents: events };
|
|
364
|
+
};
|
|
365
|
+
const atomicStore = asAtomicMutationStore(options.store);
|
|
366
|
+
const executed = atomicStore ? await atomicStore.transactionWithEvents(
|
|
367
|
+
(transaction) => runHandler(transaction.db, transaction)
|
|
368
|
+
) : await options.store.transaction((db) => runHandler(db));
|
|
369
|
+
data = executed.data;
|
|
370
|
+
domainEvents = executed.domainEvents;
|
|
333
371
|
} catch (error) {
|
|
334
372
|
return fail(invocation, "failed", errorMessage(error));
|
|
335
373
|
}
|
|
@@ -460,11 +498,6 @@ function createGovernedActionHost(options) {
|
|
|
460
498
|
return fail(invocation, "failed", message);
|
|
461
499
|
}
|
|
462
500
|
}
|
|
463
|
-
if (action.eventPhase === "after_adapters") {
|
|
464
|
-
for (const [index, event] of domainEvents.entries()) {
|
|
465
|
-
await appendEvent(invocation, event, `domain:${index}`, action.version);
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
501
|
const governanceStore = asGovernanceStore(options.store);
|
|
469
502
|
if (governanceStore && options.resolvePolicyObligations) {
|
|
470
503
|
const obligations = await governanceStore.listPolicyObligations(actionInvocationId, tenantId, spaceId);
|
|
@@ -474,10 +507,43 @@ function createGovernedActionHost(options) {
|
|
|
474
507
|
}
|
|
475
508
|
}
|
|
476
509
|
const result = withoutPrivateHostFields(data, eventResultFields);
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
510
|
+
try {
|
|
511
|
+
const atomicStore = asAtomicMutationStore(options.store);
|
|
512
|
+
if (action.eventPhase === "after_adapters" && atomicStore) {
|
|
513
|
+
await atomicStore.transactionWithEvents(async (transaction) => {
|
|
514
|
+
for (const [index, event] of domainEvents.entries()) {
|
|
515
|
+
await appendEvent(
|
|
516
|
+
invocation,
|
|
517
|
+
event,
|
|
518
|
+
`domain:${index}`,
|
|
519
|
+
action.version,
|
|
520
|
+
transaction
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
await transaction.updateActionInvocation(
|
|
524
|
+
actionInvocationId,
|
|
525
|
+
tenantId,
|
|
526
|
+
spaceId,
|
|
527
|
+
{ status: "completed", result }
|
|
528
|
+
);
|
|
529
|
+
});
|
|
530
|
+
} else {
|
|
531
|
+
if (action.eventPhase === "after_adapters") {
|
|
532
|
+
for (const [index, event] of domainEvents.entries()) {
|
|
533
|
+
await appendEvent(invocation, event, `domain:${index}`, action.version);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
await options.store.updateActionInvocation(actionInvocationId, tenantId, spaceId, {
|
|
537
|
+
status: "completed",
|
|
538
|
+
result
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
} catch (error) {
|
|
542
|
+
if (action.eventPhase === "after_adapters") {
|
|
543
|
+
throw new RecoverableFinalizationError(errorMessage(error));
|
|
544
|
+
}
|
|
545
|
+
throw error;
|
|
546
|
+
}
|
|
481
547
|
return {
|
|
482
548
|
actionInvocationId,
|
|
483
549
|
status: "completed",
|
|
@@ -486,6 +552,7 @@ function createGovernedActionHost(options) {
|
|
|
486
552
|
...invocation.hitlRiskTier ? { hitlRiskTier: invocation.hitlRiskTier } : {}
|
|
487
553
|
};
|
|
488
554
|
} catch (error) {
|
|
555
|
+
if (error instanceof RecoverableFinalizationError) throw error;
|
|
489
556
|
return fail(invocation, "failed", errorMessage(error));
|
|
490
557
|
}
|
|
491
558
|
}
|
|
@@ -550,7 +617,13 @@ function createGovernedActionHost(options) {
|
|
|
550
617
|
return actionResult(transitioned);
|
|
551
618
|
}
|
|
552
619
|
if (!decision.approved) return actionResult(transitioned);
|
|
553
|
-
return executeInvocation(
|
|
620
|
+
return executeInvocation(
|
|
621
|
+
actionInvocationId,
|
|
622
|
+
tenantId,
|
|
623
|
+
spaceId,
|
|
624
|
+
{ leaseOwner },
|
|
625
|
+
"approval_resume"
|
|
626
|
+
);
|
|
554
627
|
}
|
|
555
628
|
async function recordExecutionAttestation(actionInvocationId, tenantId, spaceId, attestation, adapterInvocationId) {
|
|
556
629
|
const governanceStore = asGovernanceStore(options.store);
|
|
@@ -584,7 +657,7 @@ function createGovernedActionHost(options) {
|
|
|
584
657
|
spaceId
|
|
585
658
|
});
|
|
586
659
|
}
|
|
587
|
-
async function appendEvent(invocation, event, deduplicationKey, defaultEventSchemaVersion = 1) {
|
|
660
|
+
async function appendEvent(invocation, event, deduplicationKey, defaultEventSchemaVersion = 1, transaction) {
|
|
588
661
|
const timestamp = now();
|
|
589
662
|
const envelope = {
|
|
590
663
|
id: lifecycleId("evt", invocation.id, deduplicationKey),
|
|
@@ -598,7 +671,7 @@ function createGovernedActionHost(options) {
|
|
|
598
671
|
actorType: invocation.actorType,
|
|
599
672
|
actionInvocationId: invocation.id,
|
|
600
673
|
payload: event.payload,
|
|
601
|
-
sequence: await options.store.nextEventSequence(
|
|
674
|
+
sequence: await (transaction ?? options.store).nextEventSequence(
|
|
602
675
|
invocation.tenantId,
|
|
603
676
|
invocation.spaceId
|
|
604
677
|
),
|
|
@@ -607,7 +680,7 @@ function createGovernedActionHost(options) {
|
|
|
607
680
|
correlationId: invocation.correlationId,
|
|
608
681
|
...invocation.causationId ? { causationId: invocation.causationId } : {}
|
|
609
682
|
};
|
|
610
|
-
await options.store.appendEvent(envelope);
|
|
683
|
+
await (transaction ?? options.store).appendEvent(envelope);
|
|
611
684
|
}
|
|
612
685
|
async function fail(invocation, status, error) {
|
|
613
686
|
await options.store.updateActionInvocation(
|
|
@@ -640,6 +713,10 @@ function asApprovalStore(store) {
|
|
|
640
713
|
const candidate = store;
|
|
641
714
|
return typeof candidate.recordHitlDecision === "function" && typeof candidate.beginApprovalDecision === "function" ? store : void 0;
|
|
642
715
|
}
|
|
716
|
+
function asAtomicMutationStore(store) {
|
|
717
|
+
const candidate = store;
|
|
718
|
+
return typeof candidate.transactionWithEvents === "function" ? store : void 0;
|
|
719
|
+
}
|
|
643
720
|
function asGovernanceStore(store) {
|
|
644
721
|
const candidate = store;
|
|
645
722
|
return typeof candidate.recordMutationGovernance === "function" && typeof candidate.appendPolicyObligations === "function" && typeof candidate.appendExecutionAttestation === "function" ? store : void 0;
|
|
@@ -866,13 +943,26 @@ var MemoryPlatformHostStore = class {
|
|
|
866
943
|
};
|
|
867
944
|
|
|
868
945
|
// src/postgres-store.ts
|
|
869
|
-
var PostgresPlatformHostStore = class {
|
|
870
|
-
constructor(db, sql) {
|
|
946
|
+
var PostgresPlatformHostStore = class _PostgresPlatformHostStore {
|
|
947
|
+
constructor(db, sql, transactionProvider) {
|
|
871
948
|
this.db = db;
|
|
872
949
|
this.sql = sql;
|
|
950
|
+
if (transactionProvider) {
|
|
951
|
+
this.transactionWithEvents = async (run) => transactionProvider.run(async ({ db: db2, sql: sql2 }) => {
|
|
952
|
+
const scoped = new _PostgresPlatformHostStore(db2, sql2);
|
|
953
|
+
return run({
|
|
954
|
+
db: db2,
|
|
955
|
+
appendEvent: (event) => scoped.appendEvent(event),
|
|
956
|
+
nextEventSequence: (tenantId, spaceId) => scoped.nextEventSequence(tenantId, spaceId),
|
|
957
|
+
listEvents: (tenantId, spaceId) => scoped.listEvents(tenantId, spaceId),
|
|
958
|
+
updateActionInvocation: (id, tenantId, spaceId, patch) => scoped.updateActionInvocation(id, tenantId, spaceId, patch)
|
|
959
|
+
});
|
|
960
|
+
});
|
|
961
|
+
}
|
|
873
962
|
}
|
|
874
963
|
db;
|
|
875
964
|
sql;
|
|
965
|
+
transactionWithEvents;
|
|
876
966
|
async ensureSchema() {
|
|
877
967
|
await this.sql.query(`
|
|
878
968
|
CREATE SCHEMA IF NOT EXISTS fabric_platform;
|
|
@@ -881,7 +971,8 @@ var PostgresPlatformHostStore = class {
|
|
|
881
971
|
action_id text NOT NULL, action_version integer NOT NULL,
|
|
882
972
|
actor_id text NOT NULL, actor_type text NOT NULL, status text NOT NULL,
|
|
883
973
|
parameters jsonb NOT NULL, result jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
884
|
-
correlation_id text NOT NULL, causation_id text, idempotency_key text,
|
|
974
|
+
correlation_id text NOT NULL, causation_id text, idempotency_key text,
|
|
975
|
+
authorization_binding_id text, error text,
|
|
885
976
|
attempt_count integer NOT NULL DEFAULT 0, lease_owner text,
|
|
886
977
|
lease_expires_at timestamptz, hitl_route text, hitl_risk_tier text,
|
|
887
978
|
hitl_reason text, hitl_policy_version text, approval_decision jsonb,
|
|
@@ -890,6 +981,7 @@ var PostgresPlatformHostStore = class {
|
|
|
890
981
|
);
|
|
891
982
|
ALTER TABLE fabric_platform.action_invocations
|
|
892
983
|
ADD COLUMN IF NOT EXISTS idempotency_key text,
|
|
984
|
+
ADD COLUMN IF NOT EXISTS authorization_binding_id text,
|
|
893
985
|
ADD COLUMN IF NOT EXISTS attempt_count integer NOT NULL DEFAULT 0,
|
|
894
986
|
ADD COLUMN IF NOT EXISTS lease_owner text,
|
|
895
987
|
ADD COLUMN IF NOT EXISTS lease_expires_at timestamptz,
|
|
@@ -964,8 +1056,9 @@ var PostgresPlatformHostStore = class {
|
|
|
964
1056
|
const result = await this.sql.query(
|
|
965
1057
|
`INSERT INTO fabric_platform.action_invocations
|
|
966
1058
|
(id,tenant_id,space_id,action_id,action_version,actor_id,actor_type,status,
|
|
967
|
-
parameters,result,correlation_id,causation_id,idempotency_key,
|
|
968
|
-
|
|
1059
|
+
parameters,result,correlation_id,causation_id,idempotency_key,
|
|
1060
|
+
authorization_binding_id,error,runtime_evidence,created_at,updated_at)
|
|
1061
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$10::jsonb,$11,$12,$13,$14,$15,$16::jsonb,$17,$17)
|
|
969
1062
|
ON CONFLICT (tenant_id,space_id,action_id,idempotency_key)
|
|
970
1063
|
WHERE idempotency_key IS NOT NULL DO UPDATE SET id=fabric_platform.action_invocations.id
|
|
971
1064
|
RETURNING *`,
|
|
@@ -983,6 +1076,7 @@ var PostgresPlatformHostStore = class {
|
|
|
983
1076
|
input.correlationId,
|
|
984
1077
|
input.causationId ?? null,
|
|
985
1078
|
input.idempotencyKey ?? null,
|
|
1079
|
+
input.authorizationBindingId ?? null,
|
|
986
1080
|
input.error ?? null,
|
|
987
1081
|
input.runtimeEvidence ? JSON.stringify(input.runtimeEvidence) : null,
|
|
988
1082
|
now
|
|
@@ -1346,6 +1440,7 @@ function toActionRecord(row) {
|
|
|
1346
1440
|
correlationId: String(row.correlation_id),
|
|
1347
1441
|
...row.causation_id ? { causationId: String(row.causation_id) } : {},
|
|
1348
1442
|
...row.idempotency_key ? { idempotencyKey: String(row.idempotency_key) } : {},
|
|
1443
|
+
...row.authorization_binding_id ? { authorizationBindingId: String(row.authorization_binding_id) } : {},
|
|
1349
1444
|
attemptCount: Number(row.attempt_count ?? 0),
|
|
1350
1445
|
...row.lease_owner ? { leaseOwner: String(row.lease_owner) } : {},
|
|
1351
1446
|
...row.lease_expires_at ? { leaseExpiresAt: new Date(row.lease_expires_at) } : {},
|