@fabricorg/platform-host 8.5.0 → 8.7.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 +16 -0
- package/README.md +88 -12
- package/dist/index.cjs +178 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -4
- package/dist/index.d.ts +34 -4
- package/dist/index.js +179 -54
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { types } from "util";
|
|
2
|
-
import { AdapterCircuitBreakerRegistry, AdapterRegistry, FABRIC_GOVERNANCE_CONTRACT_VERSION, ReconciliationRequiredError, aggregatePolicyOutcomes, assertActionFinalization, assertAdapterCircuitBreakerConfig, assertAdapterOutcomeConsistency, assertAdapterStepDeadlines, assertGovernanceRuntimeEvidence, assertMutationGovernanceContext, canonicalJson, createFabricId, evaluatePolicyDefinitions, evaluateStateMachineTransition, executeWithAdapterRetry, isAdapterCircuitOpenError, isReconciliationRequiredError, isRetryableExecutionError } from "@fabricorg/platform";
|
|
2
|
+
import { AdapterCircuitBreakerRegistry, AdapterRegistry, FABRIC_GOVERNANCE_CONTRACT_VERSION, ReconciliationRequiredError, aggregatePolicyOutcomes, assertActionFinalization, assertAdapterCircuitBreakerConfig, assertAdapterOutcomeConsistency, assertAdapterStepDeadlines, assertGovernanceRuntimeEvidence, assertMutationGovernanceContext, canonicalJson, createFabricId, evaluatePolicyDefinitions, evaluateStateMachineTransition, executeWithAdapterRetry, isActorType, isAdapterCircuitOpenError, isReconciliationRequiredError, isRetryableExecutionError } from "@fabricorg/platform";
|
|
3
3
|
import { performance } from "perf_hooks";
|
|
4
4
|
import { assertAssemblyRuntimeCompatible } from "@fabricorg/assembly";
|
|
5
5
|
import { createHash } from "crypto";
|
|
@@ -296,6 +296,52 @@ async function appendLifecycleEvent(transaction, factory, invocation, kind) {
|
|
|
296
296
|
}, metadata);
|
|
297
297
|
}
|
|
298
298
|
//#endregion
|
|
299
|
+
//#region src/agent-proposal.ts
|
|
300
|
+
/** Validate without invoking getters or retaining caller-owned evidence. */
|
|
301
|
+
function snapshotAgentProposal(value) {
|
|
302
|
+
const invalid = () => {
|
|
303
|
+
throw new Error("Invalid agent proposal.");
|
|
304
|
+
};
|
|
305
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || types.isProxy(value)) return invalid();
|
|
306
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return invalid();
|
|
307
|
+
const allowed = [
|
|
308
|
+
"confidence",
|
|
309
|
+
"riskTier",
|
|
310
|
+
"agentSessionId",
|
|
311
|
+
"agentRunId",
|
|
312
|
+
"reason",
|
|
313
|
+
"evidence"
|
|
314
|
+
];
|
|
315
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
316
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
317
|
+
if (typeof key !== "string" || !allowed.includes(key) || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) return invalid();
|
|
318
|
+
}
|
|
319
|
+
if (value.confidence !== void 0 && (typeof value.confidence !== "number" || !Number.isFinite(value.confidence))) return invalid();
|
|
320
|
+
if (value.riskTier !== void 0 && ![
|
|
321
|
+
"low",
|
|
322
|
+
"medium",
|
|
323
|
+
"high"
|
|
324
|
+
].includes(value.riskTier)) return invalid();
|
|
325
|
+
for (const key of [
|
|
326
|
+
"agentSessionId",
|
|
327
|
+
"agentRunId",
|
|
328
|
+
"reason"
|
|
329
|
+
]) {
|
|
330
|
+
const field = value[key];
|
|
331
|
+
if (field !== void 0 && (typeof field !== "string" || field.length > (key === "reason" ? 4096 : 256) || key !== "reason" && field.trim().length === 0)) return invalid();
|
|
332
|
+
}
|
|
333
|
+
const evidence = snapshotApprovalDecision({
|
|
334
|
+
approved: true,
|
|
335
|
+
approverId: "proposal",
|
|
336
|
+
approverType: "agent",
|
|
337
|
+
...value.evidence !== void 0 ? { evidence: value.evidence } : {}
|
|
338
|
+
}).evidence;
|
|
339
|
+
return {
|
|
340
|
+
...value,
|
|
341
|
+
...evidence !== void 0 ? { evidence } : {}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
//#endregion
|
|
299
345
|
//#region src/command-identity.ts
|
|
300
346
|
const PARAMETER_DIGEST_ALGORITHM = "fabric-canonical-json-sha256-v1";
|
|
301
347
|
function digestParameters(parameters) {
|
|
@@ -1001,7 +1047,11 @@ function snapshotSubmission(input) {
|
|
|
1001
1047
|
const executionReason = scalar("executionReason");
|
|
1002
1048
|
const idempotencyKey = scalar("idempotencyKey");
|
|
1003
1049
|
const authorizationBindingId = scalar("authorizationBindingId");
|
|
1050
|
+
const proposal = input.agentProposal;
|
|
1051
|
+
if (proposal !== void 0 && actorType !== "agent") throw new Error("Agent proposal requires an agent actor.");
|
|
1052
|
+
const agentProposal = proposal === void 0 ? void 0 : snapshotAgentProposal(proposal);
|
|
1004
1053
|
const live = {
|
|
1054
|
+
...agentProposal !== void 0 ? { agentProposal } : {},
|
|
1005
1055
|
actionId,
|
|
1006
1056
|
tenantId,
|
|
1007
1057
|
spaceId,
|
|
@@ -1070,6 +1120,7 @@ function privateSubmission(snapshot) {
|
|
|
1070
1120
|
const provenance = provenanceRead ? copyProvenance(provenanceRead) : void 0;
|
|
1071
1121
|
const authorizationBinding = bindingRead ? copyBinding(bindingRead) : void 0;
|
|
1072
1122
|
return Object.freeze({
|
|
1123
|
+
...snapshot.agentProposal !== void 0 ? { agentProposal: snapshot.agentProposal } : {},
|
|
1073
1124
|
actionId: snapshot.actionId,
|
|
1074
1125
|
tenantId: snapshot.tenantId,
|
|
1075
1126
|
spaceId: snapshot.spaceId,
|
|
@@ -1166,6 +1217,7 @@ function parametersCopy(snapshot) {
|
|
|
1166
1217
|
*/
|
|
1167
1218
|
function liveSubmissionView(snapshot, input) {
|
|
1168
1219
|
const view = {
|
|
1220
|
+
...snapshot.agentProposal !== void 0 ? { agentProposal: snapshotAgentProposal(snapshot.agentProposal) } : {},
|
|
1169
1221
|
actionId: snapshot.actionId,
|
|
1170
1222
|
tenantId: snapshot.tenantId,
|
|
1171
1223
|
spaceId: snapshot.spaceId,
|
|
@@ -1207,6 +1259,7 @@ function submissionView(snapshot) {
|
|
|
1207
1259
|
const provenance = fresh(snapshot.provenance);
|
|
1208
1260
|
const authorizationBinding = fresh(snapshot.authorizationBinding);
|
|
1209
1261
|
return {
|
|
1262
|
+
...snapshot.agentProposal !== void 0 ? { agentProposal: snapshotAgentProposal(snapshot.agentProposal) } : {},
|
|
1210
1263
|
actionId: snapshot.actionId,
|
|
1211
1264
|
parameters: parametersCopy(snapshot),
|
|
1212
1265
|
tenantId: snapshot.tenantId,
|
|
@@ -1321,6 +1374,7 @@ const MAX_STAGED_INVOCATION_KEY_LENGTH = 128;
|
|
|
1321
1374
|
const MAX_STAGED_INVOCATION_BYTES = 65536;
|
|
1322
1375
|
const MAX_STAGED_INVOCATION_BATCH_BYTES = 262144;
|
|
1323
1376
|
const STAGED_INVOCATION_KEY_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
|
|
1377
|
+
const STAGED_INVOCATION_PRINCIPAL_PATTERN = /^[a-z][a-z0-9._:-]{0,127}$/;
|
|
1324
1378
|
/** Validate and normalize one immutable batch before its parent transaction commits. */
|
|
1325
1379
|
function prepareStagedInvocationBatch(input) {
|
|
1326
1380
|
if (!Array.isArray(input.requests)) throw new Error("stagedInvocations must be an array.");
|
|
@@ -1340,6 +1394,8 @@ function prepareStagedInvocationBatch(input) {
|
|
|
1340
1394
|
if (!(input.parentAction.stagesInvocations ?? []).includes(request.actionId)) throw new Error(`Action "${input.parentAction.actionId}" may not stage "${String(request.actionId)}".`);
|
|
1341
1395
|
const child = input.resolveAction(request.actionId);
|
|
1342
1396
|
if (!child) throw new Error(`Staged action "${String(request.actionId)}" is not registered.`);
|
|
1397
|
+
const principal = input.parentAction.stagedInvocationPrincipals?.[child.actionId];
|
|
1398
|
+
if (principal !== void 0 && !Object.hasOwn(input.stagedInvocationPrincipals ?? {}, principal)) throw new Error(`Staged invocation principal "${principal}" is not registered with this Platform Host.`);
|
|
1343
1399
|
const parameters = finalizationJson(request.parameters);
|
|
1344
1400
|
if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) throw new Error(`stagedInvocations[${index}] parameters must be a JSON object.`);
|
|
1345
1401
|
if (!child.schema.safeParse(parameters).success) throw new Error(`stagedInvocations[${index}] parameters do not match action "${child.actionId}".`);
|
|
@@ -1354,24 +1410,27 @@ function prepareStagedInvocationBatch(input) {
|
|
|
1354
1410
|
if (byteLength(canonicalJson({
|
|
1355
1411
|
key: request.key,
|
|
1356
1412
|
actionId: child.actionId,
|
|
1357
|
-
parameters: durableParameters
|
|
1413
|
+
parameters: durableParameters,
|
|
1414
|
+
...principal !== void 0 ? { principal } : {}
|
|
1358
1415
|
})) > 65536) throw new Error(`stagedInvocations[${index}] exceeds ${MAX_STAGED_INVOCATION_BYTES} bytes.`);
|
|
1359
1416
|
return {
|
|
1360
1417
|
key: request.key,
|
|
1361
1418
|
actionId: child.actionId,
|
|
1362
1419
|
expectedActionVersion: child.version,
|
|
1363
1420
|
parameters: durableParameters,
|
|
1364
|
-
parameterDigest: digestParameters(durableParameters)
|
|
1421
|
+
parameterDigest: digestParameters(durableParameters),
|
|
1422
|
+
...principal !== void 0 ? { principal } : {}
|
|
1365
1423
|
};
|
|
1366
1424
|
});
|
|
1367
1425
|
for (const [index, request] of normalized.entries()) if (normalized.findIndex((candidate) => candidate.key === request.key) !== index) throw new Error(`stagedInvocations contains duplicate key "${request.key}".`);
|
|
1368
1426
|
normalized.sort((left, right) => compareText(left.key, right.key) || compareText(left.actionId, right.actionId));
|
|
1369
|
-
const batchIdentity = normalized.map(({ key, actionId, expectedActionVersion, parameters, parameterDigest }) => ({
|
|
1427
|
+
const batchIdentity = normalized.map(({ key, actionId, expectedActionVersion, parameters, parameterDigest, principal }) => ({
|
|
1370
1428
|
key,
|
|
1371
1429
|
actionId,
|
|
1372
1430
|
expectedActionVersion,
|
|
1373
1431
|
parameters,
|
|
1374
|
-
parameterDigest
|
|
1432
|
+
parameterDigest,
|
|
1433
|
+
...principal !== void 0 ? { principal } : {}
|
|
1375
1434
|
}));
|
|
1376
1435
|
if (byteLength(canonicalJson(batchIdentity)) > 262144) throw new Error(`stagedInvocations batch exceeds ${MAX_STAGED_INVOCATION_BATCH_BYTES} bytes.`);
|
|
1377
1436
|
const batchDigest = digestParameters({
|
|
@@ -1406,7 +1465,8 @@ function stagedInvocationRequestIdentity(record) {
|
|
|
1406
1465
|
expectedActionVersion: record.expectedActionVersion,
|
|
1407
1466
|
parameters: record.parameters,
|
|
1408
1467
|
parameterDigest: record.parameterDigest,
|
|
1409
|
-
batchDigest: record.batchDigest
|
|
1468
|
+
batchDigest: record.batchDigest,
|
|
1469
|
+
...record.principal !== void 0 ? { principal: record.principal } : {}
|
|
1410
1470
|
});
|
|
1411
1471
|
}
|
|
1412
1472
|
function cloneStagedInvocationRecord(record) {
|
|
@@ -1473,6 +1533,20 @@ const STAGED_ERROR = Object.freeze({
|
|
|
1473
1533
|
lookup: "Staged child lookup failed."
|
|
1474
1534
|
});
|
|
1475
1535
|
function createGovernedActionHost(options) {
|
|
1536
|
+
const configuredStagedPrincipals = options.stagedInvocationPrincipals;
|
|
1537
|
+
if (configuredStagedPrincipals !== void 0 && (!configuredStagedPrincipals || typeof configuredStagedPrincipals !== "object" || Array.isArray(configuredStagedPrincipals))) throw new Error("stagedInvocationPrincipals must be an object.");
|
|
1538
|
+
const stagedInvocationPrincipals = Object.freeze(Object.fromEntries(Object.entries(configuredStagedPrincipals ?? {}).map(([name, candidate]) => {
|
|
1539
|
+
if (!STAGED_INVOCATION_PRINCIPAL_PATTERN.test(name)) throw new Error(`Invalid staged invocation principal name: ${name}`);
|
|
1540
|
+
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) throw new Error(`Staged invocation principal "${name}" must be an object.`);
|
|
1541
|
+
const actorId = candidate.actorId;
|
|
1542
|
+
const actorType = candidate.actorType;
|
|
1543
|
+
if (typeof actorId !== "string" || actorId.length === 0) throw new Error(`Staged invocation principal "${name}" actorId must be a non-empty string.`);
|
|
1544
|
+
if (typeof actorType !== "string" || !isActorType(actorType)) throw new Error(`Staged invocation principal "${name}" has invalid actorType.`);
|
|
1545
|
+
return [name, Object.freeze({
|
|
1546
|
+
actorId,
|
|
1547
|
+
actorType
|
|
1548
|
+
})];
|
|
1549
|
+
})));
|
|
1476
1550
|
if (options.approvalDefaultTtlMs !== void 0 && (!Number.isSafeInteger(options.approvalDefaultTtlMs) || options.approvalDefaultTtlMs <= 0)) throw new Error("approvalDefaultTtlMs must be a positive safe integer in milliseconds.");
|
|
1477
1551
|
if (options.approvalLifecycleEvents) {
|
|
1478
1552
|
const approvalStore = asApprovalStore(options.store);
|
|
@@ -1506,6 +1580,9 @@ function createGovernedActionHost(options) {
|
|
|
1506
1580
|
const configuredActionResolver = options.resolveAction ?? options.registry?.resolveAction;
|
|
1507
1581
|
if (!configuredActionResolver) throw new Error("Platform Host requires an explicit module registry or resolveAction function.");
|
|
1508
1582
|
const actionResolver = configuredActionResolver;
|
|
1583
|
+
if (options.registry?.orderedModules) {
|
|
1584
|
+
for (const module of options.registry.orderedModules) for (const action of module.actions ?? []) for (const principal of Object.values(action.stagedInvocationPrincipals ?? {})) if (!Object.hasOwn(stagedInvocationPrincipals, principal)) throw new Error(`Staged invocation principal "${principal}" referenced by action "${action.actionId}" is not registered with this Platform Host.`);
|
|
1585
|
+
}
|
|
1509
1586
|
const stagedStore = asStagedInvocationStore(options.store);
|
|
1510
1587
|
const declaresStaging = options.registry?.orderedModules?.some((module) => (module.actions ?? []).some((action) => (action.stagesInvocations?.length ?? 0) > 0)) ?? false;
|
|
1511
1588
|
if (declaresStaging && (!stagedStore || !asAtomicMutationStore(options.store))) throw new Error("Staged descendant invocations require a store with transactionalStagedInvocations and transactionWithEvents capabilities.");
|
|
@@ -1543,7 +1620,8 @@ function createGovernedActionHost(options) {
|
|
|
1543
1620
|
if (submission.authorizationBinding) validateAuthorizationBinding(submission, parameterDigest, action.execution?.authorityMoment, now());
|
|
1544
1621
|
return {
|
|
1545
1622
|
durableParameters,
|
|
1546
|
-
parameterDigest
|
|
1623
|
+
parameterDigest,
|
|
1624
|
+
agentProposal: submission.agentProposal === void 0 ? void 0 : snapshotAgentProposal(options.redactAgentProposal ? options.redactAgentProposal(submission.actionId, submission.agentProposal) : submission.agentProposal)
|
|
1547
1625
|
};
|
|
1548
1626
|
};
|
|
1549
1627
|
const early = declaresAdmission ? void 0 : prepare();
|
|
@@ -1558,9 +1636,10 @@ function createGovernedActionHost(options) {
|
|
|
1558
1636
|
};
|
|
1559
1637
|
assertGovernanceRuntimeEvidence(runtimeEvidence);
|
|
1560
1638
|
const admission = declaresAdmission ? await admitSubmission(action, submission) : void 0;
|
|
1561
|
-
const { durableParameters, parameterDigest } = early ?? prepare();
|
|
1639
|
+
const { durableParameters, parameterDigest, agentProposal } = early ?? prepare();
|
|
1562
1640
|
const invocationInput = {
|
|
1563
1641
|
id: actionInvocationId,
|
|
1642
|
+
...agentProposal !== void 0 ? { agentProposal } : {},
|
|
1564
1643
|
tenantId: submission.tenantId,
|
|
1565
1644
|
spaceId: submission.spaceId,
|
|
1566
1645
|
actionId: submission.actionId,
|
|
@@ -1614,6 +1693,7 @@ function createGovernedActionHost(options) {
|
|
|
1614
1693
|
});
|
|
1615
1694
|
if (durableInvocation.id !== actionInvocationId && submission.idempotencyKey) {
|
|
1616
1695
|
const conflict = idempotencyConflict(durableInvocation, {
|
|
1696
|
+
agentProposal,
|
|
1617
1697
|
actorId: submission.actorId,
|
|
1618
1698
|
authorizationBindingId: submission.authorizationBindingId,
|
|
1619
1699
|
actionVersion: action.version,
|
|
@@ -2335,21 +2415,24 @@ function createGovernedActionHost(options) {
|
|
|
2335
2415
|
}
|
|
2336
2416
|
if (invocation.actorType === "agent" && options.hitlEvaluator) {
|
|
2337
2417
|
const hitlParameters = isRecord(parsed.data) ? parsed.data : invocation.parameters;
|
|
2418
|
+
const proposal = invocation.agentProposal;
|
|
2338
2419
|
const approvalStore = asApprovalStore(options.store);
|
|
2339
2420
|
if (!approvalStore) return hostOperation(() => failExecution(invocation, "failed", "The configured HITL evaluator requires a store with approval persistence support."));
|
|
2340
2421
|
let evaluatedApprovalExpiresAt;
|
|
2341
2422
|
if (!invocation.hitlRoute) {
|
|
2342
2423
|
const decision = await options.hitlEvaluator({
|
|
2424
|
+
actionInvocationId: invocation.id,
|
|
2425
|
+
invocation,
|
|
2343
2426
|
actionId: action.actionId,
|
|
2344
2427
|
actorId: invocation.actorId,
|
|
2345
2428
|
actorType: invocation.actorType,
|
|
2346
2429
|
tenantId,
|
|
2347
2430
|
spaceId,
|
|
2348
2431
|
parameters: hitlParameters,
|
|
2349
|
-
...numberValue(hitlParameters.confidence) !== void 0 ? { confidence: numberValue(hitlParameters.confidence) } : {},
|
|
2350
|
-
...isRiskTier(hitlParameters.riskTier) ? { riskTier: hitlParameters.riskTier } : {},
|
|
2351
|
-
...stringValue(hitlParameters.agentSessionId) ? { agentSessionId: stringValue(hitlParameters.agentSessionId) } : {},
|
|
2352
|
-
...stringValue(hitlParameters.agentRunId) ? { agentRunId: stringValue(hitlParameters.agentRunId) } : {}
|
|
2432
|
+
...(proposal?.confidence ?? numberValue(hitlParameters.confidence)) !== void 0 ? { confidence: proposal?.confidence ?? numberValue(hitlParameters.confidence) } : {},
|
|
2433
|
+
...isRiskTier(proposal?.riskTier ?? hitlParameters.riskTier) ? { riskTier: proposal?.riskTier ?? hitlParameters.riskTier } : {},
|
|
2434
|
+
...proposal?.agentSessionId ?? stringValue(hitlParameters.agentSessionId) ? { agentSessionId: proposal?.agentSessionId ?? stringValue(hitlParameters.agentSessionId) } : {},
|
|
2435
|
+
...proposal?.agentRunId ?? stringValue(hitlParameters.agentRunId) ? { agentRunId: proposal?.agentRunId ?? stringValue(hitlParameters.agentRunId) } : {}
|
|
2353
2436
|
});
|
|
2354
2437
|
if (decision.approvalExpiresAt !== void 0 && !validApprovalDate(decision.approvalExpiresAt)) return hostOperation(() => failExecution(invocation, "failed", "approvalExpiresAt must be a valid Date."));
|
|
2355
2438
|
evaluatedApprovalExpiresAt = decision.approvalExpiresAt === void 0 ? void 0 : new Date(decision.approvalExpiresAt);
|
|
@@ -2643,6 +2726,7 @@ function createGovernedActionHost(options) {
|
|
|
2643
2726
|
parentAction: action,
|
|
2644
2727
|
requests: rawStagedInvocations,
|
|
2645
2728
|
resolveAction: actionResolver,
|
|
2729
|
+
stagedInvocationPrincipals,
|
|
2646
2730
|
redactParameters: options.redactActionParameters,
|
|
2647
2731
|
now: now()
|
|
2648
2732
|
});
|
|
@@ -3514,6 +3598,7 @@ function createGovernedActionHost(options) {
|
|
|
3514
3598
|
parentAction: action,
|
|
3515
3599
|
requests: stagedRequests,
|
|
3516
3600
|
resolveAction: actionResolver,
|
|
3601
|
+
stagedInvocationPrincipals,
|
|
3517
3602
|
redactParameters: options.redactActionParameters,
|
|
3518
3603
|
redactedKeys: handlerStagedInvocations.map((request) => request.key),
|
|
3519
3604
|
now: now()
|
|
@@ -4084,6 +4169,8 @@ function createGovernedActionHost(options) {
|
|
|
4084
4169
|
if (!parent || parent.status !== "completed") throw new Error("Staged parent is not completed.");
|
|
4085
4170
|
const child = actionResolver(record.actionId);
|
|
4086
4171
|
if (!child) throw new StagedDispatchRejectionError("Staged action is no longer registered.");
|
|
4172
|
+
const principal = record.principal !== void 0 && Object.hasOwn(stagedInvocationPrincipals, record.principal) ? stagedInvocationPrincipals[record.principal] : void 0;
|
|
4173
|
+
if (record.principal !== void 0 && !principal) throw new StagedDispatchRejectionError("Staged invocation principal is no longer registered.");
|
|
4087
4174
|
if (child.execution?.authorityMoment === "capture") throw new StagedDispatchRejectionError("Staged action now requires capture-time authority.");
|
|
4088
4175
|
if (!child.schema.safeParse(structuredClone(record.parameters)).success) throw new StagedDispatchRejectionError("Staged parameters no longer match the registered action.");
|
|
4089
4176
|
return submitActionInternal({
|
|
@@ -4091,8 +4178,8 @@ function createGovernedActionHost(options) {
|
|
|
4091
4178
|
parameters: structuredClone(record.parameters),
|
|
4092
4179
|
tenantId: parent.tenantId,
|
|
4093
4180
|
spaceId: parent.spaceId,
|
|
4094
|
-
actorId: parent.actorId,
|
|
4095
|
-
actorType: parent.actorType,
|
|
4181
|
+
actorId: principal?.actorId ?? parent.actorId,
|
|
4182
|
+
actorType: principal?.actorType ?? parent.actorType,
|
|
4096
4183
|
correlationId: parent.correlationId,
|
|
4097
4184
|
causationId: parent.id,
|
|
4098
4185
|
provenance: {
|
|
@@ -4106,7 +4193,8 @@ function createGovernedActionHost(options) {
|
|
|
4106
4193
|
id: record.id,
|
|
4107
4194
|
origin: {
|
|
4108
4195
|
parentInvocationId: parent.id,
|
|
4109
|
-
stageKey: record.key
|
|
4196
|
+
stageKey: record.key,
|
|
4197
|
+
...record.principal !== void 0 ? { principal: record.principal } : {}
|
|
4110
4198
|
},
|
|
4111
4199
|
workerId: lease.workerId,
|
|
4112
4200
|
leaseToken: lease.leaseToken,
|
|
@@ -4313,6 +4401,7 @@ function validateAuthorizationBinding(input, parameterDigest, authorityMoment, c
|
|
|
4313
4401
|
}
|
|
4314
4402
|
function idempotencyConflict(existing, incoming) {
|
|
4315
4403
|
const reasons = [];
|
|
4404
|
+
if (canonicalJson(existing.agentProposal ?? null) !== canonicalJson(incoming.agentProposal ?? null)) reasons.push("agent_proposal");
|
|
4316
4405
|
if ((existing.idempotencyActorId ?? existing.actorId) !== incoming.actorId) reasons.push("actor");
|
|
4317
4406
|
if ((existing.idempotencyAuthorizationBindingId ?? existing.authorizationBindingId) !== incoming.authorizationBindingId) reasons.push("authority_binding");
|
|
4318
4407
|
if (existing.actionVersion !== incoming.actionVersion) reasons.push("action_version");
|
|
@@ -4775,7 +4864,7 @@ var MemoryPlatformHostStore = class {
|
|
|
4775
4864
|
kind: "existing",
|
|
4776
4865
|
invocation: existing
|
|
4777
4866
|
};
|
|
4778
|
-
if (!this.stagedInvocations.find((record) => record.id === input.stageId && record.tenantId === invocation.tenantId && record.spaceId === invocation.spaceId && record.parentInvocationId === invocation.stagedOrigin.parentInvocationId && record.key === invocation.stagedOrigin.stageKey && record.actionId === invocation.actionId && record.status === "pending" && record.leaseOwner === input.workerId && record.leaseToken === input.leaseToken && record.leaseExpiresAt !== void 0 && record.leaseExpiresAt > now)) return { kind: "lease_lost" };
|
|
4867
|
+
if (!this.stagedInvocations.find((record) => record.id === input.stageId && record.tenantId === invocation.tenantId && record.spaceId === invocation.spaceId && record.parentInvocationId === invocation.stagedOrigin.parentInvocationId && record.key === invocation.stagedOrigin.stageKey && record.actionId === invocation.actionId && record.principal === invocation.stagedOrigin.principal && record.status === "pending" && record.leaseOwner === input.workerId && record.leaseToken === input.leaseToken && record.leaseExpiresAt !== void 0 && record.leaseExpiresAt > now)) return { kind: "lease_lost" };
|
|
4779
4868
|
const createdAt = /* @__PURE__ */ new Date();
|
|
4780
4869
|
const record = {
|
|
4781
4870
|
...invocation,
|
|
@@ -5618,6 +5707,21 @@ var PostgresPlatformHostStore = class PostgresPlatformHostStore {
|
|
|
5618
5707
|
ON fabric_platform.action_invocations (approval_expires_at, id)
|
|
5619
5708
|
WHERE status='waiting_for_approval' AND approval_expires_at IS NOT NULL;
|
|
5620
5709
|
`
|
|
5710
|
+
},
|
|
5711
|
+
{
|
|
5712
|
+
version: 13,
|
|
5713
|
+
name: "agent_proposal",
|
|
5714
|
+
sql: `ALTER TABLE fabric_platform.action_invocations ADD COLUMN IF NOT EXISTS agent_proposal jsonb;`
|
|
5715
|
+
},
|
|
5716
|
+
{
|
|
5717
|
+
version: 14,
|
|
5718
|
+
name: "staged_invocation_principals",
|
|
5719
|
+
sql: `
|
|
5720
|
+
ALTER TABLE fabric_platform.staged_invocations
|
|
5721
|
+
ADD COLUMN IF NOT EXISTS principal text;
|
|
5722
|
+
ALTER TABLE fabric_platform.action_invocations
|
|
5723
|
+
ADD COLUMN IF NOT EXISTS staged_origin_principal text;
|
|
5724
|
+
`
|
|
5621
5725
|
}
|
|
5622
5726
|
]);
|
|
5623
5727
|
}
|
|
@@ -5629,6 +5733,8 @@ var PostgresPlatformHostStore = class PostgresPlatformHostStore {
|
|
|
5629
5733
|
const admitted = input.admittedAt !== void 0;
|
|
5630
5734
|
const admittedColumn = admitted ? "admitted_at," : "";
|
|
5631
5735
|
const admittedValue = admitted ? "$25," : "";
|
|
5736
|
+
const proposalColumn = input.agentProposal !== void 0 ? "agent_proposal," : "";
|
|
5737
|
+
const proposalValue = input.agentProposal !== void 0 ? `$${admitted ? 26 : 25}::jsonb,` : "";
|
|
5632
5738
|
let result;
|
|
5633
5739
|
try {
|
|
5634
5740
|
result = await this.sql.query(`INSERT INTO fabric_platform.action_invocations
|
|
@@ -5636,8 +5742,8 @@ var PostgresPlatformHostStore = class PostgresPlatformHostStore {
|
|
|
5636
5742
|
parameters,result,correlation_id,causation_id,idempotency_key,
|
|
5637
5743
|
parameter_digest,parameter_digest_algorithm,idempotency_actor_id,
|
|
5638
5744
|
idempotency_authorization_binding_id,invocation_provenance,authorization_binding,execution_reason,
|
|
5639
|
-
authorization_binding_id,error,runtime_evidence,${admittedColumn}created_at,updated_at)
|
|
5640
|
-
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)
|
|
5745
|
+
authorization_binding_id,error,runtime_evidence,${admittedColumn}${proposalColumn}created_at,updated_at)
|
|
5746
|
+
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}${proposalValue}$24,$24)
|
|
5641
5747
|
ON CONFLICT (tenant_id,space_id,action_id,idempotency_key)
|
|
5642
5748
|
WHERE idempotency_key IS NOT NULL DO UPDATE SET id=fabric_platform.action_invocations.id
|
|
5643
5749
|
RETURNING *`, [
|
|
@@ -5665,9 +5771,11 @@ var PostgresPlatformHostStore = class PostgresPlatformHostStore {
|
|
|
5665
5771
|
input.error ?? null,
|
|
5666
5772
|
input.runtimeEvidence ? JSON.stringify(input.runtimeEvidence) : null,
|
|
5667
5773
|
now,
|
|
5668
|
-
...admitted ? [input.admittedAt] : []
|
|
5774
|
+
...admitted ? [input.admittedAt] : [],
|
|
5775
|
+
...input.agentProposal !== void 0 ? [JSON.stringify(input.agentProposal)] : []
|
|
5669
5776
|
]);
|
|
5670
5777
|
} catch (error) {
|
|
5778
|
+
if (input.agentProposal !== void 0 && error?.code === "42703") throw new Error("Agent proposals require PostgreSQL migration 13 (agent_proposal); run ensureSchema().", { cause: error });
|
|
5671
5779
|
if (admitted && error?.code === "42703") throw new Error("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.", { cause: error });
|
|
5672
5780
|
throw error;
|
|
5673
5781
|
}
|
|
@@ -5678,7 +5786,9 @@ var PostgresPlatformHostStore = class PostgresPlatformHostStore {
|
|
|
5678
5786
|
const current = input.now ?? /* @__PURE__ */ new Date();
|
|
5679
5787
|
const admitted = invocation.admittedAt !== void 0;
|
|
5680
5788
|
const admittedColumn = admitted ? ",admitted_at" : "";
|
|
5681
|
-
const admittedValue = admitted ? ",$
|
|
5789
|
+
const admittedValue = admitted ? ",$31" : "";
|
|
5790
|
+
const proposalColumn = invocation.agentProposal !== void 0 ? ",agent_proposal" : "";
|
|
5791
|
+
const proposalValue = invocation.agentProposal !== void 0 ? `,$${admitted ? 32 : 31}::jsonb` : "";
|
|
5682
5792
|
const values = [
|
|
5683
5793
|
invocation.id,
|
|
5684
5794
|
invocation.tenantId,
|
|
@@ -5705,21 +5815,24 @@ var PostgresPlatformHostStore = class PostgresPlatformHostStore {
|
|
|
5705
5815
|
invocation.runtimeEvidence ? JSON.stringify(invocation.runtimeEvidence) : null,
|
|
5706
5816
|
invocation.stagedOrigin.parentInvocationId,
|
|
5707
5817
|
invocation.stagedOrigin.stageKey,
|
|
5818
|
+
invocation.stagedOrigin.principal ?? null,
|
|
5708
5819
|
input.stageId,
|
|
5709
5820
|
input.workerId,
|
|
5710
5821
|
input.leaseToken,
|
|
5711
5822
|
current,
|
|
5712
|
-
...admitted ? [invocation.admittedAt] : []
|
|
5823
|
+
...admitted ? [invocation.admittedAt] : [],
|
|
5824
|
+
...invocation.agentProposal !== void 0 ? [JSON.stringify(invocation.agentProposal)] : []
|
|
5713
5825
|
];
|
|
5714
5826
|
let result;
|
|
5715
5827
|
try {
|
|
5716
5828
|
result = await this.sql.query(`WITH fence AS (
|
|
5717
5829
|
SELECT stage.id
|
|
5718
5830
|
FROM fabric_platform.staged_invocations AS stage
|
|
5719
|
-
WHERE stage.id=$
|
|
5831
|
+
WHERE stage.id=$27 AND stage.tenant_id=$2 AND stage.space_id=$3
|
|
5720
5832
|
AND stage.parent_invocation_id=$24 AND stage.stage_key=$25 AND stage.action_id=$4
|
|
5721
|
-
AND stage.
|
|
5722
|
-
AND stage.
|
|
5833
|
+
AND stage.principal IS NOT DISTINCT FROM $26
|
|
5834
|
+
AND stage.status='pending' AND stage.lease_owner=$28 AND stage.lease_token=$29
|
|
5835
|
+
AND stage.lease_expires_at IS NOT NULL AND stage.lease_expires_at > $30
|
|
5723
5836
|
FOR SHARE
|
|
5724
5837
|
)
|
|
5725
5838
|
INSERT INTO fabric_platform.action_invocations
|
|
@@ -5727,16 +5840,19 @@ var PostgresPlatformHostStore = class PostgresPlatformHostStore {
|
|
|
5727
5840
|
parameters,result,correlation_id,causation_id,idempotency_key,parameter_digest,
|
|
5728
5841
|
parameter_digest_algorithm,idempotency_actor_id,idempotency_authorization_binding_id,
|
|
5729
5842
|
invocation_provenance,authorization_binding,execution_reason,authorization_binding_id,error,
|
|
5730
|
-
runtime_evidence,staged_parent_invocation_id,staged_stage_key${admittedColumn},created_at,updated_at)
|
|
5843
|
+
runtime_evidence,staged_parent_invocation_id,staged_stage_key,staged_origin_principal${admittedColumn}${proposalColumn},created_at,updated_at)
|
|
5731
5844
|
SELECT $1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$10::jsonb,$11,$12,$13,$14,$15,$16,$17,
|
|
5732
|
-
$18::jsonb,$19::jsonb,$20,$21,$22,$23::jsonb,$24,$25${admittedValue},$
|
|
5845
|
+
$18::jsonb,$19::jsonb,$20,$21,$22,$23::jsonb,$24,$25,$26${admittedValue}${proposalValue},$30,$30
|
|
5733
5846
|
FROM fence
|
|
5734
5847
|
ON CONFLICT (tenant_id,space_id,staged_parent_invocation_id,staged_stage_key)
|
|
5735
5848
|
WHERE staged_parent_invocation_id IS NOT NULL AND staged_stage_key IS NOT NULL
|
|
5736
5849
|
DO UPDATE SET id=fabric_platform.action_invocations.id
|
|
5737
5850
|
RETURNING *`, values);
|
|
5738
5851
|
} catch (error) {
|
|
5739
|
-
|
|
5852
|
+
const databaseError = error;
|
|
5853
|
+
if (invocation.agentProposal !== void 0 && databaseError?.code === "42703" && typeof databaseError.message === "string" && /\bagent_proposal\b/.test(databaseError.message)) throw new Error("Agent proposals require PostgreSQL migration 13 (agent_proposal); run ensureSchema().", { cause: error });
|
|
5854
|
+
if (databaseError?.code === "42703") throw new Error("A staged child needs PostgreSQL migration 14 (staged_invocation_principals); run ensureSchema() before dispatching staged invocations.", { cause: error });
|
|
5855
|
+
if (databaseError?.code === "42P01") throw new Error("A staged child needs PostgreSQL migration 11 (staged_descendant_invocations); run ensureSchema() before dispatching staged invocations.", { cause: error });
|
|
5740
5856
|
throw error;
|
|
5741
5857
|
}
|
|
5742
5858
|
if (result.rows[0]) {
|
|
@@ -5778,33 +5894,39 @@ var PostgresPlatformHostStore = class PostgresPlatformHostStore {
|
|
|
5778
5894
|
}
|
|
5779
5895
|
for (const record of records) {
|
|
5780
5896
|
if (record.tenantId !== first.tenantId || record.spaceId !== first.spaceId || record.parentInvocationId !== first.parentInvocationId || record.batchDigest !== first.batchDigest) throw new Error(`Invalid staged invocation batch for parent ${first.parentInvocationId}.`);
|
|
5781
|
-
|
|
5782
|
-
|
|
5897
|
+
try {
|
|
5898
|
+
await this.sql.query(`INSERT INTO fabric_platform.staged_invocations
|
|
5899
|
+
(id,tenant_id,space_id,parent_invocation_id,parent_action_id,stage_key,action_id,principal,
|
|
5783
5900
|
expected_action_version,parameters,parameter_digest,batch_digest,status,child_invocation_id,
|
|
5784
5901
|
attempt_count,available_at,lease_owner,lease_expires_at,lease_token,last_error,created_at,updated_at)
|
|
5785
|
-
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9::jsonb,$
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5902
|
+
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10::jsonb,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22)`, [
|
|
5903
|
+
record.id,
|
|
5904
|
+
record.tenantId,
|
|
5905
|
+
record.spaceId,
|
|
5906
|
+
record.parentInvocationId,
|
|
5907
|
+
record.parentActionId,
|
|
5908
|
+
record.key,
|
|
5909
|
+
record.actionId,
|
|
5910
|
+
record.principal ?? null,
|
|
5911
|
+
record.expectedActionVersion,
|
|
5912
|
+
JSON.stringify(record.parameters),
|
|
5913
|
+
record.parameterDigest,
|
|
5914
|
+
record.batchDigest,
|
|
5915
|
+
record.status,
|
|
5916
|
+
record.childInvocationId ?? null,
|
|
5917
|
+
record.attemptCount,
|
|
5918
|
+
record.availableAt,
|
|
5919
|
+
record.leaseOwner ?? null,
|
|
5920
|
+
record.leaseExpiresAt ?? null,
|
|
5921
|
+
record.leaseToken,
|
|
5922
|
+
record.lastError ?? null,
|
|
5923
|
+
record.createdAt,
|
|
5924
|
+
record.updatedAt
|
|
5925
|
+
]);
|
|
5926
|
+
} catch (error) {
|
|
5927
|
+
if (error?.code === "42703") throw new Error("Staging an invocation needs PostgreSQL migration 14 (staged_invocation_principals); run ensureSchema() before staging invocations.", { cause: error });
|
|
5928
|
+
throw error;
|
|
5929
|
+
}
|
|
5808
5930
|
}
|
|
5809
5931
|
}
|
|
5810
5932
|
async getActionInvocation(id, tenantId, spaceId) {
|
|
@@ -6825,7 +6947,8 @@ function toActionRecord(row) {
|
|
|
6825
6947
|
...row.idempotency_authorization_binding_id ? { idempotencyAuthorizationBindingId: String(row.idempotency_authorization_binding_id) } : {},
|
|
6826
6948
|
...row.staged_parent_invocation_id && row.staged_stage_key ? { stagedOrigin: {
|
|
6827
6949
|
parentInvocationId: String(row.staged_parent_invocation_id),
|
|
6828
|
-
stageKey: String(row.staged_stage_key)
|
|
6950
|
+
stageKey: String(row.staged_stage_key),
|
|
6951
|
+
...row.staged_origin_principal ? { principal: String(row.staged_origin_principal) } : {}
|
|
6829
6952
|
} } : {},
|
|
6830
6953
|
...row.invocation_provenance ? { provenance: row.invocation_provenance } : {},
|
|
6831
6954
|
...row.authorization_binding ? { authorizationBinding: row.authorization_binding } : {},
|
|
@@ -6855,6 +6978,7 @@ function toActionRecord(row) {
|
|
|
6855
6978
|
...row.mutation_footprint ? { mutationFootprint: row.mutation_footprint } : {},
|
|
6856
6979
|
...row.execution_principal ? { executionPrincipal: row.execution_principal } : {},
|
|
6857
6980
|
...row.runtime_evidence ? { runtimeEvidence: row.runtime_evidence } : {},
|
|
6981
|
+
...row.agent_proposal != null ? { agentProposal: row.agent_proposal } : {},
|
|
6858
6982
|
...row.admitted_at ? { admittedAt: new Date(row.admitted_at) } : {},
|
|
6859
6983
|
...row.error ? { error: String(row.error) } : {},
|
|
6860
6984
|
createdAt: new Date(row.created_at),
|
|
@@ -6870,6 +6994,7 @@ function toStagedInvocationRecord(row) {
|
|
|
6870
6994
|
parentActionId: String(row.parent_action_id),
|
|
6871
6995
|
key: String(row.stage_key),
|
|
6872
6996
|
actionId: String(row.action_id),
|
|
6997
|
+
...row.principal ? { principal: String(row.principal) } : {},
|
|
6873
6998
|
expectedActionVersion: Number(row.expected_action_version),
|
|
6874
6999
|
parameters: row.parameters,
|
|
6875
7000
|
parameterDigest: String(row.parameter_digest),
|
|
@@ -8351,6 +8476,6 @@ async function runEventAppendChecks(subject) {
|
|
|
8351
8476
|
};
|
|
8352
8477
|
}
|
|
8353
8478
|
//#endregion
|
|
8354
|
-
export { APPROVAL_DECIDED_EVENT, APPROVAL_EXPIRED_EVENT, APPROVAL_REQUESTED_EVENT, ActionAdmissionError, ExternalCompletionError, IdempotencyConflictError, MAX_APPROVAL_EVIDENCE_BYTES, MAX_STAGED_INVOCATIONS, MAX_STAGED_INVOCATION_BATCH_BYTES, MAX_STAGED_INVOCATION_BYTES, MAX_STAGED_INVOCATION_KEY_LENGTH, MemoryPlatformHostStore, PARAMETER_DIGEST_ALGORITHM, PLATFORM_HOST_CONTRACT_VERSION, PLATFORM_HOST_HEALTH_CONTRACT_VERSION, PLATFORM_HOST_METRIC_NAMES, PostgresPlatformHostStore, STAGED_INVOCATION_KEY_PATTERN, SubmissionRefusalError, applyPostgresMigrations, asStagedInvocationStore, canonicalJson, cloneOutboxRecord, cloneStagedInvocationRecord, createDurableSagaParentLifecycle, createGovernedActionHost, createStoreBackedActionDispatcher, digestParameters, emitPlatformHostTelemetry, eventAppendChecks, getPlatformHostHealthSnapshot, outboxActorDisclosure, prepareStagedInvocationBatch, runEventAppendChecks, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, runTenantIsolationChecks, stagedInvocationRequestIdentity, tenantIsolationChecks, toEnterpriseEventEnvelope };
|
|
8479
|
+
export { APPROVAL_DECIDED_EVENT, APPROVAL_EXPIRED_EVENT, APPROVAL_REQUESTED_EVENT, ActionAdmissionError, ExternalCompletionError, IdempotencyConflictError, MAX_APPROVAL_EVIDENCE_BYTES, MAX_STAGED_INVOCATIONS, MAX_STAGED_INVOCATION_BATCH_BYTES, MAX_STAGED_INVOCATION_BYTES, MAX_STAGED_INVOCATION_KEY_LENGTH, MemoryPlatformHostStore, PARAMETER_DIGEST_ALGORITHM, PLATFORM_HOST_CONTRACT_VERSION, PLATFORM_HOST_HEALTH_CONTRACT_VERSION, PLATFORM_HOST_METRIC_NAMES, PostgresPlatformHostStore, STAGED_INVOCATION_KEY_PATTERN, STAGED_INVOCATION_PRINCIPAL_PATTERN, SubmissionRefusalError, applyPostgresMigrations, asStagedInvocationStore, canonicalJson, cloneOutboxRecord, cloneStagedInvocationRecord, createDurableSagaParentLifecycle, createGovernedActionHost, createStoreBackedActionDispatcher, digestParameters, emitPlatformHostTelemetry, eventAppendChecks, getPlatformHostHealthSnapshot, outboxActorDisclosure, prepareStagedInvocationBatch, runEventAppendChecks, runOutboxRelayCycle, runPlatformActionWorker, runPlatformActionWorkerCycle, runTenantIsolationChecks, stagedInvocationRequestIdentity, tenantIsolationChecks, toEnterpriseEventEnvelope };
|
|
8355
8480
|
|
|
8356
8481
|
//# sourceMappingURL=index.js.map
|