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