@synapsor/runner 1.6.2 → 1.6.3

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +77 -2
  2. package/README.md +33 -19
  3. package/SECURITY.md +10 -2
  4. package/THREAT_MODEL.md +27 -4
  5. package/dist/authoring.mjs +53 -3
  6. package/dist/cli.d.ts +19 -2
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/local-ui.d.ts +85 -1
  9. package/dist/local-ui.d.ts.map +1 -1
  10. package/dist/runner.mjs +36107 -22949
  11. package/dist/runtime.mjs +3581 -237
  12. package/dist/shadow.mjs +2423 -65
  13. package/docs/README.md +22 -2
  14. package/docs/agent-guided-setup.md +239 -0
  15. package/docs/approval-roles-and-operator-identity.md +353 -0
  16. package/docs/auto-boundary-and-scoped-explore.md +32 -6
  17. package/docs/capability-authoring.md +19 -0
  18. package/docs/current-scope.md +20 -1
  19. package/docs/dsl-json-parity.md +75 -0
  20. package/docs/dsl-reference.md +10 -0
  21. package/docs/fresh-developer-usability.md +155 -80
  22. package/docs/getting-started-own-database.md +5 -4
  23. package/docs/guarded-crud-writeback.md +19 -0
  24. package/docs/guided-onboarding.md +331 -0
  25. package/docs/human-attention-notifications.md +367 -0
  26. package/docs/limitations.md +19 -3
  27. package/docs/local-mode.md +16 -2
  28. package/docs/migrating-to-synapsor-spec.md +38 -0
  29. package/docs/production.md +39 -0
  30. package/docs/release-notes.md +66 -2
  31. package/docs/runner-config-reference.md +132 -0
  32. package/docs/running-a-runner-fleet.md +16 -1
  33. package/docs/security-boundary.md +24 -0
  34. package/docs/store-lifecycle.md +20 -1
  35. package/docs/supervised-automatic-apply.md +267 -0
  36. package/docs/troubleshooting-first-run.md +35 -0
  37. package/examples/fitflow-guided-onboarding/README.md +43 -0
  38. package/examples/fitflow-guided-onboarding/app/page.tsx +8 -0
  39. package/examples/fitflow-guided-onboarding/docker-compose.yml +16 -0
  40. package/examples/fitflow-guided-onboarding/package.json +18 -0
  41. package/examples/fitflow-guided-onboarding/prisma/schema.prisma +98 -0
  42. package/examples/fitflow-guided-onboarding/seed/postgres.sql +401 -0
  43. package/examples/operator-oidc/issuer.mjs +188 -0
  44. package/examples/support-plan-credit/README.md +6 -2
  45. package/package.json +5 -3
  46. package/schemas/schema-candidate-review.schema.json +42 -0
  47. package/schemas/synapsor.runner.schema.json +227 -4
package/dist/runtime.mjs CHANGED
@@ -158,7 +158,7 @@ var SUBJECT_KEYS = /* @__PURE__ */ new Set(["resource", "schema", "table", "prim
158
158
  var ARG_KEYS = /* @__PURE__ */ new Set(["type", "description", "required", "max_length", "minimum", "maximum", "enum", "max_items", "fields"]);
159
159
  var LOOKUP_KEYS = /* @__PURE__ */ new Set(["id_from_arg"]);
160
160
  var EVIDENCE_KEYS = /* @__PURE__ */ new Set(["required", "sources", "query_audit", "handle_prefix"]);
161
- var PROPOSAL_KEYS = /* @__PURE__ */ new Set(["action", "operation", "allowed_fields", "patch", "numeric_bounds", "transition_guards", "reversibility", "conflict_guard", "approval", "writeback"]);
161
+ var PROPOSAL_KEYS = /* @__PURE__ */ new Set(["action", "operation", "allowed_fields", "patch", "numeric_bounds", "transition_guards", "reversibility", "conflict_guard", "approval", "execution", "writeback"]);
162
162
  var OPERATION_KEYS = /* @__PURE__ */ new Set(["kind", "cardinality", "selection", "max_rows", "aggregate_bounds", "batch", "deduplication", "version_advance"]);
163
163
  var SELECTION_KEYS = /* @__PURE__ */ new Set(["all"]);
164
164
  var PREDICATE_TERM_KEYS = /* @__PURE__ */ new Set(["column", "operator", "value"]);
@@ -173,6 +173,7 @@ var NUMERIC_BOUND_KEYS = /* @__PURE__ */ new Set(["minimum", "maximum"]);
173
173
  var TRANSITION_GUARD_KEYS = /* @__PURE__ */ new Set(["from_column", "allowed"]);
174
174
  var CONFLICT_GUARD_KEYS = /* @__PURE__ */ new Set(["column", "weak_guard_ack"]);
175
175
  var APPROVAL_KEYS = /* @__PURE__ */ new Set(["mode", "required_role", "required_approvals", "policy"]);
176
+ var EXECUTION_KEYS = /* @__PURE__ */ new Set(["supervised_worker"]);
176
177
  var WRITEBACK_KEYS = /* @__PURE__ */ new Set(["mode", "executor", "idempotency_key"]);
177
178
  var REVERSIBILITY_KEYS = /* @__PURE__ */ new Set(["mode"]);
178
179
  var AGGREGATE_READ_KEYS = /* @__PURE__ */ new Set(["function", "count_mode", "column", "selection", "minimum_group_size"]);
@@ -394,8 +395,8 @@ function validateContexts(value, errors, warnings) {
394
395
  }
395
396
  function validateCapabilities(value, contextNames, resourceNames, errors, warnings) {
396
397
  const names = /* @__PURE__ */ new Set();
397
- if (!Array.isArray(value) || value.length === 0) {
398
- errors.push({ path: "$.capabilities", code: "CAPABILITIES_REQUIRED", message: "At least one capability is required." });
398
+ if (!Array.isArray(value)) {
399
+ errors.push({ path: "$.capabilities", code: "CAPABILITIES_REQUIRED", message: "capabilities must be an array; an empty array represents zero authority." });
399
400
  return names;
400
401
  }
401
402
  value.forEach((capability, index) => {
@@ -1122,6 +1123,55 @@ function validateProposalAction(value, subject, path3, errors) {
1122
1123
  });
1123
1124
  }
1124
1125
  }
1126
+ if (value.execution !== void 0) {
1127
+ validateNestedObject(value.execution, EXECUTION_KEYS, `${path3}.execution`, errors);
1128
+ if (isRecord(value.execution) && value.execution.supervised_worker !== "allowed") {
1129
+ errors.push({
1130
+ path: `${path3}.execution.supervised_worker`,
1131
+ code: "INVALID_SUPERVISED_WORKER_PERMISSION",
1132
+ message: "execution.supervised_worker must be allowed when the optional permission is present."
1133
+ });
1134
+ }
1135
+ if (isRecord(value.execution) && value.execution.supervised_worker === "allowed") {
1136
+ const cardinality = isRecord(value.operation) ? value.operation.cardinality ?? "single" : "single";
1137
+ const writebackMode = isRecord(value.writeback) ? value.writeback.mode : "direct_sql";
1138
+ if (writebackMode !== "direct_sql") {
1139
+ errors.push({
1140
+ path: `${path3}.writeback.mode`,
1141
+ code: "SUPERVISED_WORKER_DIRECT_SQL_REQUIRED",
1142
+ message: "supervised-worker execution is initially limited to Runner-owned direct_sql writeback."
1143
+ });
1144
+ }
1145
+ if (cardinality !== "single") {
1146
+ errors.push({
1147
+ path: `${path3}.operation.cardinality`,
1148
+ code: "SUPERVISED_WORKER_SINGLE_ROW_REQUIRED",
1149
+ message: "supervised-worker execution is initially limited to deterministic single-row writes."
1150
+ });
1151
+ }
1152
+ if (deleteOperation) {
1153
+ errors.push({
1154
+ path: `${path3}.operation.kind`,
1155
+ code: "SUPERVISED_WORKER_DELETE_FORBIDDEN",
1156
+ message: "hard DELETE is not eligible for supervised-worker execution in this release."
1157
+ });
1158
+ }
1159
+ if (value.reversibility !== void 0) {
1160
+ errors.push({
1161
+ path: `${path3}.reversibility`,
1162
+ code: "SUPERVISED_WORKER_REVERSIBILITY_FORBIDDEN",
1163
+ message: "reviewed compensation actions require manual execution in this release."
1164
+ });
1165
+ }
1166
+ if (operation === "update" && (!isRecord(value.conflict_guard) || !isSafeIdentifier(value.conflict_guard.column) || value.conflict_guard.weak_guard_ack === true)) {
1167
+ errors.push({
1168
+ path: `${path3}.conflict_guard.column`,
1169
+ code: "SUPERVISED_WORKER_EXACT_CONFLICT_GUARD_REQUIRED",
1170
+ message: "supervised-worker UPDATE requires an exact source version/conflict column; a weak row hash is not eligible."
1171
+ });
1172
+ }
1173
+ }
1174
+ }
1125
1175
  if (value.writeback !== void 0) {
1126
1176
  validateNestedObject(value.writeback, WRITEBACK_KEYS, `${path3}.writeback`, errors);
1127
1177
  if (isRecord(value.writeback) && !["direct_sql", "app_handler", "cloud_worker", "none"].includes(String(value.writeback.mode)))
@@ -1467,7 +1517,7 @@ function validatePolicies(value, errors) {
1467
1517
  if (!Array.isArray(policy.rules)) {
1468
1518
  errors.push({ path: `${path3}.rules`, code: "POLICY_RULES_NOT_ARRAY", message: "policy.rules must be an array." });
1469
1519
  } else if (policy.kind === "approval") {
1470
- policy.rules.forEach((rule, ruleIndex) => validateApprovalPolicyRuleShape(rule, `${path3}.rules[${ruleIndex}]`, errors));
1520
+ policy.rules.forEach((rule2, ruleIndex) => validateApprovalPolicyRuleShape(rule2, `${path3}.rules[${ruleIndex}]`, errors));
1471
1521
  }
1472
1522
  }
1473
1523
  if (policy.limits !== void 0) {
@@ -1507,16 +1557,16 @@ function validateApprovalPolicyLimitShape(limit, path3, errors) {
1507
1557
  errors.push({ path: `${path3}.field`, code: "APPROVAL_POLICY_COUNT_FIELD_FORBIDDEN", message: "count approval limits must not declare a field." });
1508
1558
  }
1509
1559
  }
1510
- function validateApprovalPolicyRuleShape(rule, path3, errors) {
1511
- if (!isRecord(rule)) {
1560
+ function validateApprovalPolicyRuleShape(rule2, path3, errors) {
1561
+ if (!isRecord(rule2)) {
1512
1562
  errors.push({ path: path3, code: "APPROVAL_POLICY_RULE_NOT_OBJECT", message: "approval policy rules must be objects." });
1513
1563
  return;
1514
1564
  }
1515
1565
  const keys = /* @__PURE__ */ new Set(["field", "max"]);
1516
- checkUnknownKeys(rule, keys, path3, errors);
1517
- if (!isSafeIdentifier(rule.field))
1566
+ checkUnknownKeys(rule2, keys, path3, errors);
1567
+ if (!isSafeIdentifier(rule2.field))
1518
1568
  errors.push({ path: `${path3}.field`, code: "INVALID_APPROVAL_POLICY_FIELD", message: "approval policy rule field must be a safe identifier." });
1519
- if (!Number.isInteger(rule.max) || Number(rule.max) < 0)
1569
+ if (!Number.isInteger(rule2.max) || Number(rule2.max) < 0)
1520
1570
  errors.push({ path: `${path3}.max`, code: "INVALID_APPROVAL_POLICY_MAX", message: "approval policy rule max must be a non-negative integer." });
1521
1571
  }
1522
1572
  function validateCapabilityApprovalPolicies(capabilities, policies, policyByName, errors) {
@@ -1563,11 +1613,11 @@ function validateApprovalPolicyRulesAgainstCapability(policy, policyIndex, capab
1563
1613
  if (!proposal)
1564
1614
  return;
1565
1615
  if (Array.isArray(policy.rules))
1566
- policy.rules.forEach((rule, ruleIndex) => {
1567
- if (!isRecord(rule))
1616
+ policy.rules.forEach((rule2, ruleIndex) => {
1617
+ if (!isRecord(rule2))
1568
1618
  return;
1569
- const field = rule.field;
1570
- const max = rule.max;
1619
+ const field = rule2.field;
1620
+ const max = rule2.max;
1571
1621
  const rulePath = `$.policies[${policyIndex}].rules[${ruleIndex}]`;
1572
1622
  if (!isSafeIdentifier(field) || !Number.isInteger(max))
1573
1623
  return;
@@ -1787,7 +1837,7 @@ function sortJson(value) {
1787
1837
  }
1788
1838
 
1789
1839
  // packages/config/src/index.ts
1790
- var TOP_LEVEL_KEYS2 = /* @__PURE__ */ new Set(["version", "mode", "storage", "sources", "trusted_context", "contexts", "executors", "capabilities", "contracts", "policies", "approvals", "proposal_freshness", "operator_identity", "session_auth", "http_security", "rate_limits", "metrics", "graduated_trust", "cloud", "governance", "generated_authority", "strict", "result_format"]);
1840
+ var TOP_LEVEL_KEYS2 = /* @__PURE__ */ new Set(["version", "mode", "storage", "sources", "trusted_context", "contexts", "executors", "capabilities", "contracts", "policies", "approvals", "proposal_freshness", "operator_identity", "session_auth", "http_security", "rate_limits", "metrics", "graduated_trust", "cloud", "governance", "generated_authority", "supervised_worker", "notifications", "strict", "result_format"]);
1791
1841
  var STORAGE_KEYS = /* @__PURE__ */ new Set(["sqlite_path", "shared_postgres"]);
1792
1842
  var SHARED_POSTGRES_STORAGE_KEYS = /* @__PURE__ */ new Set(["mode", "url_env", "schema", "lock_timeout_ms", "max_entries"]);
1793
1843
  var APPROVALS_KEYS = /* @__PURE__ */ new Set(["disable_auto_approval"]);
@@ -1876,6 +1926,95 @@ var RATE_LIMIT_RULE_KEYS = /* @__PURE__ */ new Set(["requests", "window_seconds"
1876
1926
  var CLOUD_KEYS = /* @__PURE__ */ new Set(["base_url_env", "runner_token_env", "runner_id", "runner_version", "project_id", "adapter_id", "source_id", "engines", "capabilities", "session"]);
1877
1927
  var GOVERNANCE_KEYS = /* @__PURE__ */ new Set(["mode", "connection_file", "evidence_residency", "queue_when_unavailable", "sync_interval_ms", "max_attempts", "outbox_retention_days"]);
1878
1928
  var GENERATED_AUTHORITY_KEYS = /* @__PURE__ */ new Set(["generation_lock_path", "enforcement"]);
1929
+ var SUPERVISED_WORKER_KEYS = /* @__PURE__ */ new Set(["enabled", "profile", "capabilities"]);
1930
+ var SUPERVISED_WORKER_CAPABILITY_KEYS = /* @__PURE__ */ new Set([
1931
+ "capability",
1932
+ "contract_digest",
1933
+ "mode",
1934
+ "concurrency",
1935
+ "queue_limit",
1936
+ "lease_seconds",
1937
+ "max_attempts",
1938
+ "proposal_ttl_seconds",
1939
+ "rate_limit",
1940
+ "write_url_env",
1941
+ "require_least_privilege_writer",
1942
+ "writer_posture_fingerprint",
1943
+ "worker_identity",
1944
+ "control_role",
1945
+ "required_attention_sinks"
1946
+ ]);
1947
+ var SUPERVISED_WORKER_RATE_LIMIT_KEYS = /* @__PURE__ */ new Set(["executions", "window_seconds"]);
1948
+ var NOTIFICATION_KEYS = /* @__PURE__ */ new Set(["enabled", "workbench_url_env", "sinks"]);
1949
+ var NOTIFICATION_SINK_KEYS = /* @__PURE__ */ new Set([
1950
+ "id",
1951
+ "type",
1952
+ "enabled",
1953
+ "url_env",
1954
+ "signing_secret_env",
1955
+ "destination",
1956
+ "minimum_severity",
1957
+ "events",
1958
+ "capabilities",
1959
+ "environments",
1960
+ "delivery",
1961
+ "max_attempts",
1962
+ "timeout_ms",
1963
+ "max_response_bytes",
1964
+ "replay_window_seconds",
1965
+ "allow_private_destinations",
1966
+ "private_host_allowlist",
1967
+ "recovery_notifications",
1968
+ "budgets",
1969
+ "quiet_hours"
1970
+ ]);
1971
+ var NOTIFICATION_BUDGET_KEYS = /* @__PURE__ */ new Set([
1972
+ "per_minute",
1973
+ "per_hour",
1974
+ "immediate_informational_per_hour",
1975
+ "aggregation_window_seconds",
1976
+ "cooldown_seconds",
1977
+ "max_unresolved_reminders",
1978
+ "digest_cadence_minutes",
1979
+ "escalation_delay_seconds",
1980
+ "retry_attempt_threshold",
1981
+ "degraded_duration_seconds",
1982
+ "queue_depth_threshold",
1983
+ "queue_age_seconds"
1984
+ ]);
1985
+ var NOTIFICATION_QUIET_HOURS_KEYS = /* @__PURE__ */ new Set(["start_utc_hour", "end_utc_hour"]);
1986
+ var NOTIFICATION_EVENT_TYPES = /* @__PURE__ */ new Set([
1987
+ "proposal.created",
1988
+ "proposal.review_required",
1989
+ "proposal.auto_approved",
1990
+ "proposal.approved",
1991
+ "proposal.queued",
1992
+ "proposal.expiring",
1993
+ "proposal.expired",
1994
+ "proposal.cancelled",
1995
+ "proposal.applied",
1996
+ "proposal.conflict",
1997
+ "proposal.refused",
1998
+ "worker.started",
1999
+ "worker.paused",
2000
+ "worker.unhealthy",
2001
+ "worker.recovered",
2002
+ "worker.queue_backlog",
2003
+ "worker.retry_scheduled",
2004
+ "worker.dead_lettered",
2005
+ "worker.unknown_outcome",
2006
+ "worker.reconciliation_required",
2007
+ "capability.review_required",
2008
+ "capability.activated",
2009
+ "capability.revoked",
2010
+ "contract.digest_stale",
2011
+ "schema.drift_detected",
2012
+ "credential.posture_changed",
2013
+ "policy.limit_near",
2014
+ "policy.limit_exceeded",
2015
+ "sensitive_override_activated",
2016
+ "notification.replayed"
2017
+ ]);
1879
2018
  var SOURCE_KEYS = /* @__PURE__ */ new Set([
1880
2019
  "engine",
1881
2020
  "read_url_env",
@@ -1917,6 +2056,7 @@ var CAPABILITY_KEYS2 = /* @__PURE__ */ new Set([
1917
2056
  "reversibility",
1918
2057
  "conflict_guard",
1919
2058
  "approval",
2059
+ "execution",
1920
2060
  "writeback",
1921
2061
  "operation",
1922
2062
  "single_tenant_dev_ack",
@@ -1935,6 +2075,7 @@ var TRANSITION_GUARD_KEYS2 = /* @__PURE__ */ new Set(["from_column", "allowed"])
1935
2075
  var REVERSIBILITY_KEYS2 = /* @__PURE__ */ new Set(["mode"]);
1936
2076
  var CONFLICT_GUARD_KEYS2 = /* @__PURE__ */ new Set(["column", "weak_guard_ack"]);
1937
2077
  var APPROVAL_KEYS2 = /* @__PURE__ */ new Set(["mode", "required_role", "required_approvals", "policy"]);
2078
+ var EXECUTION_KEYS2 = /* @__PURE__ */ new Set(["supervised_worker"]);
1938
2079
  var POLICY_KEYS2 = /* @__PURE__ */ new Set(["name", "kind", "mode", "rules", "limits"]);
1939
2080
  var APPROVAL_POLICY_RULE_KEYS = /* @__PURE__ */ new Set(["field", "max"]);
1940
2081
  var APPROVAL_POLICY_LIMIT_KEYS2 = /* @__PURE__ */ new Set(["kind", "max", "period", "field", "scope"]);
@@ -2036,7 +2177,20 @@ function validateRunnerCapabilityConfig(input) {
2036
2177
  validateRateLimits(input.rate_limits, strict, errors);
2037
2178
  validateMetrics(input.metrics, strict, errors);
2038
2179
  validateGraduatedTrust(input.graduated_trust, strict, errors);
2039
- validateCapabilities2(input.capabilities, input.sources, input.contexts, input.executors, input.mode, strict, errors, warnings, hasContracts);
2180
+ validateCapabilities2(
2181
+ input.capabilities,
2182
+ input.sources,
2183
+ input.contexts,
2184
+ input.executors,
2185
+ input.mode,
2186
+ strict,
2187
+ errors,
2188
+ warnings,
2189
+ hasContracts,
2190
+ input.generated_authority
2191
+ );
2192
+ validateNotifications(input.notifications, strict, errors);
2193
+ validateSupervisedWorker(input.supervised_worker, input, strict, errors);
2040
2194
  validateProposalFreshness(input.proposal_freshness, input.capabilities, strict, errors);
2041
2195
  validateEffectiveContextCompatibility(input.trusted_context, input.contexts, input.capabilities, errors);
2042
2196
  validateApprovalPolicyReferences(input.capabilities, input.policies, errors);
@@ -2058,6 +2212,314 @@ function validateGeneratedAuthority(value, strict, errors) {
2058
2212
  errors.push({ path: "$.generated_authority.enforcement", code: "INVALID_GENERATION_LOCK_ENFORCEMENT", message: "generated authority must use required lock enforcement." });
2059
2213
  }
2060
2214
  }
2215
+ function validateNotifications(value, strict, errors) {
2216
+ if (value === void 0) return;
2217
+ if (!isRecord2(value)) {
2218
+ errors.push({ path: "$.notifications", code: "NOTIFICATIONS_NOT_OBJECT", message: "notifications must be an operator-owned configuration object." });
2219
+ return;
2220
+ }
2221
+ if (strict) checkUnknownKeys2(value, NOTIFICATION_KEYS, "$.notifications", errors);
2222
+ if (typeof value.enabled !== "boolean") {
2223
+ errors.push({ path: "$.notifications.enabled", code: "NOTIFICATIONS_ENABLED_REQUIRED", message: "notifications.enabled must be an explicit boolean." });
2224
+ }
2225
+ if (value.workbench_url_env !== void 0 && !isEnvName(value.workbench_url_env)) {
2226
+ errors.push({ path: "$.notifications.workbench_url_env", code: "INVALID_WORKBENCH_URL_ENV", message: "workbench_url_env must name an environment variable, not contain a URL." });
2227
+ }
2228
+ if (!Array.isArray(value.sinks)) {
2229
+ errors.push({ path: "$.notifications.sinks", code: "NOTIFICATION_SINKS_REQUIRED", message: "notifications.sinks must be an explicit array." });
2230
+ return;
2231
+ }
2232
+ if (value.sinks.length > 32) {
2233
+ errors.push({ path: "$.notifications.sinks", code: "TOO_MANY_NOTIFICATION_SINKS", message: "notifications supports at most 32 operator-owned sinks." });
2234
+ }
2235
+ if (value.enabled === true && value.sinks.filter(isRecord2).every((sink) => sink.enabled === false)) {
2236
+ errors.push({ path: "$.notifications.sinks", code: "ENABLED_NOTIFICATION_SINK_REQUIRED", message: "enabled notifications require at least one sink that is not disabled." });
2237
+ }
2238
+ const seen = /* @__PURE__ */ new Set();
2239
+ for (const [index, rawSink] of value.sinks.entries()) {
2240
+ const path3 = `$.notifications.sinks[${index}]`;
2241
+ if (!isRecord2(rawSink)) {
2242
+ errors.push({ path: path3, code: "NOTIFICATION_SINK_NOT_OBJECT", message: "each notification sink must be an object." });
2243
+ continue;
2244
+ }
2245
+ if (strict) checkUnknownKeys2(rawSink, NOTIFICATION_SINK_KEYS, path3, errors);
2246
+ if (!isSafeName(rawSink.id)) {
2247
+ errors.push({ path: `${path3}.id`, code: "INVALID_NOTIFICATION_SINK_ID", message: "notification sink id must be a fixed safe name." });
2248
+ } else if (seen.has(rawSink.id)) {
2249
+ errors.push({ path: `${path3}.id`, code: "DUPLICATE_NOTIFICATION_SINK_ID", message: "notification sink ids must be unique." });
2250
+ } else seen.add(rawSink.id);
2251
+ if (rawSink.type !== "webhook" && rawSink.type !== "jsonl") {
2252
+ errors.push({ path: `${path3}.type`, code: "INVALID_NOTIFICATION_SINK_TYPE", message: "notification sink type must be webhook or jsonl." });
2253
+ }
2254
+ if (rawSink.enabled !== void 0 && typeof rawSink.enabled !== "boolean") {
2255
+ errors.push({ path: `${path3}.enabled`, code: "INVALID_NOTIFICATION_SINK_ENABLED", message: "notification sink enabled must be boolean." });
2256
+ }
2257
+ if (rawSink.type === "webhook") {
2258
+ if (!isEnvName(rawSink.url_env)) {
2259
+ errors.push({ path: `${path3}.url_env`, code: "NOTIFICATION_WEBHOOK_URL_ENV_REQUIRED", message: "webhook url_env must name an environment variable." });
2260
+ }
2261
+ if (!isEnvName(rawSink.signing_secret_env)) {
2262
+ errors.push({ path: `${path3}.signing_secret_env`, code: "NOTIFICATION_SIGNING_SECRET_ENV_REQUIRED", message: "webhook signing_secret_env must name an environment variable." });
2263
+ }
2264
+ if (rawSink.destination !== void 0) {
2265
+ errors.push({ path: `${path3}.destination`, code: "WEBHOOK_DESTINATION_UNSUPPORTED", message: "webhook destinations come only from url_env." });
2266
+ }
2267
+ }
2268
+ if (rawSink.type === "jsonl") {
2269
+ if (rawSink.destination !== "stdout") {
2270
+ errors.push({ path: `${path3}.destination`, code: "JSONL_STDOUT_REQUIRED", message: "the built-in JSONL development sink writes only to stdout." });
2271
+ }
2272
+ if (rawSink.url_env !== void 0 || rawSink.signing_secret_env !== void 0) {
2273
+ errors.push({ path: path3, code: "JSONL_WEBHOOK_FIELDS_FORBIDDEN", message: "JSONL sinks do not accept webhook URL or signing-secret fields." });
2274
+ }
2275
+ }
2276
+ if (rawSink.minimum_severity !== void 0 && !["informational", "warning", "critical"].includes(String(rawSink.minimum_severity))) {
2277
+ errors.push({ path: `${path3}.minimum_severity`, code: "INVALID_NOTIFICATION_SEVERITY", message: "minimum_severity must be informational, warning, or critical." });
2278
+ }
2279
+ if (rawSink.delivery !== void 0 && !["immediate", "digest", "all"].includes(String(rawSink.delivery))) {
2280
+ errors.push({ path: `${path3}.delivery`, code: "INVALID_NOTIFICATION_DELIVERY_MODE", message: "delivery must be immediate, digest, or all." });
2281
+ }
2282
+ if (rawSink.events !== void 0) {
2283
+ if (!Array.isArray(rawSink.events) || rawSink.events.length > 64 || rawSink.events.some((event) => typeof event !== "string" || !NOTIFICATION_EVENT_TYPES.has(event))) {
2284
+ errors.push({ path: `${path3}.events`, code: "INVALID_NOTIFICATION_EVENT_FILTER", message: "events must contain only supported fixed attention event names." });
2285
+ }
2286
+ }
2287
+ if (rawSink.capabilities !== void 0 && (!Array.isArray(rawSink.capabilities) || rawSink.capabilities.length > 256 || rawSink.capabilities.some((item) => !isQualifiedName2(item)))) {
2288
+ errors.push({ path: `${path3}.capabilities`, code: "INVALID_NOTIFICATION_CAPABILITY_FILTER", message: "capabilities must contain fixed qualified capability names." });
2289
+ }
2290
+ if (rawSink.environments !== void 0 && (!Array.isArray(rawSink.environments) || rawSink.environments.length > 4 || rawSink.environments.some((item) => !["development", "staging", "production", "unknown"].includes(String(item))))) {
2291
+ errors.push({ path: `${path3}.environments`, code: "INVALID_NOTIFICATION_ENVIRONMENT_FILTER", message: "environments must contain development, staging, production, or unknown." });
2292
+ }
2293
+ for (const [key, minimum, maximum] of [
2294
+ ["max_attempts", 1, 100],
2295
+ ["timeout_ms", 100, 3e4],
2296
+ ["max_response_bytes", 0, 65536],
2297
+ ["replay_window_seconds", 30, 3600]
2298
+ ]) {
2299
+ if (rawSink[key] !== void 0 && (!Number.isSafeInteger(rawSink[key]) || Number(rawSink[key]) < minimum || Number(rawSink[key]) > maximum)) {
2300
+ errors.push({ path: `${path3}.${key}`, code: "INVALID_NOTIFICATION_BOUND", message: `${key} must be an integer from ${minimum} through ${maximum}.` });
2301
+ }
2302
+ }
2303
+ if (rawSink.allow_private_destinations !== void 0 && typeof rawSink.allow_private_destinations !== "boolean") {
2304
+ errors.push({ path: `${path3}.allow_private_destinations`, code: "INVALID_PRIVATE_DESTINATION_POLICY", message: "allow_private_destinations must be boolean." });
2305
+ }
2306
+ if (rawSink.recovery_notifications !== void 0 && typeof rawSink.recovery_notifications !== "boolean") {
2307
+ errors.push({ path: `${path3}.recovery_notifications`, code: "INVALID_RECOVERY_NOTIFICATION_POLICY", message: "recovery_notifications must be boolean." });
2308
+ }
2309
+ if (rawSink.private_host_allowlist !== void 0) {
2310
+ if (!Array.isArray(rawSink.private_host_allowlist) || rawSink.private_host_allowlist.length > 32 || rawSink.private_host_allowlist.some((host) => !isSafeNotificationHost(host))) {
2311
+ errors.push({ path: `${path3}.private_host_allowlist`, code: "INVALID_PRIVATE_HOST_ALLOWLIST", message: "private_host_allowlist must contain at most 32 fixed DNS hostnames or IP literals." });
2312
+ }
2313
+ if (rawSink.allow_private_destinations !== true && Array.isArray(rawSink.private_host_allowlist) && rawSink.private_host_allowlist.length > 0) {
2314
+ errors.push({ path: `${path3}.allow_private_destinations`, code: "PRIVATE_DESTINATION_OPT_IN_REQUIRED", message: "private host exceptions require allow_private_destinations=true." });
2315
+ }
2316
+ }
2317
+ validateNotificationBudgets(rawSink.budgets, `${path3}.budgets`, strict, errors);
2318
+ validateNotificationQuietHours(rawSink.quiet_hours, `${path3}.quiet_hours`, strict, errors);
2319
+ }
2320
+ }
2321
+ function validateNotificationBudgets(value, path3, strict, errors) {
2322
+ if (value === void 0) return;
2323
+ if (!isRecord2(value)) {
2324
+ errors.push({ path: path3, code: "NOTIFICATION_BUDGETS_NOT_OBJECT", message: "notification budgets must be an object." });
2325
+ return;
2326
+ }
2327
+ if (strict) checkUnknownKeys2(value, NOTIFICATION_BUDGET_KEYS, path3, errors);
2328
+ for (const [key, minimum, maximum] of [
2329
+ ["per_minute", 1, 1e4],
2330
+ ["per_hour", 1, 1e5],
2331
+ ["immediate_informational_per_hour", 0, 1e4],
2332
+ ["aggregation_window_seconds", 1, 86400],
2333
+ ["cooldown_seconds", 0, 604800],
2334
+ ["max_unresolved_reminders", 0, 100],
2335
+ ["digest_cadence_minutes", 1, 10080],
2336
+ ["escalation_delay_seconds", 0, 604800],
2337
+ ["retry_attempt_threshold", 1, 100],
2338
+ ["degraded_duration_seconds", 1, 86400],
2339
+ ["queue_depth_threshold", 1, 1e6],
2340
+ ["queue_age_seconds", 1, 604800]
2341
+ ]) {
2342
+ if (value[key] !== void 0 && (!Number.isSafeInteger(value[key]) || Number(value[key]) < minimum || Number(value[key]) > maximum)) {
2343
+ errors.push({ path: `${path3}.${key}`, code: "INVALID_NOTIFICATION_BUDGET", message: `${key} must be an integer from ${minimum} through ${maximum}.` });
2344
+ }
2345
+ }
2346
+ }
2347
+ function validateNotificationQuietHours(value, path3, strict, errors) {
2348
+ if (value === void 0) return;
2349
+ if (!isRecord2(value)) {
2350
+ errors.push({ path: path3, code: "NOTIFICATION_QUIET_HOURS_NOT_OBJECT", message: "quiet_hours must be an object." });
2351
+ return;
2352
+ }
2353
+ if (strict) checkUnknownKeys2(value, NOTIFICATION_QUIET_HOURS_KEYS, path3, errors);
2354
+ for (const key of ["start_utc_hour", "end_utc_hour"]) {
2355
+ if (!Number.isSafeInteger(value[key]) || Number(value[key]) < 0 || Number(value[key]) > 23) {
2356
+ errors.push({ path: `${path3}.${key}`, code: "INVALID_NOTIFICATION_QUIET_HOUR", message: `${key} must be an integer from 0 through 23.` });
2357
+ }
2358
+ }
2359
+ }
2360
+ function validateSupervisedWorker(value, config, strict, errors) {
2361
+ if (value === void 0) return;
2362
+ if (!isRecord2(value)) {
2363
+ errors.push({ path: "$.supervised_worker", code: "SUPERVISED_WORKER_NOT_OBJECT", message: "supervised_worker must be an explicit deployment policy object." });
2364
+ return;
2365
+ }
2366
+ if (strict) checkUnknownKeys2(value, SUPERVISED_WORKER_KEYS, "$.supervised_worker", errors);
2367
+ if (typeof value.enabled !== "boolean") {
2368
+ errors.push({ path: "$.supervised_worker.enabled", code: "SUPERVISED_WORKER_ENABLED_REQUIRED", message: "supervised_worker.enabled must be an explicit boolean." });
2369
+ }
2370
+ if (value.profile !== "development" && value.profile !== "staging" && value.profile !== "production") {
2371
+ errors.push({ path: "$.supervised_worker.profile", code: "SUPERVISED_WORKER_PROFILE_REQUIRED", message: "supervised_worker.profile must be development, staging, or production." });
2372
+ }
2373
+ if (!Array.isArray(value.capabilities)) {
2374
+ errors.push({ path: "$.supervised_worker.capabilities", code: "SUPERVISED_WORKER_CAPABILITIES_REQUIRED", message: "supervised_worker.capabilities must be an explicit exact-digest allowlist." });
2375
+ return;
2376
+ }
2377
+ if (value.enabled === true && value.capabilities.length === 0) {
2378
+ errors.push({ path: "$.supervised_worker.capabilities", code: "SUPERVISED_WORKER_ALLOWLIST_REQUIRED", message: "enabled supervised-worker execution requires at least one exact capability/digest allowlist entry." });
2379
+ }
2380
+ if (config.mode !== "review" && value.enabled === true) {
2381
+ errors.push({ path: "$.mode", code: "SUPERVISED_WORKER_REVIEW_MODE_REQUIRED", message: "supervised-worker execution requires Runner review mode." });
2382
+ }
2383
+ if (isRecord2(config.governance) && config.governance.mode === "cloud_linked" && value.enabled === true) {
2384
+ errors.push({ path: "$.governance.mode", code: "SUPERVISED_WORKER_LOCAL_AUTHORITY_REQUIRED", message: "Cloud-linked governance does not permit the local supervised worker to become execution authority." });
2385
+ }
2386
+ if (value.profile === "production" && value.enabled === true) {
2387
+ const operator = isRecord2(config.operator_identity) ? config.operator_identity : void 0;
2388
+ if (!operator || operator.provider !== "signed_key" && operator.provider !== "jwt_oidc") {
2389
+ errors.push({ path: "$.operator_identity.provider", code: "SUPERVISED_WORKER_VERIFIED_OPERATOR_REQUIRED", message: "production supervised-worker controls require signed_key or jwt_oidc operator identity." });
2390
+ }
2391
+ }
2392
+ const runtimeCapabilities = Array.isArray(config.capabilities) ? config.capabilities.filter(isRecord2) : [];
2393
+ const byName = new Map(runtimeCapabilities.flatMap((capability) => isQualifiedName2(capability.name) ? [[String(capability.name), capability]] : []));
2394
+ const sources = isRecord2(config.sources) ? config.sources : {};
2395
+ const notifications = isRecord2(config.notifications) ? config.notifications : void 0;
2396
+ const notificationSinks = new Map(
2397
+ Array.isArray(notifications?.sinks) ? notifications.sinks.filter(isRecord2).filter((sink) => isSafeName(sink.id)).map((sink) => [String(sink.id), sink]) : []
2398
+ );
2399
+ const seen = /* @__PURE__ */ new Set();
2400
+ for (const [index, rawEntry] of value.capabilities.entries()) {
2401
+ const path3 = `$.supervised_worker.capabilities[${index}]`;
2402
+ if (!isRecord2(rawEntry)) {
2403
+ errors.push({ path: path3, code: "SUPERVISED_WORKER_CAPABILITY_NOT_OBJECT", message: "each supervised-worker allowlist entry must be an object." });
2404
+ continue;
2405
+ }
2406
+ if (strict) checkUnknownKeys2(rawEntry, SUPERVISED_WORKER_CAPABILITY_KEYS, path3, errors);
2407
+ if (!isQualifiedName2(rawEntry.capability)) {
2408
+ errors.push({ path: `${path3}.capability`, code: "INVALID_SUPERVISED_WORKER_CAPABILITY", message: "capability must be a fixed qualified capability name." });
2409
+ }
2410
+ if (typeof rawEntry.contract_digest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(rawEntry.contract_digest)) {
2411
+ errors.push({ path: `${path3}.contract_digest`, code: "INVALID_SUPERVISED_WORKER_DIGEST", message: "contract_digest must be the exact canonical sha256 digest." });
2412
+ }
2413
+ if (rawEntry.mode !== "supervised_worker") {
2414
+ errors.push({ path: `${path3}.mode`, code: "INVALID_SUPERVISED_WORKER_MODE", message: "mode must be supervised_worker." });
2415
+ }
2416
+ for (const [key2, minimum, maximum] of [
2417
+ ["concurrency", 1, 32],
2418
+ ["queue_limit", 1, 1e4],
2419
+ ["lease_seconds", 15, 3600],
2420
+ ["max_attempts", 1, 100],
2421
+ ["proposal_ttl_seconds", 60, 2592e3]
2422
+ ]) {
2423
+ if (!Number.isSafeInteger(rawEntry[key2]) || Number(rawEntry[key2]) < minimum || Number(rawEntry[key2]) > maximum) {
2424
+ errors.push({ path: `${path3}.${key2}`, code: "INVALID_SUPERVISED_WORKER_BOUND", message: `${key2} must be an integer from ${minimum} through ${maximum}.` });
2425
+ }
2426
+ }
2427
+ if (!isEnvName(rawEntry.write_url_env)) {
2428
+ errors.push({ path: `${path3}.write_url_env`, code: "SUPERVISED_WORKER_WRITE_ENV_REQUIRED", message: "write_url_env must name the separately configured writer credential environment variable." });
2429
+ }
2430
+ if (rawEntry.require_least_privilege_writer !== void 0 && typeof rawEntry.require_least_privilege_writer !== "boolean") {
2431
+ errors.push({ path: `${path3}.require_least_privilege_writer`, code: "INVALID_SUPERVISED_WORKER_POSTURE_MODE", message: "require_least_privilege_writer must be an explicit boolean when present." });
2432
+ }
2433
+ if (rawEntry.writer_posture_fingerprint !== void 0 && (typeof rawEntry.writer_posture_fingerprint !== "string" || !/^sha256:[a-f0-9]{64}$/.test(rawEntry.writer_posture_fingerprint))) {
2434
+ errors.push({ path: `${path3}.writer_posture_fingerprint`, code: "INVALID_SUPERVISED_WORKER_POSTURE_FINGERPRINT", message: "writer_posture_fingerprint must be the reviewed non-secret sha256 posture digest." });
2435
+ }
2436
+ if (rawEntry.require_least_privilege_writer === true && rawEntry.writer_posture_fingerprint === void 0) {
2437
+ errors.push({ path: `${path3}.writer_posture_fingerprint`, code: "SUPERVISED_WORKER_POSTURE_FINGERPRINT_REQUIRED", message: "hardened writer posture requires an exact reviewed posture fingerprint." });
2438
+ }
2439
+ if (value.enabled === true && value.profile === "production" && (rawEntry.require_least_privilege_writer !== true || rawEntry.writer_posture_fingerprint === void 0)) {
2440
+ errors.push({ path: path3, code: "SUPERVISED_WORKER_PRODUCTION_POSTURE_REQUIRED", message: "production supervised execution requires live least-privilege writer verification bound to an exact reviewed posture fingerprint." });
2441
+ }
2442
+ if (rawEntry.worker_identity !== void 0 && !isSafeName(rawEntry.worker_identity)) {
2443
+ errors.push({ path: `${path3}.worker_identity`, code: "INVALID_SUPERVISED_WORKER_IDENTITY", message: "worker_identity must be a fixed safe operator identity." });
2444
+ }
2445
+ if (rawEntry.control_role !== void 0 && !isSafeName(rawEntry.control_role)) {
2446
+ errors.push({ path: `${path3}.control_role`, code: "INVALID_SUPERVISED_WORKER_CONTROL_ROLE", message: "control_role must be a fixed safe operator role." });
2447
+ }
2448
+ if (rawEntry.required_attention_sinks !== void 0) {
2449
+ if (!Array.isArray(rawEntry.required_attention_sinks) || rawEntry.required_attention_sinks.length < 1 || rawEntry.required_attention_sinks.length > 8 || rawEntry.required_attention_sinks.some((sink) => !isSafeName(sink))) {
2450
+ errors.push({ path: `${path3}.required_attention_sinks`, code: "INVALID_REQUIRED_ATTENTION_SINKS", message: "required_attention_sinks must contain 1 through 8 fixed sink ids." });
2451
+ } else {
2452
+ if (notifications?.enabled !== true) {
2453
+ errors.push({ path: "$.notifications.enabled", code: "HEALTH_GATE_NOTIFICATIONS_REQUIRED", message: "required attention sinks need notifications.enabled=true." });
2454
+ }
2455
+ for (const [sinkIndex, sinkId] of rawEntry.required_attention_sinks.entries()) {
2456
+ const sink = notificationSinks.get(String(sinkId));
2457
+ if (!sink || sink.enabled === false) {
2458
+ errors.push({ path: `${path3}.required_attention_sinks[${sinkIndex}]`, code: "REQUIRED_ATTENTION_SINK_UNAVAILABLE", message: `required attention sink ${String(sinkId)} must name an enabled configured sink.` });
2459
+ }
2460
+ }
2461
+ }
2462
+ }
2463
+ if (!isRecord2(rawEntry.rate_limit)) {
2464
+ errors.push({ path: `${path3}.rate_limit`, code: "SUPERVISED_WORKER_RATE_LIMIT_REQUIRED", message: "rate_limit must define a bounded execution window." });
2465
+ } else {
2466
+ if (strict) checkUnknownKeys2(rawEntry.rate_limit, SUPERVISED_WORKER_RATE_LIMIT_KEYS, `${path3}.rate_limit`, errors);
2467
+ if (!Number.isSafeInteger(rawEntry.rate_limit.executions) || Number(rawEntry.rate_limit.executions) < 1 || Number(rawEntry.rate_limit.executions) > 1e5) {
2468
+ errors.push({ path: `${path3}.rate_limit.executions`, code: "INVALID_SUPERVISED_WORKER_RATE", message: "rate_limit.executions must be an integer from 1 through 100000." });
2469
+ }
2470
+ if (!Number.isSafeInteger(rawEntry.rate_limit.window_seconds) || Number(rawEntry.rate_limit.window_seconds) < 1 || Number(rawEntry.rate_limit.window_seconds) > 86400) {
2471
+ errors.push({ path: `${path3}.rate_limit.window_seconds`, code: "INVALID_SUPERVISED_WORKER_WINDOW", message: "rate_limit.window_seconds must be an integer from 1 through 86400." });
2472
+ }
2473
+ }
2474
+ const key = `${String(rawEntry.capability)}:${String(rawEntry.contract_digest)}`;
2475
+ if (seen.has(key)) {
2476
+ errors.push({ path: path3, code: "DUPLICATE_SUPERVISED_WORKER_ALLOWLIST", message: "each capability/digest may appear only once." });
2477
+ }
2478
+ seen.add(key);
2479
+ const capability = byName.get(String(rawEntry.capability));
2480
+ if (!capability) {
2481
+ if (runtimeCapabilities.length > 0 || !Array.isArray(config.contracts)) {
2482
+ errors.push({ path: `${path3}.capability`, code: "SUPERVISED_WORKER_CAPABILITY_UNKNOWN", message: "allowlist capability must resolve from the active Runner contract catalog." });
2483
+ }
2484
+ continue;
2485
+ }
2486
+ const provenance = isRecord2(capability.contract_provenance) ? capability.contract_provenance : void 0;
2487
+ if (!provenance || provenance.digest !== rawEntry.contract_digest) {
2488
+ errors.push({ path: `${path3}.contract_digest`, code: "SUPERVISED_WORKER_DIGEST_MISMATCH", message: "allowlist digest must exactly match the active capability contract digest." });
2489
+ }
2490
+ if (!isRecord2(capability.execution) || capability.execution.supervised_worker !== "allowed") {
2491
+ errors.push({ path: `${path3}.capability`, code: "SUPERVISED_WORKER_CONTRACT_PERMISSION_REQUIRED", message: "the active capability contract does not permit supervised-worker execution." });
2492
+ }
2493
+ const source = isNonEmptyString2(capability.source) && isRecord2(sources[capability.source]) ? sources[capability.source] : void 0;
2494
+ if (!source || source.write_url_env !== rawEntry.write_url_env || source.read_only === true) {
2495
+ errors.push({ path: `${path3}.write_url_env`, code: "SUPERVISED_WORKER_WRITER_MISMATCH", message: "allowlist write_url_env must match the selected writable source without embedding a credential." });
2496
+ }
2497
+ if (!source || !isRecord2(source.receipts)) {
2498
+ errors.push({ path: `$.sources.${String(capability.source)}.receipts`, code: "SUPERVISED_WORKER_RECEIPT_AUTHORITY_REQUIRED", message: "supervised-worker execution requires explicit source_db or runner_ledger receipt authority." });
2499
+ }
2500
+ if (rawEntry.require_least_privilege_writer === true && source && isRecord2(source.receipts) && source.receipts.authority === "source_db" && (source.receipts.provisioning !== "precreated" || !isSafeIdentifier2(source.receipts.schema) || !isSafeIdentifier2(source.receipts.table))) {
2501
+ errors.push({
2502
+ path: `$.sources.${String(capability.source)}.receipts`,
2503
+ code: "SUPERVISED_WORKER_PRECREATED_RECEIPT_REQUIRED",
2504
+ message: "hardened supervised execution with source_db receipts requires an explicitly named precreated receipt table; the runtime writer must not retain schema-creation authority."
2505
+ });
2506
+ }
2507
+ if (!isRecord2(capability.target) || !isSafeIdentifier2(capability.target.tenant_key)) {
2508
+ errors.push({ path: `${path3}.capability`, code: "SUPERVISED_WORKER_TENANT_SCOPE_REQUIRED", message: "supervised-worker execution requires a trusted tenant-scoped target." });
2509
+ }
2510
+ const operation = isRecord2(capability.operation) ? capability.operation : { kind: "update" };
2511
+ const operationKind = operation.kind ?? "update";
2512
+ if (operation.cardinality === "set" || operationKind === "delete" || capability.reversibility !== void 0 || !isRecord2(capability.writeback) || capability.writeback.mode !== "direct_sql") {
2513
+ errors.push({ path: `${path3}.capability`, code: "SUPERVISED_WORKER_SHAPE_INELIGIBLE", message: "the first supervised-worker release permits only native single-row direct_sql UPDATE/INSERT without compensation." });
2514
+ }
2515
+ if (operationKind === "update" && (!isRecord2(capability.conflict_guard) || !isSafeIdentifier2(capability.conflict_guard.column) || capability.conflict_guard.weak_guard_ack === true)) {
2516
+ errors.push({ path: `${path3}.capability`, code: "SUPERVISED_WORKER_EXACT_GUARD_REQUIRED", message: "supervised-worker UPDATE requires an exact version/conflict guard." });
2517
+ }
2518
+ if (operationKind === "insert" && !isRecord2(operation.deduplication)) {
2519
+ errors.push({ path: `${path3}.capability`, code: "SUPERVISED_WORKER_INSERT_DEDUP_REQUIRED", message: "supervised-worker INSERT requires reviewed deterministic source deduplication." });
2520
+ }
2521
+ }
2522
+ }
2061
2523
  function validateHttpSecurity(value, sessionAuth, trustedContext, contexts, strict, errors, warnings) {
2062
2524
  if (value === void 0) return;
2063
2525
  if (!isRecord2(value)) {
@@ -2314,9 +2776,9 @@ function validateRateLimits(value, strict, errors) {
2314
2776
  if (!isRecord2(value.capabilities)) {
2315
2777
  errors.push({ path: "$.rate_limits.capabilities", code: "RATE_LIMIT_CAPABILITIES_NOT_OBJECT", message: "capabilities must map reviewed capability names to rate-limit rules." });
2316
2778
  } else {
2317
- for (const [name, rule] of Object.entries(value.capabilities)) {
2779
+ for (const [name, rule2] of Object.entries(value.capabilities)) {
2318
2780
  if (!isQualifiedName2(name)) errors.push({ path: `$.rate_limits.capabilities.${name}`, code: "INVALID_RATE_LIMIT_CAPABILITY", message: "rate-limit capability keys must be qualified capability names." });
2319
- validateRateLimitRule(rule, `$.rate_limits.capabilities.${name}`, strict, errors);
2781
+ validateRateLimitRule(rule2, `$.rate_limits.capabilities.${name}`, strict, errors);
2320
2782
  }
2321
2783
  }
2322
2784
  }
@@ -2896,7 +3358,7 @@ function validatePolicies2(value, strict, errors) {
2896
3358
  if (!Array.isArray(policy.rules)) {
2897
3359
  errors.push({ path: `${path3}.rules`, code: "POLICY_RULES_NOT_ARRAY", message: "policy.rules must be an array." });
2898
3360
  } else if (policy.kind === "approval") {
2899
- policy.rules.forEach((rule, ruleIndex) => validateApprovalPolicyRule(rule, `${path3}.rules[${ruleIndex}]`, strict, errors));
3361
+ policy.rules.forEach((rule2, ruleIndex) => validateApprovalPolicyRule(rule2, `${path3}.rules[${ruleIndex}]`, strict, errors));
2900
3362
  }
2901
3363
  }
2902
3364
  if (policy.limits !== void 0) {
@@ -3087,10 +3549,20 @@ function validateExecutorAuth(value, path3, strict, errors) {
3087
3549
  errors.push({ path: `${path3}.token_env`, code: "EXECUTOR_TOKEN_ENV_REQUIRED", message: "bearer_env auth requires token_env." });
3088
3550
  }
3089
3551
  }
3090
- function validateCapabilities2(value, sources, contexts, executors, mode, strict, errors, warnings, hasContracts = false) {
3552
+ function validateCapabilities2(value, sources, contexts, executors, mode, strict, errors, warnings, hasContracts = false, generatedAuthority) {
3091
3553
  if (mode === "cloud" && value === void 0) return;
3092
3554
  if (hasContracts && value === void 0) return;
3093
3555
  if (hasContracts && Array.isArray(value) && value.length === 0) return;
3556
+ const emptyReadOnlyShell = mode === "read_only" && Array.isArray(value) && value.length === 0;
3557
+ if (emptyReadOnlyShell) {
3558
+ const lockBound = isRecord2(generatedAuthority) && generatedAuthority.enforcement === "required";
3559
+ warnings.push({
3560
+ path: "$.capabilities",
3561
+ code: lockBound ? "AUTHORING_PROJECT_HAS_NO_ACTIVE_CAPABILITIES" : "READ_ONLY_CONFIG_HAS_NO_ACTIVE_CAPABILITIES",
3562
+ message: lockBound ? "This generated authoring project exposes no named runtime capabilities until an exact reviewed digest is activated." : "This read-only configuration exposes no model-facing capabilities."
3563
+ });
3564
+ return;
3565
+ }
3094
3566
  if (!Array.isArray(value) || value.length === 0) {
3095
3567
  errors.push({ path: "$.capabilities", code: "CAPABILITIES_REQUIRED", message: "At least one capability is required." });
3096
3568
  return;
@@ -3622,6 +4094,16 @@ function validateProposalCapability(capability, path3, strict, errors) {
3622
4094
  }
3623
4095
  }
3624
4096
  }
4097
+ if (capability.execution !== void 0) {
4098
+ if (!isRecord2(capability.execution)) {
4099
+ errors.push({ path: `${path3}.execution`, code: "EXECUTION_NOT_OBJECT", message: "execution must be an explicit reviewed permission object." });
4100
+ } else {
4101
+ if (strict) checkUnknownKeys2(capability.execution, EXECUTION_KEYS2, `${path3}.execution`, errors);
4102
+ if (capability.execution.supervised_worker !== "allowed") {
4103
+ errors.push({ path: `${path3}.execution.supervised_worker`, code: "INVALID_SUPERVISED_WORKER_PERMISSION", message: "execution.supervised_worker must be allowed when present." });
4104
+ }
4105
+ }
4106
+ }
3625
4107
  if (operation === "delete" && capabilityWritebackMode(capability) === "direct_sql") {
3626
4108
  const approval = isRecord2(capability.approval) ? capability.approval : void 0;
3627
4109
  if (!approval || approval.mode !== "human" && approval.mode !== "operator") {
@@ -3896,6 +4378,10 @@ function isSafeIdentifier2(value) {
3896
4378
  function isSafeName(value) {
3897
4379
  return typeof value === "string" && /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value);
3898
4380
  }
4381
+ function isSafeNotificationHost(value) {
4382
+ if (typeof value !== "string" || value.length < 1 || value.length > 253 || /[\s/@?#]/.test(value)) return false;
4383
+ return /^[A-Za-z0-9.-]+$/.test(value) || /^[0-9A-Fa-f:]+$/.test(value);
4384
+ }
3899
4385
  function isQualifiedName2(value) {
3900
4386
  return typeof value === "string" && /^[A-Za-z_][A-Za-z0-9_]*\.[A-Za-z_][A-Za-z0-9_]*$/.test(value);
3901
4387
  }
@@ -5979,6 +6465,90 @@ var PostgresProposalRuntimeStore = class {
5979
6465
  async requireWritebackReconciliation(intentId, reason) {
5980
6466
  await this.withWrite("requireWritebackReconciliation", (store) => store.requireWritebackReconciliation(intentId, reason));
5981
6467
  }
6468
+ async enqueueWorkerProposal(input) {
6469
+ return await this.withWrite("enqueueWorkerProposal", (store) => store.enqueueWorkerProposal(input));
6470
+ }
6471
+ async claimWorkerItem(input) {
6472
+ return await this.withWrite("claimWorkerItem", (store) => store.claimWorkerItem(input));
6473
+ }
6474
+ async assertActiveWorkerLease(input) {
6475
+ return await this.withRead((store) => store.assertActiveWorkerLease(input));
6476
+ }
6477
+ async renewWorkerLease(input) {
6478
+ return await this.withWrite("renewWorkerLease", (store) => store.renewWorkerLease(input));
6479
+ }
6480
+ async completeWorkerItem(proposalId, workerId, outcome, now, leaseId) {
6481
+ return await this.withWrite("completeWorkerItem", (store) => store.completeWorkerItem(proposalId, workerId, outcome, now, leaseId));
6482
+ }
6483
+ async blockWorkerItem(input) {
6484
+ return await this.withWrite("blockWorkerItem", (store) => store.blockWorkerItem(input));
6485
+ }
6486
+ async requireWorkerReconciliation(input) {
6487
+ return await this.withWrite("requireWorkerReconciliation", (store) => store.requireWorkerReconciliation(input));
6488
+ }
6489
+ async workerControlState() {
6490
+ return await this.withRead((store) => store.workerControlState());
6491
+ }
6492
+ async updateWorkerControl(input) {
6493
+ return await this.withWrite("updateWorkerControl", (store) => store.updateWorkerControl(input));
6494
+ }
6495
+ async cancelWorkerItem(input) {
6496
+ return await this.withWrite("cancelWorkerItem", (store) => store.cancelWorkerItem(input));
6497
+ }
6498
+ async listWorkerQueue(status) {
6499
+ return await this.withRead((store) => store.listWorkerQueue(status));
6500
+ }
6501
+ async getWorkerQueueItem(proposalId) {
6502
+ return await this.withRead((store) => store.getWorkerQueueItem(proposalId));
6503
+ }
6504
+ async recordAttentionEvent(input) {
6505
+ return await this.withWrite("recordAttentionEvent", (store) => store.recordAttentionEvent(input));
6506
+ }
6507
+ async listAttentionEvents(filters = {}) {
6508
+ return await this.withRead((store) => store.listAttentionEvents(filters));
6509
+ }
6510
+ async getAttentionEvent(eventId) {
6511
+ return await this.withRead((store) => store.getAttentionEvent(eventId));
6512
+ }
6513
+ async listAttentionItems(filters = {}) {
6514
+ return await this.withRead((store) => store.listAttentionItems(filters));
6515
+ }
6516
+ async getAttentionItem(attentionId) {
6517
+ return await this.withRead((store) => store.getAttentionItem(attentionId));
6518
+ }
6519
+ async acknowledgeAttention(input) {
6520
+ return await this.withWrite("acknowledgeAttention", (store) => store.acknowledgeAttention(input));
6521
+ }
6522
+ async resolveAttention(input) {
6523
+ return await this.withWrite("resolveAttention", (store) => store.resolveAttention(input));
6524
+ }
6525
+ async enqueueNotificationDelivery(input) {
6526
+ return await this.withWrite("enqueueNotificationDelivery", (store) => store.enqueueNotificationDelivery(input));
6527
+ }
6528
+ async includeNotificationDeliveriesInDigest(input) {
6529
+ return await this.withWrite(
6530
+ "includeNotificationDeliveriesInDigest",
6531
+ (store) => store.includeNotificationDeliveriesInDigest(input)
6532
+ );
6533
+ }
6534
+ async claimNotificationDeliveries(input) {
6535
+ return await this.withWrite("claimNotificationDeliveries", (store) => store.claimNotificationDeliveries(input));
6536
+ }
6537
+ async completeNotificationDelivery(input) {
6538
+ return await this.withWrite("completeNotificationDelivery", (store) => store.completeNotificationDelivery(input));
6539
+ }
6540
+ async failNotificationDelivery(input) {
6541
+ return await this.withWrite("failNotificationDelivery", (store) => store.failNotificationDelivery(input));
6542
+ }
6543
+ async listNotificationDeliveries(filters = {}) {
6544
+ return await this.withRead((store) => store.listNotificationDeliveries(filters));
6545
+ }
6546
+ async getNotificationDelivery(deliveryId) {
6547
+ return await this.withRead((store) => store.getNotificationDelivery(deliveryId));
6548
+ }
6549
+ async requeueNotificationDelivery(input) {
6550
+ return await this.withWrite("requeueNotificationDelivery", (store) => store.requeueNotificationDelivery(input));
6551
+ }
5982
6552
  async enqueueCloudOutbox(input) {
5983
6553
  return await this.withWrite("enqueueCloudOutbox", (store) => store.enqueueCloudOutbox(input));
5984
6554
  }
@@ -6236,6 +6806,9 @@ var ProposalStore = class {
6236
6806
  shadow_study_cases: this.countTable("shadow_study_cases"),
6237
6807
  shadow_outcomes: this.countTable("shadow_outcomes"),
6238
6808
  worker_queue: this.countTable("worker_queue"),
6809
+ attention_events: this.countTable("attention_events"),
6810
+ attention_items: this.countTable("attention_items"),
6811
+ notification_deliveries: this.countTable("notification_deliveries"),
6239
6812
  policy_recommendations: this.countTable("policy_recommendations"),
6240
6813
  page_count: pageCount,
6241
6814
  page_size: pageSize,
@@ -6575,15 +7148,95 @@ var ProposalStore = class {
6575
7148
  updated_at TEXT NOT NULL
6576
7149
  );
6577
7150
 
7151
+ CREATE TABLE IF NOT EXISTS attention_events (
7152
+ event_id TEXT PRIMARY KEY,
7153
+ schema_version TEXT NOT NULL,
7154
+ event_type TEXT NOT NULL,
7155
+ severity TEXT NOT NULL,
7156
+ occurred_at TEXT NOT NULL,
7157
+ environment TEXT NOT NULL,
7158
+ proposal_id TEXT,
7159
+ job_id TEXT,
7160
+ operation_id TEXT,
7161
+ correlation_id TEXT,
7162
+ capability TEXT,
7163
+ contract_digest TEXT,
7164
+ attention_key TEXT,
7165
+ attention_required INTEGER NOT NULL,
7166
+ immediate_default INTEGER NOT NULL,
7167
+ summary TEXT NOT NULL,
7168
+ approval_source TEXT,
7169
+ worker_state TEXT,
7170
+ failure_class TEXT,
7171
+ expires_at TEXT,
7172
+ workbench_path TEXT,
7173
+ details_json TEXT NOT NULL,
7174
+ payload_hash TEXT NOT NULL,
7175
+ created_at TEXT NOT NULL
7176
+ );
7177
+
7178
+ CREATE TABLE IF NOT EXISTS attention_items (
7179
+ attention_id TEXT PRIMARY KEY,
7180
+ attention_key TEXT NOT NULL UNIQUE,
7181
+ status TEXT NOT NULL,
7182
+ severity TEXT NOT NULL,
7183
+ environment TEXT NOT NULL,
7184
+ event_type TEXT NOT NULL,
7185
+ capability TEXT,
7186
+ contract_digest TEXT,
7187
+ title TEXT NOT NULL,
7188
+ occurrence_count INTEGER NOT NULL,
7189
+ first_event_id TEXT NOT NULL,
7190
+ latest_event_id TEXT NOT NULL,
7191
+ first_seen_at TEXT NOT NULL,
7192
+ last_seen_at TEXT NOT NULL,
7193
+ acknowledged_by TEXT,
7194
+ acknowledged_at TEXT,
7195
+ acknowledgement_identity_json TEXT,
7196
+ acknowledgement_decision_hash TEXT,
7197
+ acknowledgement_signature TEXT,
7198
+ acknowledgement_integrity_hash TEXT,
7199
+ resolved_at TEXT,
7200
+ expires_at TEXT,
7201
+ FOREIGN KEY (first_event_id) REFERENCES attention_events(event_id),
7202
+ FOREIGN KEY (latest_event_id) REFERENCES attention_events(event_id)
7203
+ );
7204
+
7205
+ CREATE TABLE IF NOT EXISTS notification_deliveries (
7206
+ delivery_id TEXT PRIMARY KEY,
7207
+ sink_id TEXT NOT NULL,
7208
+ event_id TEXT NOT NULL,
7209
+ attention_id TEXT,
7210
+ status TEXT NOT NULL,
7211
+ attempts INTEGER NOT NULL DEFAULT 0,
7212
+ max_attempts INTEGER NOT NULL,
7213
+ next_attempt_at TEXT NOT NULL,
7214
+ lease_owner TEXT,
7215
+ lease_id TEXT,
7216
+ lease_expires_at TEXT,
7217
+ last_error_code TEXT,
7218
+ external_reference TEXT,
7219
+ delivered_at TEXT,
7220
+ created_at TEXT NOT NULL,
7221
+ updated_at TEXT NOT NULL,
7222
+ UNIQUE(sink_id, event_id),
7223
+ FOREIGN KEY (event_id) REFERENCES attention_events(event_id),
7224
+ FOREIGN KEY (attention_id) REFERENCES attention_items(attention_id)
7225
+ );
7226
+
6578
7227
  CREATE TABLE IF NOT EXISTS worker_queue (
6579
7228
  proposal_id TEXT PRIMARY KEY,
6580
7229
  status TEXT NOT NULL,
7230
+ execution_mode TEXT DEFAULT 'legacy',
7231
+ contract_digest TEXT,
6581
7232
  attempts INTEGER NOT NULL DEFAULT 0,
6582
7233
  max_attempts INTEGER NOT NULL,
6583
7234
  next_attempt_at TEXT NOT NULL,
6584
7235
  lease_owner TEXT,
7236
+ lease_id TEXT,
6585
7237
  lease_expires_at TEXT,
6586
7238
  last_error_code TEXT,
7239
+ terminal_outcome TEXT,
6587
7240
  created_at TEXT NOT NULL,
6588
7241
  updated_at TEXT NOT NULL,
6589
7242
  FOREIGN KEY (proposal_id) REFERENCES proposals(proposal_id)
@@ -6605,6 +7258,12 @@ var ProposalStore = class {
6605
7258
  CREATE INDEX IF NOT EXISTS idx_cloud_outbox_due ON cloud_outbox(status, next_attempt_at, sequence, created_at);
6606
7259
  CREATE INDEX IF NOT EXISTS idx_cloud_outbox_proposal ON cloud_outbox(proposal_id, sequence, created_at);
6607
7260
  CREATE INDEX IF NOT EXISTS idx_cloud_governance_proposal ON cloud_governance_events(proposal_id, created_at);
7261
+ CREATE INDEX IF NOT EXISTS idx_attention_events_type_time ON attention_events(event_type, occurred_at, event_id);
7262
+ CREATE INDEX IF NOT EXISTS idx_attention_events_proposal ON attention_events(proposal_id, occurred_at, event_id);
7263
+ CREATE INDEX IF NOT EXISTS idx_attention_events_capability ON attention_events(capability, occurred_at, event_id);
7264
+ CREATE INDEX IF NOT EXISTS idx_attention_items_status ON attention_items(status, severity, last_seen_at, attention_id);
7265
+ CREATE INDEX IF NOT EXISTS idx_notification_deliveries_due ON notification_deliveries(status, next_attempt_at, created_at, delivery_id);
7266
+ CREATE INDEX IF NOT EXISTS idx_notification_deliveries_sink ON notification_deliveries(sink_id, status, updated_at, delivery_id);
6608
7267
 
6609
7268
  INSERT OR IGNORE INTO proposal_store_schema(version, applied_at)
6610
7269
  VALUES (1, datetime('now'));
@@ -6636,6 +7295,14 @@ var ProposalStore = class {
6636
7295
  this.ensureColumn("approvals", "signature", "TEXT");
6637
7296
  this.ensureColumn("approvals", "integrity_hash", "TEXT");
6638
7297
  this.ensureColumn("approvals", "freshness_proof_digest", "TEXT");
7298
+ this.ensureColumn("attention_items", "acknowledgement_identity_json", "TEXT");
7299
+ this.ensureColumn("attention_items", "acknowledgement_decision_hash", "TEXT");
7300
+ this.ensureColumn("attention_items", "acknowledgement_signature", "TEXT");
7301
+ this.ensureColumn("attention_items", "acknowledgement_integrity_hash", "TEXT");
7302
+ this.ensureColumn("worker_queue", "execution_mode", "TEXT DEFAULT 'legacy'");
7303
+ this.ensureColumn("worker_queue", "contract_digest", "TEXT");
7304
+ this.ensureColumn("worker_queue", "lease_id", "TEXT");
7305
+ this.ensureColumn("worker_queue", "terminal_outcome", "TEXT");
6639
7306
  }
6640
7307
  ensureSearchIndexes() {
6641
7308
  this.db.exec(`
@@ -6679,6 +7346,7 @@ var ProposalStore = class {
6679
7346
 
6680
7347
  CREATE INDEX IF NOT EXISTS idx_proposal_events_kind_created ON proposal_events(kind, created_at);
6681
7348
  CREATE INDEX IF NOT EXISTS idx_worker_queue_claim ON worker_queue(status, next_attempt_at, lease_expires_at, created_at);
7349
+ CREATE INDEX IF NOT EXISTS idx_worker_queue_supervised_claim ON worker_queue(execution_mode, contract_digest, status, next_attempt_at, lease_expires_at, created_at);
6682
7350
  `);
6683
7351
  }
6684
7352
  ensureColumn(table, column, definition) {
@@ -7058,6 +7726,7 @@ var ProposalStore = class {
7058
7726
  const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
7059
7727
  const window2 = utcDayWindow(now);
7060
7728
  const trippedLimits = [];
7729
+ const nearLimits = [];
7061
7730
  let quorumDeferred = false;
7062
7731
  this.transaction(() => {
7063
7732
  const proposal = this.requireProposal(proposalId);
@@ -7110,6 +7779,17 @@ var ProposalStore = class {
7110
7779
  window_end: window2.end,
7111
7780
  reason: `${scope} daily auto-approval count ${projected2} exceeds ${limit.max}`
7112
7781
  });
7782
+ } else if (projected2 / limit.max >= 0.8) {
7783
+ nearLimits.push({
7784
+ ...limit,
7785
+ scope,
7786
+ observed: rows.length,
7787
+ proposed: 1,
7788
+ projected: projected2,
7789
+ window_start: window2.start,
7790
+ window_end: window2.end,
7791
+ reason: `${scope} daily auto-approval count ${projected2} is approaching ${limit.max}`
7792
+ });
7113
7793
  }
7114
7794
  continue;
7115
7795
  }
@@ -7144,6 +7824,17 @@ var ProposalStore = class {
7144
7824
  window_end: window2.end,
7145
7825
  reason: invalidHistory || !field || typeof proposed !== "number" || !Number.isSafeInteger(proposed) ? `${scope} daily auto-approval total could not be verified safely${field ? ` for ${field}` : ""}` : `${scope} daily auto-approval total ${projected} for ${field} exceeds ${limit.max}`
7146
7826
  });
7827
+ } else if (projected / limit.max >= 0.8) {
7828
+ nearLimits.push({
7829
+ ...limit,
7830
+ scope,
7831
+ observed,
7832
+ proposed: proposedNumber,
7833
+ projected,
7834
+ window_start: window2.start,
7835
+ window_end: window2.end,
7836
+ reason: `${scope} daily auto-approval total is approaching its reviewed maximum`
7837
+ });
7147
7838
  }
7148
7839
  }
7149
7840
  if (trippedLimits.length > 0) {
@@ -7180,6 +7871,19 @@ var ProposalStore = class {
7180
7871
  aggregate_limits: options.limits ?? [],
7181
7872
  freshness_proof_digest: options.freshness_proof_digest ?? null
7182
7873
  });
7874
+ for (const near of nearLimits) {
7875
+ this.appendEvent(proposalId, "policy_limit_near", actor, {
7876
+ policy: options.policy,
7877
+ kind: near.kind,
7878
+ scope: near.scope,
7879
+ observed: near.observed,
7880
+ proposed: near.proposed,
7881
+ projected: near.projected,
7882
+ max: near.max,
7883
+ window_start: near.window_start,
7884
+ window_end: near.window_end
7885
+ });
7886
+ }
7183
7887
  });
7184
7888
  return {
7185
7889
  proposal: this.requireProposal(proposalId),
@@ -7489,6 +8193,30 @@ var ProposalStore = class {
7489
8193
  decision_hash: input.identity?.decision_hash
7490
8194
  });
7491
8195
  this.recordExecutionReceiptRows(receipt, proposal);
8196
+ const workerItem = this.workerQueueItem(intent.proposal_id);
8197
+ if (workerItem?.status === "reconciliation_required") {
8198
+ const queueStatus = receipt.status === "failed" ? "dead_letter" : "completed";
8199
+ const terminalOutcome = receipt.status === "failed" ? "dead_letter" : receipt.status;
8200
+ this.db.prepare(`
8201
+ UPDATE worker_queue
8202
+ SET status = ?, lease_owner = NULL, lease_id = NULL,
8203
+ lease_expires_at = NULL, last_error_code = ?,
8204
+ terminal_outcome = ?, updated_at = ?
8205
+ WHERE proposal_id = ? AND status = 'reconciliation_required'
8206
+ `).run(
8207
+ queueStatus,
8208
+ receipt.status === "failed" ? "RECONCILED_FAILED" : null,
8209
+ terminalOutcome,
8210
+ receipt.executed_at,
8211
+ intent.proposal_id
8212
+ );
8213
+ this.appendEvent(intent.proposal_id, "writeback_worker_reconciled", input.actor, {
8214
+ intent_id: intent.intent_id,
8215
+ outcome: receipt.status,
8216
+ queue_status: queueStatus,
8217
+ source_database_changed: false
8218
+ });
8219
+ }
7492
8220
  });
7493
8221
  return this.requireWritebackIntent(intent.intent_id);
7494
8222
  }
@@ -7817,6 +8545,78 @@ var ProposalStore = class {
7817
8545
  const rows = this.db.prepare(query.sql).all(...query.params);
7818
8546
  return rows.map(rowToEvent).filter((event) => event !== void 0);
7819
8547
  }
8548
+ enqueueWorkerProposal(options) {
8549
+ const proposal = this.requireProposal(options.proposal_id);
8550
+ if (proposal.state !== "approved" && proposal.state !== "pending_worker") {
8551
+ throw new ProposalStoreError(
8552
+ "WORKER_PROPOSAL_NOT_APPROVED",
8553
+ `proposal ${proposal.proposal_id} is ${proposal.state}, not approved for worker execution`
8554
+ );
8555
+ }
8556
+ const executionMode = options.execution_mode ?? "legacy";
8557
+ const contractDigest = options.contract_digest;
8558
+ if (executionMode === "supervised_worker" && !contractDigest) {
8559
+ throw new ProposalStoreError(
8560
+ "SUPERVISED_WORKER_DIGEST_REQUIRED",
8561
+ `supervised worker queue item ${proposal.proposal_id} requires an exact contract digest`
8562
+ );
8563
+ }
8564
+ if (contractDigest && !/^sha256:[a-f0-9]{64}$/.test(contractDigest)) {
8565
+ throw new ProposalStoreError("WORKER_CONTRACT_DIGEST_INVALID", "worker queue contract digest must be a full lowercase sha256 digest");
8566
+ }
8567
+ const maxAttempts = Math.max(1, Math.min(options.max_attempts ?? 5, 100));
8568
+ const queueLimit = Math.max(1, Math.min(options.queue_limit ?? 1e4, 1e5));
8569
+ const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
8570
+ this.transaction(() => {
8571
+ const existing = this.workerQueueItem(proposal.proposal_id);
8572
+ if (existing) {
8573
+ if (existing.execution_mode !== executionMode || existing.contract_digest !== contractDigest) {
8574
+ throw new ProposalStoreError(
8575
+ "WORKER_QUEUE_AUTHORITY_MISMATCH",
8576
+ `worker queue item ${proposal.proposal_id} already exists under different execution authority`
8577
+ );
8578
+ }
8579
+ return;
8580
+ }
8581
+ const active = this.db.prepare(`
8582
+ SELECT COUNT(*) AS count
8583
+ FROM worker_queue q
8584
+ JOIN proposals p ON p.proposal_id = q.proposal_id
8585
+ WHERE q.execution_mode = ?
8586
+ AND (? IS NULL OR q.contract_digest = ?)
8587
+ AND p.action = ?
8588
+ AND q.status IN ('queued', 'leased', 'retry_wait', 'blocked', 'reconciliation_required')
8589
+ `).get(executionMode, contractDigest ?? null, contractDigest ?? null, proposal.action);
8590
+ if (isRecord3(active) && Number(active.count ?? 0) >= queueLimit) {
8591
+ throw new ProposalStoreError(
8592
+ "WORKER_QUEUE_LIMIT_EXCEEDED",
8593
+ `worker queue limit ${queueLimit} reached for ${proposal.action}`
8594
+ );
8595
+ }
8596
+ this.db.prepare(`
8597
+ INSERT INTO worker_queue (
8598
+ proposal_id, status, execution_mode, contract_digest, attempts,
8599
+ max_attempts, next_attempt_at, lease_owner, lease_id,
8600
+ lease_expires_at, last_error_code, terminal_outcome, created_at,
8601
+ updated_at
8602
+ ) VALUES (?, 'queued', ?, ?, 0, ?, ?, NULL, NULL, NULL, NULL, NULL, ?, ?)
8603
+ `).run(
8604
+ proposal.proposal_id,
8605
+ executionMode,
8606
+ contractDigest ?? null,
8607
+ maxAttempts,
8608
+ now,
8609
+ now,
8610
+ now
8611
+ );
8612
+ this.appendEvent(proposal.proposal_id, "writeback_worker_queued", "runner", {
8613
+ execution_mode: executionMode,
8614
+ contract_digest: contractDigest ?? null,
8615
+ max_attempts: maxAttempts
8616
+ });
8617
+ });
8618
+ return this.requireWorkerQueueItem(proposal.proposal_id);
8619
+ }
7820
8620
  enqueueApprovedForWorker(options = {}) {
7821
8621
  const maxAttempts = Math.max(1, Math.min(options.maxAttempts ?? 5, 100));
7822
8622
  const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
@@ -7828,9 +8628,11 @@ var ProposalStore = class {
7828
8628
  for (const proposal of proposals) {
7829
8629
  this.db.prepare(`
7830
8630
  INSERT OR IGNORE INTO worker_queue (
7831
- proposal_id, status, attempts, max_attempts, next_attempt_at,
7832
- lease_owner, lease_expires_at, last_error_code, created_at, updated_at
7833
- ) VALUES (?, 'queued', 0, ?, ?, NULL, NULL, NULL, ?, ?)
8631
+ proposal_id, status, execution_mode, contract_digest, attempts,
8632
+ max_attempts, next_attempt_at, lease_owner, lease_id,
8633
+ lease_expires_at, last_error_code, terminal_outcome, created_at,
8634
+ updated_at
8635
+ ) VALUES (?, 'queued', 'legacy', NULL, 0, ?, ?, NULL, NULL, NULL, NULL, NULL, ?, ?)
7834
8636
  `).run(proposal.proposal_id, maxAttempts, now, now, now);
7835
8637
  }
7836
8638
  });
@@ -7842,67 +8644,358 @@ var ProposalStore = class {
7842
8644
  const leaseExpiresAt = new Date(Date.parse(now) + leaseSeconds * 1e3).toISOString();
7843
8645
  let claimed;
7844
8646
  this.transaction(() => {
7845
- const raw = this.db.prepare(`
7846
- SELECT q.*
7847
- FROM worker_queue q
8647
+ if (options.maxConcurrent !== void 0) {
8648
+ const maximum = Math.max(1, Math.min(options.maxConcurrent, 32));
8649
+ const active = this.db.prepare(`
8650
+ SELECT COUNT(*) AS count
8651
+ FROM worker_queue q
8652
+ JOIN proposals p ON p.proposal_id = q.proposal_id
8653
+ WHERE q.status = 'leased'
8654
+ AND q.lease_expires_at > ?
8655
+ AND (? IS NULL OR q.execution_mode = ?)
8656
+ AND (? IS NULL OR p.action = ?)
8657
+ AND (? IS NULL OR p.tenant_id = ?)
8658
+ AND (? IS NULL OR q.contract_digest = ?)
8659
+ `).get(
8660
+ now,
8661
+ options.executionMode ?? null,
8662
+ options.executionMode ?? null,
8663
+ options.capability ?? null,
8664
+ options.capability ?? null,
8665
+ options.tenant ?? null,
8666
+ options.tenant ?? null,
8667
+ options.contractDigest ?? null,
8668
+ options.contractDigest ?? null
8669
+ );
8670
+ if (isRecord3(active) && Number(active.count ?? 0) >= maximum) return;
8671
+ }
8672
+ if (options.rateLimit) {
8673
+ const executions = Math.max(1, Math.min(options.rateLimit.executions, 1e5));
8674
+ const windowSeconds = Math.max(1, Math.min(options.rateLimit.windowSeconds, 86400));
8675
+ const windowStart = new Date(Date.parse(now) - windowSeconds * 1e3).toISOString();
8676
+ const recent = this.db.prepare(`
8677
+ SELECT COUNT(*) AS count
8678
+ FROM worker_queue q
8679
+ JOIN proposals p ON p.proposal_id = q.proposal_id
8680
+ WHERE q.status = 'completed'
8681
+ AND q.updated_at >= ?
8682
+ AND (? IS NULL OR q.execution_mode = ?)
8683
+ AND (? IS NULL OR p.action = ?)
8684
+ AND (? IS NULL OR p.tenant_id = ?)
8685
+ AND (? IS NULL OR q.contract_digest = ?)
8686
+ `).get(
8687
+ windowStart,
8688
+ options.executionMode ?? null,
8689
+ options.executionMode ?? null,
8690
+ options.capability ?? null,
8691
+ options.capability ?? null,
8692
+ options.tenant ?? null,
8693
+ options.tenant ?? null,
8694
+ options.contractDigest ?? null,
8695
+ options.contractDigest ?? null
8696
+ );
8697
+ if (isRecord3(recent) && Number(recent.count ?? 0) >= executions) return;
8698
+ }
8699
+ const raw = this.db.prepare(`
8700
+ SELECT q.*
8701
+ FROM worker_queue q
7848
8702
  JOIN proposals p ON p.proposal_id = q.proposal_id
7849
8703
  WHERE (
7850
8704
  (q.status IN ('queued', 'retry_wait') AND q.next_attempt_at <= ?)
7851
8705
  OR (q.status = 'leased' AND q.lease_expires_at <= ?)
7852
8706
  )
7853
8707
  AND p.state IN ('approved', 'pending_worker', 'failed')
8708
+ AND (? IS NULL OR q.execution_mode = ?)
8709
+ AND (? IS NULL OR p.action = ?)
8710
+ AND (? IS NULL OR p.tenant_id = ?)
8711
+ AND (? IS NULL OR q.contract_digest = ?)
7854
8712
  ORDER BY q.next_attempt_at ASC, q.created_at ASC
7855
8713
  LIMIT 1
7856
- `).get(now, now);
8714
+ `).get(
8715
+ now,
8716
+ now,
8717
+ options.executionMode ?? null,
8718
+ options.executionMode ?? null,
8719
+ options.capability ?? null,
8720
+ options.capability ?? null,
8721
+ options.tenant ?? null,
8722
+ options.tenant ?? null,
8723
+ options.contractDigest ?? null,
8724
+ options.contractDigest ?? null
8725
+ );
7857
8726
  const item = rowToWorkerQueueItem(raw);
7858
8727
  if (!item) return;
8728
+ const proposal = this.requireProposal(item.proposal_id);
8729
+ if (options.proposalTtlSeconds !== void 0) {
8730
+ const ttlSeconds = Math.max(60, Math.min(options.proposalTtlSeconds, 2592e3));
8731
+ const expiresAt = Date.parse(proposal.created_at) + ttlSeconds * 1e3;
8732
+ if (!Number.isFinite(expiresAt) || expiresAt <= Date.parse(now)) {
8733
+ this.blockQueuedWorkerItem(item, options.workerId, "SUPERVISED_WORKER_PROPOSAL_EXPIRED", now);
8734
+ return;
8735
+ }
8736
+ }
8737
+ if (options.policyExecution) {
8738
+ const trips = this.workerPolicyExecutionLimitTrips({
8739
+ proposal,
8740
+ policy: options.policyExecution.policy,
8741
+ limits: options.policyExecution.limits,
8742
+ now
8743
+ });
8744
+ if (trips.length > 0) {
8745
+ this.blockQueuedWorkerItem(
8746
+ item,
8747
+ options.workerId,
8748
+ "SUPERVISED_WORKER_POLICY_LIMIT_EXCEEDED",
8749
+ now,
8750
+ { policy: options.policyExecution.policy, tripped_limits: trips }
8751
+ );
8752
+ return;
8753
+ }
8754
+ }
8755
+ const leaseId = workerLeaseId(item.proposal_id, options.workerId, item.attempts + 1, now);
7859
8756
  this.db.prepare(`
7860
8757
  UPDATE worker_queue
7861
8758
  SET status = 'leased', attempts = attempts + 1, lease_owner = ?,
7862
- lease_expires_at = ?, updated_at = ?
8759
+ lease_id = ?, lease_expires_at = ?, last_error_code = NULL,
8760
+ terminal_outcome = NULL, updated_at = ?
7863
8761
  WHERE proposal_id = ?
7864
- `).run(options.workerId, leaseExpiresAt, now, item.proposal_id);
7865
- const proposal = this.requireProposal(item.proposal_id);
8762
+ `).run(options.workerId, leaseId, leaseExpiresAt, now, item.proposal_id);
7866
8763
  if (proposal.state === "failed") {
7867
8764
  this.db.prepare("UPDATE proposals SET state = 'pending_worker', updated_at = ? WHERE proposal_id = ?").run(now, item.proposal_id);
7868
8765
  }
7869
8766
  this.appendEvent(item.proposal_id, "writeback_worker_claimed", options.workerId, {
7870
8767
  attempt: item.attempts + 1,
7871
8768
  max_attempts: item.max_attempts,
8769
+ execution_mode: item.execution_mode,
8770
+ contract_digest: item.contract_digest ?? null,
8771
+ lease_id: leaseId,
7872
8772
  lease_expires_at: leaseExpiresAt
7873
8773
  });
7874
8774
  claimed = this.workerQueueItem(item.proposal_id);
7875
8775
  });
7876
8776
  return claimed;
7877
8777
  }
7878
- completeWorkerItem(proposalId, workerId, outcome, now = (/* @__PURE__ */ new Date()).toISOString()) {
8778
+ assertWorkerPolicyExecutionLimits(input) {
8779
+ const proposal = this.requireProposal(input.proposalId);
8780
+ const trips = this.workerPolicyExecutionLimitTrips({
8781
+ proposal,
8782
+ policy: input.policy,
8783
+ limits: input.limits,
8784
+ now: input.now ?? (/* @__PURE__ */ new Date()).toISOString()
8785
+ });
8786
+ if (trips.length > 0) {
8787
+ throw new ProposalStoreError(
8788
+ "SUPERVISED_WORKER_POLICY_LIMIT_EXCEEDED",
8789
+ `execution-time policy limit no longer permits proposal ${proposal.proposal_id}`
8790
+ );
8791
+ }
8792
+ }
8793
+ blockQueuedWorkerItem(item, actor, errorCode, now, payload = {}) {
8794
+ this.db.prepare(`
8795
+ UPDATE worker_queue
8796
+ SET status = 'blocked', lease_owner = NULL, lease_id = NULL,
8797
+ lease_expires_at = NULL, last_error_code = ?,
8798
+ terminal_outcome = NULL, updated_at = ?
8799
+ WHERE proposal_id = ?
8800
+ `).run(errorCode, now, item.proposal_id);
8801
+ this.appendEvent(item.proposal_id, "writeback_worker_blocked", actor, {
8802
+ error_code: errorCode,
8803
+ execution_mode: item.execution_mode,
8804
+ contract_digest: item.contract_digest ?? null,
8805
+ ...payload
8806
+ });
8807
+ }
8808
+ workerPolicyExecutionLimitTrips(input) {
8809
+ if (input.limits.length === 0) return [];
8810
+ const actor = `policy:${input.policy}`;
8811
+ const candidateApproval = this.db.prepare(`
8812
+ SELECT approval_id
8813
+ FROM approvals
8814
+ WHERE proposal_id = ?
8815
+ AND approver = ?
8816
+ AND status = 'approved'
8817
+ AND proposal_hash = ?
8818
+ AND proposal_version = ?
8819
+ LIMIT 1
8820
+ `).get(
8821
+ input.proposal.proposal_id,
8822
+ actor,
8823
+ input.proposal.proposal_hash,
8824
+ input.proposal.proposal_version
8825
+ );
8826
+ if (!isRecord3(candidateApproval)) return [];
8827
+ const window2 = utcDayWindow(input.now);
8828
+ const rows = this.db.prepare(`
8829
+ SELECT DISTINCT
8830
+ p.proposal_id,
8831
+ p.business_object,
8832
+ p.object_id,
8833
+ p.change_set_json
8834
+ FROM worker_queue q
8835
+ JOIN proposals p ON p.proposal_id = q.proposal_id
8836
+ JOIN approvals a ON a.proposal_id = p.proposal_id
8837
+ WHERE a.approver = ?
8838
+ AND a.status = 'approved'
8839
+ AND p.tenant_id = ?
8840
+ AND (
8841
+ (q.status = 'leased' AND q.lease_expires_at > ?)
8842
+ OR (
8843
+ q.status = 'completed'
8844
+ AND q.terminal_outcome IN ('applied', 'already_applied')
8845
+ AND q.updated_at >= ?
8846
+ AND q.updated_at < ?
8847
+ )
8848
+ OR (
8849
+ q.status = 'reconciliation_required'
8850
+ AND q.updated_at >= ?
8851
+ AND q.updated_at < ?
8852
+ )
8853
+ OR (
8854
+ p.state = 'applied'
8855
+ AND p.updated_at >= ?
8856
+ AND p.updated_at < ?
8857
+ )
8858
+ )
8859
+ `).all(
8860
+ actor,
8861
+ input.proposal.tenant_id,
8862
+ input.now,
8863
+ window2.start,
8864
+ window2.end,
8865
+ window2.start,
8866
+ window2.end,
8867
+ window2.start,
8868
+ window2.end
8869
+ );
8870
+ const active = /* @__PURE__ */ new Map();
8871
+ let invalidHistory = false;
8872
+ for (const row of rows) {
8873
+ if (!isRecord3(row)) continue;
8874
+ try {
8875
+ const proposalId = String(row.proposal_id);
8876
+ active.set(proposalId, {
8877
+ proposal_id: proposalId,
8878
+ business_object: String(row.business_object),
8879
+ object_id: String(row.object_id),
8880
+ change_set: parseChangeSet(JSON.parse(String(row.change_set_json)))
8881
+ });
8882
+ } catch {
8883
+ invalidHistory = true;
8884
+ active.set(String(row.proposal_id), {
8885
+ proposal_id: String(row.proposal_id),
8886
+ business_object: String(row.business_object),
8887
+ object_id: String(row.object_id),
8888
+ change_set: input.proposal.change_set
8889
+ });
8890
+ }
8891
+ }
8892
+ active.set(input.proposal.proposal_id, {
8893
+ proposal_id: input.proposal.proposal_id,
8894
+ business_object: input.proposal.business_object,
8895
+ object_id: input.proposal.object_id,
8896
+ change_set: input.proposal.change_set
8897
+ });
8898
+ const trips = [];
8899
+ for (const limit of input.limits) {
8900
+ const scope = limit.scope ?? "tenant_policy";
8901
+ const scoped = [...active.values()].filter((proposal) => scope !== "tenant_policy_object" || proposal.business_object === input.proposal.business_object && proposal.object_id === input.proposal.object_id);
8902
+ if (limit.kind === "count") {
8903
+ const projected2 = scoped.length;
8904
+ if (projected2 > limit.max) {
8905
+ trips.push({
8906
+ ...limit,
8907
+ scope,
8908
+ observed: Math.max(0, projected2 - 1),
8909
+ proposed: 1,
8910
+ projected: projected2,
8911
+ window_start: window2.start,
8912
+ window_end: window2.end,
8913
+ reason: `${scope} execution count ${projected2} exceeds ${limit.max}`
8914
+ });
8915
+ }
8916
+ continue;
8917
+ }
8918
+ const field = limit.field;
8919
+ let projected = 0;
8920
+ let proposed = 0;
8921
+ let invalid = !field || invalidHistory;
8922
+ for (const proposal of scoped) {
8923
+ const value = field ? proposal.change_set.patch[field] : void 0;
8924
+ if (typeof value !== "number" || !Number.isSafeInteger(value)) {
8925
+ invalid = true;
8926
+ continue;
8927
+ }
8928
+ projected += value;
8929
+ if (proposal.proposal_id === input.proposal.proposal_id) proposed = value;
8930
+ }
8931
+ if (invalid || projected > limit.max) {
8932
+ trips.push({
8933
+ ...limit,
8934
+ scope,
8935
+ observed: projected - proposed,
8936
+ proposed,
8937
+ projected,
8938
+ window_start: window2.start,
8939
+ window_end: window2.end,
8940
+ reason: invalid ? `${scope} execution total could not be verified safely${field ? ` for ${field}` : ""}` : `${scope} execution total ${projected} for ${field} exceeds ${limit.max}`
8941
+ });
8942
+ }
8943
+ }
8944
+ return trips;
8945
+ }
8946
+ assertActiveWorkerLease(options) {
8947
+ const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
8948
+ const item = this.assertWorkerLease(options.proposalId, options.workerId, options.leaseId);
8949
+ if (!item.lease_expires_at || Date.parse(item.lease_expires_at) <= Date.parse(now)) {
8950
+ throw new ProposalStoreError("WORKER_LEASE_EXPIRED", `worker lease ${options.leaseId} for ${options.proposalId} has expired`);
8951
+ }
8952
+ return item;
8953
+ }
8954
+ renewWorkerLease(options) {
8955
+ const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
8956
+ const leaseSeconds = Math.max(15, Math.min(options.leaseSeconds ?? 60, 3600));
8957
+ const leaseExpiresAt = new Date(Date.parse(now) + leaseSeconds * 1e3).toISOString();
8958
+ this.transaction(() => {
8959
+ this.assertActiveWorkerLease({ ...options, now });
8960
+ this.db.prepare(`
8961
+ UPDATE worker_queue
8962
+ SET lease_expires_at = ?, updated_at = ?
8963
+ WHERE proposal_id = ? AND status = 'leased' AND lease_owner = ? AND lease_id = ?
8964
+ `).run(leaseExpiresAt, now, options.proposalId, options.workerId, options.leaseId);
8965
+ });
8966
+ return this.requireWorkerQueueItem(options.proposalId);
8967
+ }
8968
+ completeWorkerItem(proposalId, workerId, outcome, now = (/* @__PURE__ */ new Date()).toISOString(), leaseId) {
7879
8969
  this.transaction(() => {
7880
- this.assertWorkerLease(proposalId, workerId);
8970
+ this.assertWorkerLease(proposalId, workerId, leaseId);
7881
8971
  this.db.prepare(`
7882
8972
  UPDATE worker_queue
7883
- SET status = 'completed', lease_owner = NULL, lease_expires_at = NULL,
7884
- last_error_code = NULL, updated_at = ?
8973
+ SET status = 'completed', lease_owner = NULL, lease_id = NULL,
8974
+ lease_expires_at = NULL, last_error_code = NULL,
8975
+ terminal_outcome = ?, updated_at = ?
7885
8976
  WHERE proposal_id = ?
7886
- `).run(now, proposalId);
7887
- this.appendEvent(proposalId, "writeback_worker_completed", workerId, { outcome });
8977
+ `).run(outcome, now, proposalId);
8978
+ this.appendEvent(proposalId, "writeback_worker_completed", workerId, { outcome, lease_id: leaseId ?? null });
7888
8979
  });
7889
8980
  return this.requireWorkerQueueItem(proposalId);
7890
8981
  }
7891
8982
  retryWorkerItem(options) {
7892
8983
  const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
7893
8984
  this.transaction(() => {
7894
- const item = this.assertWorkerLease(options.proposalId, options.workerId);
8985
+ const item = this.assertWorkerLease(options.proposalId, options.workerId, options.leaseId);
7895
8986
  const deadLetter = item.attempts >= item.max_attempts;
7896
8987
  this.db.prepare(`
7897
8988
  UPDATE worker_queue
7898
- SET status = ?, next_attempt_at = ?, lease_owner = NULL,
7899
- lease_expires_at = NULL, last_error_code = ?, updated_at = ?
8989
+ SET status = ?, next_attempt_at = ?, lease_owner = NULL, lease_id = NULL,
8990
+ lease_expires_at = NULL, last_error_code = ?, terminal_outcome = NULL,
8991
+ updated_at = ?
7900
8992
  WHERE proposal_id = ?
7901
8993
  `).run(deadLetter ? "dead_letter" : "retry_wait", options.retryAt, options.errorCode, now, options.proposalId);
7902
8994
  this.appendEvent(options.proposalId, deadLetter ? "writeback_dead_lettered" : "writeback_retry_scheduled", options.workerId, {
7903
8995
  attempt: item.attempts,
7904
8996
  max_attempts: item.max_attempts,
7905
8997
  error_code: options.errorCode,
8998
+ lease_id: options.leaseId ?? null,
7906
8999
  ...deadLetter ? {} : { retry_at: options.retryAt }
7907
9000
  });
7908
9001
  });
@@ -7911,17 +9004,95 @@ var ProposalStore = class {
7911
9004
  deadLetterWorkerItem(options) {
7912
9005
  const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
7913
9006
  this.transaction(() => {
7914
- const item = this.assertWorkerLease(options.proposalId, options.workerId);
9007
+ const item = this.assertWorkerLease(options.proposalId, options.workerId, options.leaseId);
7915
9008
  this.db.prepare(`
7916
9009
  UPDATE worker_queue
7917
- SET status = 'dead_letter', lease_owner = NULL, lease_expires_at = NULL,
7918
- last_error_code = ?, updated_at = ?
9010
+ SET status = 'dead_letter', lease_owner = NULL, lease_id = NULL,
9011
+ lease_expires_at = NULL, last_error_code = ?,
9012
+ terminal_outcome = 'dead_letter', updated_at = ?
7919
9013
  WHERE proposal_id = ?
7920
9014
  `).run(options.errorCode, now, options.proposalId);
7921
9015
  this.appendEvent(options.proposalId, "writeback_dead_lettered", options.workerId, {
7922
9016
  attempt: item.attempts,
7923
9017
  max_attempts: item.max_attempts,
7924
- error_code: options.errorCode
9018
+ error_code: options.errorCode,
9019
+ lease_id: options.leaseId ?? null
9020
+ });
9021
+ });
9022
+ return this.requireWorkerQueueItem(options.proposalId);
9023
+ }
9024
+ blockWorkerItem(options) {
9025
+ const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
9026
+ this.transaction(() => {
9027
+ this.assertWorkerLease(options.proposalId, options.workerId, options.leaseId);
9028
+ this.db.prepare(`
9029
+ UPDATE worker_queue
9030
+ SET status = 'blocked', lease_owner = NULL, lease_id = NULL,
9031
+ lease_expires_at = NULL, last_error_code = ?,
9032
+ terminal_outcome = 'blocked', updated_at = ?
9033
+ WHERE proposal_id = ?
9034
+ `).run(options.errorCode, now, options.proposalId);
9035
+ this.appendEvent(options.proposalId, "writeback_worker_blocked", options.workerId, {
9036
+ error_code: options.errorCode,
9037
+ lease_id: options.leaseId
9038
+ });
9039
+ });
9040
+ return this.requireWorkerQueueItem(options.proposalId);
9041
+ }
9042
+ requireWorkerReconciliation(options) {
9043
+ const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
9044
+ this.transaction(() => {
9045
+ this.assertWorkerLease(options.proposalId, options.workerId, options.leaseId);
9046
+ this.db.prepare(`
9047
+ UPDATE worker_queue
9048
+ SET status = 'reconciliation_required', lease_owner = NULL,
9049
+ lease_id = NULL, lease_expires_at = NULL, last_error_code = ?,
9050
+ terminal_outcome = 'reconciliation_required', updated_at = ?
9051
+ WHERE proposal_id = ?
9052
+ `).run(options.errorCode, now, options.proposalId);
9053
+ this.appendEvent(options.proposalId, "writeback_reconciliation_required", options.workerId, {
9054
+ safe_error_code: options.errorCode,
9055
+ lease_id: options.leaseId
9056
+ });
9057
+ });
9058
+ return this.requireWorkerQueueItem(options.proposalId);
9059
+ }
9060
+ cancelWorkerItem(options) {
9061
+ const now = options.now ?? (/* @__PURE__ */ new Date()).toISOString();
9062
+ const proposal = this.requireProposal(options.proposalId);
9063
+ assertOperatorDecision(
9064
+ proposal,
9065
+ "worker_cancel",
9066
+ options.actor,
9067
+ options.identity,
9068
+ options.require_verified_identity === true
9069
+ );
9070
+ this.transaction(() => {
9071
+ const item = this.requireWorkerQueueItem(options.proposalId);
9072
+ if (item.status !== "queued" && item.status !== "retry_wait") {
9073
+ throw new ProposalStoreError(
9074
+ "WORKER_ITEM_NOT_CANCELLABLE",
9075
+ `worker queue item ${options.proposalId} is ${item.status}, not safely cancellable before lease`
9076
+ );
9077
+ }
9078
+ this.db.prepare(`
9079
+ UPDATE worker_queue
9080
+ SET status = 'cancelled', lease_owner = NULL, lease_id = NULL,
9081
+ lease_expires_at = NULL, terminal_outcome = 'cancelled',
9082
+ updated_at = ?
9083
+ WHERE proposal_id = ?
9084
+ `).run(now, options.proposalId);
9085
+ if (proposal.state === "approved" || proposal.state === "pending_worker") {
9086
+ this.db.prepare(`
9087
+ UPDATE proposals
9088
+ SET state = 'canceled', updated_at = ?
9089
+ WHERE proposal_id = ?
9090
+ `).run(now, options.proposalId);
9091
+ }
9092
+ this.appendEvent(options.proposalId, "writeback_canceled", options.actor, {
9093
+ execution_mode: item.execution_mode,
9094
+ contract_digest: item.contract_digest ?? null,
9095
+ identity: options.identity ? publicIdentitySummary(options.identity) : null
7925
9096
  });
7926
9097
  });
7927
9098
  return this.requireWorkerQueueItem(options.proposalId);
@@ -7950,7 +9121,8 @@ var ProposalStore = class {
7950
9121
  this.db.prepare(`
7951
9122
  UPDATE worker_queue
7952
9123
  SET status = 'queued', attempts = 0, max_attempts = ?, next_attempt_at = ?,
7953
- lease_owner = NULL, lease_expires_at = NULL, last_error_code = NULL, updated_at = ?
9124
+ lease_owner = NULL, lease_id = NULL, lease_expires_at = NULL,
9125
+ last_error_code = NULL, terminal_outcome = NULL, updated_at = ?
7954
9126
  WHERE proposal_id = ?
7955
9127
  `).run(retryBudget, now, now, options.proposalId);
7956
9128
  if (proposal.state === "failed") {
@@ -7975,7 +9147,8 @@ var ProposalStore = class {
7975
9147
  }
7976
9148
  this.db.prepare(`
7977
9149
  UPDATE worker_queue
7978
- SET status = 'discarded', lease_owner = NULL, lease_expires_at = NULL, updated_at = ?
9150
+ SET status = 'discarded', lease_owner = NULL, lease_id = NULL,
9151
+ lease_expires_at = NULL, terminal_outcome = 'discarded', updated_at = ?
7979
9152
  WHERE proposal_id = ?
7980
9153
  `).run(now, options.proposalId);
7981
9154
  this.appendEvent(options.proposalId, "writeback_dead_letter_discarded", options.identity.subject, {
@@ -7993,9 +9166,9 @@ var ProposalStore = class {
7993
9166
  if (!item) throw new ProposalStoreError("WORKER_ITEM_NOT_FOUND", `worker queue item not found for ${proposalId}`);
7994
9167
  return item;
7995
9168
  }
7996
- assertWorkerLease(proposalId, workerId) {
9169
+ assertWorkerLease(proposalId, workerId, leaseId) {
7997
9170
  const item = this.requireWorkerQueueItem(proposalId);
7998
- if (item.status !== "leased" || item.lease_owner !== workerId) {
9171
+ if (item.status !== "leased" || item.lease_owner !== workerId || leaseId !== void 0 && item.lease_id !== leaseId) {
7999
9172
  throw new ProposalStoreError("WORKER_LEASE_MISMATCH", `worker ${workerId} does not hold the lease for ${proposalId}`);
8000
9173
  }
8001
9174
  return item;
@@ -8258,6 +9431,642 @@ var ProposalStore = class {
8258
9431
  if (!recommendation) throw new ProposalStoreError("POLICY_RECOMMENDATION_NOT_FOUND", `policy recommendation not found: ${recommendationId}`);
8259
9432
  return recommendation;
8260
9433
  }
9434
+ recordAttentionEvent(input) {
9435
+ let event;
9436
+ const record = () => {
9437
+ event = this.recordAttentionEventInternal(input);
9438
+ };
9439
+ if (this.db.isTransaction) record();
9440
+ else this.transaction(record);
9441
+ if (!event) throw new ProposalStoreError("ATTENTION_EVENT_CREATE_FAILED", "attention event was not persisted");
9442
+ return event;
9443
+ }
9444
+ listAttentionEvents(filters = {}) {
9445
+ const clauses = [];
9446
+ const params = [];
9447
+ if (filters.event_type) {
9448
+ clauses.push("event_type = ?");
9449
+ params.push(filters.event_type);
9450
+ }
9451
+ if (filters.severity) {
9452
+ clauses.push("severity = ?");
9453
+ params.push(filters.severity);
9454
+ }
9455
+ if (filters.proposal_id) {
9456
+ clauses.push("proposal_id = ?");
9457
+ params.push(filters.proposal_id);
9458
+ }
9459
+ if (filters.capability) {
9460
+ clauses.push("capability = ?");
9461
+ params.push(filters.capability);
9462
+ }
9463
+ if (filters.tenant) {
9464
+ clauses.push("EXISTS (SELECT 1 FROM proposals p WHERE p.proposal_id = attention_events.proposal_id AND p.tenant_id = ?)");
9465
+ params.push(filters.tenant);
9466
+ }
9467
+ if (filters.principal) {
9468
+ clauses.push("EXISTS (SELECT 1 FROM proposals p WHERE p.proposal_id = attention_events.proposal_id AND p.principal = ?)");
9469
+ params.push(filters.principal);
9470
+ }
9471
+ if (filters.from) {
9472
+ clauses.push("occurred_at >= ?");
9473
+ params.push(filters.from);
9474
+ }
9475
+ const limit = Math.max(1, Math.min(filters.limit ?? 200, 1e3));
9476
+ const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
9477
+ return this.db.prepare(`
9478
+ SELECT *
9479
+ FROM attention_events
9480
+ ${where}
9481
+ ORDER BY occurred_at DESC, event_id DESC
9482
+ LIMIT ?
9483
+ `).all(...params, limit).map(rowToAttentionEvent).filter((event) => event !== void 0);
9484
+ }
9485
+ getAttentionEvent(eventId) {
9486
+ return rowToAttentionEvent(
9487
+ this.db.prepare("SELECT * FROM attention_events WHERE event_id = ?").get(eventId)
9488
+ );
9489
+ }
9490
+ listAttentionItems(filters = {}) {
9491
+ const clauses = [];
9492
+ const params = [];
9493
+ if (filters.status) {
9494
+ clauses.push("status = ?");
9495
+ params.push(filters.status);
9496
+ }
9497
+ if (filters.severity) {
9498
+ clauses.push("severity = ?");
9499
+ params.push(filters.severity);
9500
+ }
9501
+ if (filters.capability) {
9502
+ clauses.push("capability = ?");
9503
+ params.push(filters.capability);
9504
+ }
9505
+ if (filters.tenant) {
9506
+ clauses.push(`EXISTS (
9507
+ SELECT 1
9508
+ FROM attention_events ae
9509
+ JOIN proposals p ON p.proposal_id = ae.proposal_id
9510
+ WHERE ae.attention_key = attention_items.attention_key
9511
+ AND p.tenant_id = ?
9512
+ )`);
9513
+ params.push(filters.tenant);
9514
+ }
9515
+ if (filters.principal) {
9516
+ clauses.push(`EXISTS (
9517
+ SELECT 1
9518
+ FROM attention_events ae
9519
+ JOIN proposals p ON p.proposal_id = ae.proposal_id
9520
+ WHERE ae.attention_key = attention_items.attention_key
9521
+ AND p.principal = ?
9522
+ )`);
9523
+ params.push(filters.principal);
9524
+ }
9525
+ const limit = Math.max(1, Math.min(filters.limit ?? 200, 1e3));
9526
+ const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
9527
+ return this.db.prepare(`
9528
+ SELECT *
9529
+ FROM attention_items
9530
+ ${where}
9531
+ ORDER BY
9532
+ CASE severity WHEN 'critical' THEN 0 WHEN 'warning' THEN 1 ELSE 2 END,
9533
+ last_seen_at DESC,
9534
+ attention_id DESC
9535
+ LIMIT ?
9536
+ `).all(...params, limit).map(rowToAttentionItem).filter((item) => item !== void 0);
9537
+ }
9538
+ getAttentionItem(attentionId) {
9539
+ return rowToAttentionItem(this.db.prepare("SELECT * FROM attention_items WHERE attention_id = ?").get(attentionId));
9540
+ }
9541
+ acknowledgeAttention(input) {
9542
+ const actor = boundedSafeLabel(input.actor, "attention acknowledgement actor", 256);
9543
+ const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
9544
+ this.transaction(() => {
9545
+ const item = this.requireAttentionItem(input.attention_id);
9546
+ if (item.status !== "open") {
9547
+ throw new ProposalStoreError("ATTENTION_ITEM_NOT_ACKNOWLEDGEABLE", `attention item ${item.attention_id} is ${item.status}`);
9548
+ }
9549
+ assertAttentionOperatorDecision(
9550
+ item,
9551
+ actor,
9552
+ input.identity,
9553
+ input.require_verified_identity === true
9554
+ );
9555
+ this.db.prepare(`
9556
+ UPDATE attention_items
9557
+ SET status = 'acknowledged', acknowledged_by = ?, acknowledged_at = ?,
9558
+ acknowledgement_identity_json = ?,
9559
+ acknowledgement_decision_hash = ?,
9560
+ acknowledgement_signature = ?,
9561
+ acknowledgement_integrity_hash = ?,
9562
+ last_seen_at = MAX(last_seen_at, ?)
9563
+ WHERE attention_id = ?
9564
+ `).run(
9565
+ actor,
9566
+ now,
9567
+ input.identity ? JSON.stringify(input.identity) : null,
9568
+ input.identity?.decision_hash ?? null,
9569
+ input.identity?.signature ?? null,
9570
+ input.identity?.integrity_hash ?? null,
9571
+ now,
9572
+ item.attention_id
9573
+ );
9574
+ });
9575
+ return this.requireAttentionItem(input.attention_id);
9576
+ }
9577
+ resolveAttention(input) {
9578
+ const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
9579
+ this.transaction(() => {
9580
+ const item = this.requireAttentionItem(input.attention_id);
9581
+ if (item.status === "expired") {
9582
+ throw new ProposalStoreError("ATTENTION_ITEM_NOT_RESOLVABLE", `attention item ${item.attention_id} is expired`);
9583
+ }
9584
+ this.db.prepare(`
9585
+ UPDATE attention_items
9586
+ SET status = 'resolved', resolved_at = ?, last_seen_at = MAX(last_seen_at, ?)
9587
+ WHERE attention_id = ?
9588
+ `).run(now, now, item.attention_id);
9589
+ });
9590
+ return this.requireAttentionItem(input.attention_id);
9591
+ }
9592
+ enqueueNotificationDelivery(input) {
9593
+ const sinkId = boundedSinkId(input.sink_id);
9594
+ const event = this.requireAttentionEvent(input.event_id);
9595
+ const attention = input.attention_id ? this.requireAttentionItem(input.attention_id) : void 0;
9596
+ if (attention && event.attention_key !== attention.attention_key) {
9597
+ throw new ProposalStoreError(
9598
+ "NOTIFICATION_ATTENTION_MISMATCH",
9599
+ `attention event ${event.event_id} does not belong to attention item ${attention.attention_id}`
9600
+ );
9601
+ }
9602
+ const maxAttempts = Math.max(1, Math.min(input.max_attempts ?? 5, 100));
9603
+ const status = input.status ?? "pending";
9604
+ const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
9605
+ if (!Number.isFinite(Date.parse(now))) {
9606
+ throw new ProposalStoreError("NOTIFICATION_TIME_INVALID", "notification delivery time must be an ISO timestamp");
9607
+ }
9608
+ const nextAttemptAt = input.next_attempt_at ?? now;
9609
+ if (!Number.isFinite(Date.parse(nextAttemptAt))) {
9610
+ throw new ProposalStoreError(
9611
+ "NOTIFICATION_TIME_INVALID",
9612
+ "notification delivery next-attempt time must be an ISO timestamp"
9613
+ );
9614
+ }
9615
+ const deliveryId = notificationDeliveryId(sinkId, event.event_id);
9616
+ this.transaction(() => {
9617
+ const existing = this.getNotificationDelivery(deliveryId);
9618
+ if (existing) {
9619
+ if (existing.sink_id !== sinkId || existing.event_id !== event.event_id || existing.attention_id !== attention?.attention_id) {
9620
+ throw new ProposalStoreError(
9621
+ "NOTIFICATION_DELIVERY_IDEMPOTENCY_MISMATCH",
9622
+ `notification delivery ${deliveryId} already exists with different immutable routing`
9623
+ );
9624
+ }
9625
+ return;
9626
+ }
9627
+ this.db.prepare(`
9628
+ INSERT INTO notification_deliveries (
9629
+ delivery_id, sink_id, event_id, attention_id, status, attempts,
9630
+ max_attempts, next_attempt_at, lease_owner, lease_id,
9631
+ lease_expires_at, last_error_code, external_reference, delivered_at,
9632
+ created_at, updated_at
9633
+ ) VALUES (?, ?, ?, ?, ?, 0, ?, ?, NULL, NULL, NULL, NULL, NULL, NULL, ?, ?)
9634
+ `).run(
9635
+ deliveryId,
9636
+ sinkId,
9637
+ event.event_id,
9638
+ attention?.attention_id ?? null,
9639
+ status,
9640
+ maxAttempts,
9641
+ nextAttemptAt,
9642
+ now,
9643
+ now
9644
+ );
9645
+ });
9646
+ return this.requireNotificationDelivery(deliveryId);
9647
+ }
9648
+ includeNotificationDeliveriesInDigest(input) {
9649
+ const sinkId = boundedSinkId(input.sink_id);
9650
+ const deliveryIds = [...new Set(input.delivery_ids)];
9651
+ if (deliveryIds.length === 0 || deliveryIds.length > 1e3) {
9652
+ throw new ProposalStoreError(
9653
+ "NOTIFICATION_DIGEST_MEMBERS_INVALID",
9654
+ "a notification digest must contain from 1 through 1000 delivery ids"
9655
+ );
9656
+ }
9657
+ const digestEvent = this.requireAttentionEvent(input.digest_event_id);
9658
+ if (digestEvent.event_type !== "notification.digest") {
9659
+ throw new ProposalStoreError(
9660
+ "NOTIFICATION_DIGEST_EVENT_INVALID",
9661
+ "notification digest membership requires a notification.digest event"
9662
+ );
9663
+ }
9664
+ const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
9665
+ if (!Number.isFinite(Date.parse(now))) {
9666
+ throw new ProposalStoreError("NOTIFICATION_TIME_INVALID", "notification digest time must be an ISO timestamp");
9667
+ }
9668
+ let included = 0;
9669
+ this.transaction(() => {
9670
+ for (const deliveryId of deliveryIds) {
9671
+ const item = this.requireNotificationDelivery(deliveryId);
9672
+ const digestReference = `digest:${digestEvent.event_id}`;
9673
+ if (item.status === "suppressed" && item.sink_id === sinkId && item.external_reference === digestReference) {
9674
+ continue;
9675
+ }
9676
+ if (item.sink_id !== sinkId || item.status !== "batched") {
9677
+ throw new ProposalStoreError(
9678
+ "NOTIFICATION_DIGEST_MEMBER_STATE_INVALID",
9679
+ `notification delivery ${deliveryId} is not a batched member of sink ${sinkId}`
9680
+ );
9681
+ }
9682
+ this.db.prepare(`
9683
+ UPDATE notification_deliveries
9684
+ SET status = 'suppressed', external_reference = ?, updated_at = ?
9685
+ WHERE delivery_id = ?
9686
+ `).run(digestReference, now, deliveryId);
9687
+ included += 1;
9688
+ }
9689
+ });
9690
+ return included;
9691
+ }
9692
+ claimNotificationDeliveries(input) {
9693
+ const owner = boundedSafeLabel(input.owner, "notification dispatcher identity", 128);
9694
+ const sinkId = input.sink_id ? boundedSinkId(input.sink_id) : void 0;
9695
+ const limit = Math.max(1, Math.min(input.limit ?? 20, 100));
9696
+ const leaseSeconds = Math.max(15, Math.min(input.lease_seconds ?? 60, 3600));
9697
+ const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
9698
+ if (!Number.isFinite(Date.parse(now))) {
9699
+ throw new ProposalStoreError("NOTIFICATION_TIME_INVALID", "notification claim time must be an ISO timestamp");
9700
+ }
9701
+ const leaseExpiresAt = new Date(Date.parse(now) + leaseSeconds * 1e3).toISOString();
9702
+ const claimed = [];
9703
+ this.transaction(() => {
9704
+ const rows = this.db.prepare(`
9705
+ SELECT *
9706
+ FROM notification_deliveries
9707
+ WHERE (
9708
+ (status IN ('pending', 'retry_wait') AND next_attempt_at <= ?)
9709
+ OR (status = 'leased' AND lease_expires_at <= ?)
9710
+ )
9711
+ AND (? IS NULL OR sink_id = ?)
9712
+ ORDER BY next_attempt_at ASC, created_at ASC, delivery_id ASC
9713
+ LIMIT ?
9714
+ `).all(now, now, sinkId ?? null, sinkId ?? null, limit);
9715
+ for (const row of rows) {
9716
+ const item = rowToNotificationDelivery(row);
9717
+ if (!item) continue;
9718
+ const leaseId = notificationLeaseId(item.delivery_id, owner, item.attempts + 1, now);
9719
+ this.db.prepare(`
9720
+ UPDATE notification_deliveries
9721
+ SET status = 'leased', attempts = attempts + 1, lease_owner = ?,
9722
+ lease_id = ?, lease_expires_at = ?, last_error_code = NULL,
9723
+ updated_at = ?
9724
+ WHERE delivery_id = ?
9725
+ `).run(owner, leaseId, leaseExpiresAt, now, item.delivery_id);
9726
+ claimed.push(this.requireNotificationDelivery(item.delivery_id));
9727
+ }
9728
+ });
9729
+ return claimed;
9730
+ }
9731
+ completeNotificationDelivery(input) {
9732
+ const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
9733
+ const externalReference = input.external_reference ? boundedSafeLabel(input.external_reference, "notification external reference", 256) : void 0;
9734
+ this.transaction(() => {
9735
+ this.assertNotificationLease(input.delivery_id, input.owner, input.lease_id, now);
9736
+ this.db.prepare(`
9737
+ UPDATE notification_deliveries
9738
+ SET status = 'delivered', lease_owner = NULL, lease_id = NULL,
9739
+ lease_expires_at = NULL, last_error_code = NULL,
9740
+ external_reference = COALESCE(?, external_reference),
9741
+ delivered_at = ?, updated_at = ?
9742
+ WHERE delivery_id = ?
9743
+ `).run(externalReference ?? null, now, now, input.delivery_id);
9744
+ });
9745
+ return this.requireNotificationDelivery(input.delivery_id);
9746
+ }
9747
+ failNotificationDelivery(input) {
9748
+ const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
9749
+ const errorCode = boundedSafeErrorCode(input.error_code);
9750
+ this.transaction(() => {
9751
+ const item = this.assertNotificationLease(input.delivery_id, input.owner, input.lease_id, now);
9752
+ const retryable = input.retryable && item.attempts < item.max_attempts;
9753
+ const retryAt = retryable ? input.retry_at ?? new Date(Date.parse(now) + 1e3).toISOString() : now;
9754
+ if (!Number.isFinite(Date.parse(retryAt))) {
9755
+ throw new ProposalStoreError("NOTIFICATION_RETRY_TIME_INVALID", "notification retry time must be an ISO timestamp");
9756
+ }
9757
+ this.db.prepare(`
9758
+ UPDATE notification_deliveries
9759
+ SET status = ?, next_attempt_at = ?, lease_owner = NULL, lease_id = NULL,
9760
+ lease_expires_at = NULL, last_error_code = ?, updated_at = ?
9761
+ WHERE delivery_id = ?
9762
+ `).run(retryable ? "retry_wait" : "dead_letter", retryAt, errorCode, now, input.delivery_id);
9763
+ });
9764
+ return this.requireNotificationDelivery(input.delivery_id);
9765
+ }
9766
+ listNotificationDeliveries(filters = {}) {
9767
+ const clauses = [];
9768
+ const params = [];
9769
+ for (const [column, value] of [
9770
+ ["status", filters.status],
9771
+ ["sink_id", filters.sink_id],
9772
+ ["event_id", filters.event_id],
9773
+ ["attention_id", filters.attention_id]
9774
+ ]) {
9775
+ if (!value) continue;
9776
+ clauses.push(`${column} = ?`);
9777
+ params.push(value);
9778
+ }
9779
+ const limit = Math.max(1, Math.min(filters.limit ?? 200, 1e3));
9780
+ const where = clauses.length > 0 ? `WHERE ${clauses.join(" AND ")}` : "";
9781
+ return this.db.prepare(`
9782
+ SELECT *
9783
+ FROM notification_deliveries
9784
+ ${where}
9785
+ ORDER BY created_at DESC, delivery_id DESC
9786
+ LIMIT ?
9787
+ `).all(...params, limit).map(rowToNotificationDelivery).filter((delivery) => delivery !== void 0);
9788
+ }
9789
+ getNotificationDelivery(deliveryId) {
9790
+ return rowToNotificationDelivery(
9791
+ this.db.prepare("SELECT * FROM notification_deliveries WHERE delivery_id = ?").get(deliveryId)
9792
+ );
9793
+ }
9794
+ requeueNotificationDelivery(input) {
9795
+ const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
9796
+ const reason = boundedSafeLabel(input.reason, "notification replay reason", 256);
9797
+ this.transaction(() => {
9798
+ const item = this.requireNotificationDelivery(input.delivery_id);
9799
+ if (!["dead_letter", "suppressed", "batched"].includes(item.status)) {
9800
+ throw new ProposalStoreError(
9801
+ "NOTIFICATION_DELIVERY_NOT_REQUEUEABLE",
9802
+ `notification delivery ${item.delivery_id} is ${item.status}`
9803
+ );
9804
+ }
9805
+ assertNotificationReplayOperatorDecision(item, input.identity, reason);
9806
+ const event = this.requireAttentionEvent(item.event_id);
9807
+ this.db.prepare(`
9808
+ UPDATE notification_deliveries
9809
+ SET status = 'pending', attempts = 0, next_attempt_at = ?,
9810
+ lease_owner = NULL, lease_id = NULL, lease_expires_at = NULL,
9811
+ last_error_code = NULL, delivered_at = NULL, updated_at = ?
9812
+ WHERE delivery_id = ?
9813
+ `).run(now, now, item.delivery_id);
9814
+ this.recordAttentionEventInternal({
9815
+ event_type: "notification.replayed",
9816
+ severity: "informational",
9817
+ environment: event.environment,
9818
+ ...event.proposal_id ? { proposal_id: event.proposal_id } : {},
9819
+ ...event.job_id ? { job_id: event.job_id } : {},
9820
+ ...event.operation_id ? { operation_id: event.operation_id } : {},
9821
+ ...event.correlation_id ? { correlation_id: event.correlation_id } : {},
9822
+ ...event.capability ? { capability: event.capability } : {},
9823
+ ...event.contract_digest ? { contract_digest: event.contract_digest } : {},
9824
+ attention_required: false,
9825
+ immediate_default: false,
9826
+ summary: `Notification delivery ${item.delivery_id} requeued by a verified operator`,
9827
+ ...event.workbench_path ? { workbench_path: event.workbench_path } : {},
9828
+ details: {
9829
+ delivery_id: item.delivery_id,
9830
+ sink_id: item.sink_id,
9831
+ replayed_event_id: item.event_id,
9832
+ operator_subject: input.identity.subject,
9833
+ identity_provider: input.identity.provider,
9834
+ operator_decision_hash: input.identity.decision_hash,
9835
+ reason,
9836
+ approval_replayed: false,
9837
+ mutation_replayed: false,
9838
+ source_database_changed: false
9839
+ },
9840
+ source_event_key: `notification-replay:${item.delivery_id}:${input.identity.decision_hash}`,
9841
+ now
9842
+ });
9843
+ });
9844
+ return this.requireNotificationDelivery(input.delivery_id);
9845
+ }
9846
+ recordAttentionEventInternal(input) {
9847
+ assertAttentionEventInput(input);
9848
+ const occurredAt = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
9849
+ const environment = boundedSafeLabel(input.environment, "attention environment", 64);
9850
+ const capability = input.capability ? boundedSafeLabel(input.capability, "attention capability", 256) : void 0;
9851
+ const contractDigest = input.contract_digest;
9852
+ const attentionRequired = input.attention_required ?? defaultAttentionEventTypes.has(input.event_type);
9853
+ const immediateDefault = input.immediate_default ?? defaultImmediateAttentionEventTypes.has(input.event_type);
9854
+ const details = Object.fromEntries(
9855
+ Object.entries(input.details ?? {}).sort(([left], [right]) => left.localeCompare(right))
9856
+ );
9857
+ const sourceEventKey = input.source_event_key ? boundedSafeLabel(input.source_event_key, "attention source event key", 512) : canonicalJsonDigest({
9858
+ event_type: input.event_type,
9859
+ occurred_at: occurredAt,
9860
+ proposal_id: input.proposal_id ?? null,
9861
+ job_id: input.job_id ?? null,
9862
+ operation_id: input.operation_id ?? null,
9863
+ capability: capability ?? null,
9864
+ details
9865
+ });
9866
+ const eventId = attentionEventId(sourceEventKey, input.event_type);
9867
+ const attentionKey = attentionRequired ? boundedSafeLabel(
9868
+ input.attention_key ?? defaultAttentionKey({
9869
+ environment,
9870
+ event_type: input.event_type,
9871
+ capability,
9872
+ contract_digest: contractDigest,
9873
+ details
9874
+ }),
9875
+ "attention coalescing key",
9876
+ 512
9877
+ ) : void 0;
9878
+ const unsigned = {
9879
+ schema_version: "synapsor.attention-event.v1",
9880
+ event_id: eventId,
9881
+ event_type: input.event_type,
9882
+ severity: input.severity,
9883
+ occurred_at: occurredAt,
9884
+ environment,
9885
+ ...input.proposal_id ? { proposal_id: boundedSafeLabel(input.proposal_id, "attention proposal id", 256) } : {},
9886
+ ...input.job_id ? { job_id: boundedSafeLabel(input.job_id, "attention job id", 256) } : {},
9887
+ ...input.operation_id ? { operation_id: boundedSafeLabel(input.operation_id, "attention operation id", 256) } : {},
9888
+ ...input.correlation_id ? { correlation_id: boundedSafeLabel(input.correlation_id, "attention correlation id", 256) } : {},
9889
+ ...capability ? { capability } : {},
9890
+ ...contractDigest ? { contract_digest: contractDigest } : {},
9891
+ ...attentionKey ? { attention_key: attentionKey } : {},
9892
+ attention_required: attentionRequired,
9893
+ immediate_default: immediateDefault,
9894
+ summary: boundedSafeLabel(input.summary ?? attentionEventTitle(input.event_type), "attention summary", 512),
9895
+ ...input.approval_source ? { approval_source: input.approval_source } : {},
9896
+ ...input.worker_state ? { worker_state: boundedSafeLabel(input.worker_state, "attention worker state", 128) } : {},
9897
+ ...input.failure_class ? { failure_class: boundedSafeLabel(input.failure_class, "attention failure class", 128) } : {},
9898
+ ...input.expires_at ? { expires_at: input.expires_at } : {},
9899
+ ...input.workbench_path ? { workbench_path: boundedWorkbenchPath(input.workbench_path) } : {},
9900
+ details
9901
+ };
9902
+ assertNoSecretMaterial(unsigned, `attention_event.${eventId}`);
9903
+ const event = {
9904
+ ...unsigned,
9905
+ payload_hash: canonicalJsonDigest(unsigned)
9906
+ };
9907
+ const existing = rowToAttentionEvent(this.db.prepare("SELECT * FROM attention_events WHERE event_id = ?").get(eventId));
9908
+ if (existing) {
9909
+ if (existing.payload_hash !== event.payload_hash) {
9910
+ throw new ProposalStoreError(
9911
+ "ATTENTION_EVENT_IDEMPOTENCY_MISMATCH",
9912
+ `attention event ${eventId} already exists with different immutable content`
9913
+ );
9914
+ }
9915
+ return existing;
9916
+ }
9917
+ this.db.prepare(`
9918
+ INSERT INTO attention_events (
9919
+ event_id, schema_version, event_type, severity, occurred_at, environment,
9920
+ proposal_id, job_id, operation_id, correlation_id, capability,
9921
+ contract_digest, attention_key, attention_required, immediate_default,
9922
+ summary, approval_source, worker_state, failure_class, expires_at,
9923
+ workbench_path, details_json, payload_hash, created_at
9924
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
9925
+ `).run(
9926
+ event.event_id,
9927
+ event.schema_version,
9928
+ event.event_type,
9929
+ event.severity,
9930
+ event.occurred_at,
9931
+ event.environment,
9932
+ event.proposal_id ?? null,
9933
+ event.job_id ?? null,
9934
+ event.operation_id ?? null,
9935
+ event.correlation_id ?? null,
9936
+ event.capability ?? null,
9937
+ event.contract_digest ?? null,
9938
+ event.attention_key ?? null,
9939
+ event.attention_required ? 1 : 0,
9940
+ event.immediate_default ? 1 : 0,
9941
+ event.summary,
9942
+ event.approval_source ?? null,
9943
+ event.worker_state ?? null,
9944
+ event.failure_class ?? null,
9945
+ event.expires_at ?? null,
9946
+ event.workbench_path ?? null,
9947
+ JSON.stringify(event.details),
9948
+ event.payload_hash,
9949
+ event.occurred_at
9950
+ );
9951
+ if (event.attention_required && event.attention_key) this.projectAttentionItem(event);
9952
+ if (event.proposal_id && event.event_type === "proposal.expired") {
9953
+ this.closeProposalExpiryAttention(event, "expired");
9954
+ } else if (event.proposal_id && (event.event_type === "proposal.applied" || event.event_type === "proposal.cancelled" || event.event_type === "proposal.refused")) {
9955
+ this.closeProposalExpiryAttention(event, "resolved");
9956
+ }
9957
+ return event;
9958
+ }
9959
+ closeProposalExpiryAttention(event, status) {
9960
+ if (!event.proposal_id) return;
9961
+ this.db.prepare(`
9962
+ UPDATE attention_items
9963
+ SET status = ?,
9964
+ event_type = ?,
9965
+ title = ?,
9966
+ latest_event_id = ?,
9967
+ resolved_at = CASE WHEN ? = 'resolved' THEN ? ELSE NULL END,
9968
+ last_seen_at = MAX(last_seen_at, ?)
9969
+ WHERE status IN ('open', 'acknowledged')
9970
+ AND attention_key IN (
9971
+ SELECT DISTINCT attention_key
9972
+ FROM attention_events
9973
+ WHERE proposal_id = ?
9974
+ AND event_type = 'proposal.expiring'
9975
+ AND attention_key IS NOT NULL
9976
+ )
9977
+ `).run(
9978
+ status,
9979
+ event.event_type,
9980
+ event.summary,
9981
+ event.event_id,
9982
+ status,
9983
+ event.occurred_at,
9984
+ event.occurred_at,
9985
+ event.proposal_id
9986
+ );
9987
+ }
9988
+ projectAttentionItem(event) {
9989
+ const attentionKey = event.attention_key;
9990
+ if (!attentionKey) return;
9991
+ const existing = rowToAttentionItem(this.db.prepare("SELECT * FROM attention_items WHERE attention_key = ?").get(attentionKey));
9992
+ if (!existing) {
9993
+ this.db.prepare(`
9994
+ INSERT INTO attention_items (
9995
+ attention_id, attention_key, status, severity, environment, event_type,
9996
+ capability, contract_digest, title, occurrence_count, first_event_id,
9997
+ latest_event_id, first_seen_at, last_seen_at, acknowledged_by,
9998
+ acknowledged_at, resolved_at, expires_at
9999
+ ) VALUES (?, ?, 'open', ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, NULL, NULL, NULL, ?)
10000
+ `).run(
10001
+ attentionItemId(attentionKey),
10002
+ attentionKey,
10003
+ event.severity,
10004
+ event.environment,
10005
+ event.event_type,
10006
+ event.capability ?? null,
10007
+ event.contract_digest ?? null,
10008
+ event.summary,
10009
+ event.event_id,
10010
+ event.event_id,
10011
+ event.occurred_at,
10012
+ event.occurred_at,
10013
+ event.expires_at ?? null
10014
+ );
10015
+ return;
10016
+ }
10017
+ const severity = attentionSeverityRank(event.severity) > attentionSeverityRank(existing.severity) ? event.severity : existing.severity;
10018
+ this.db.prepare(`
10019
+ UPDATE attention_items
10020
+ SET status = 'open', severity = ?, event_type = ?, title = ?,
10021
+ occurrence_count = occurrence_count + 1, latest_event_id = ?,
10022
+ last_seen_at = ?, acknowledged_by = NULL, acknowledged_at = NULL,
10023
+ acknowledgement_identity_json = NULL,
10024
+ acknowledgement_decision_hash = NULL,
10025
+ acknowledgement_signature = NULL,
10026
+ acknowledgement_integrity_hash = NULL,
10027
+ resolved_at = NULL,
10028
+ expires_at = COALESCE(?, expires_at)
10029
+ WHERE attention_id = ?
10030
+ `).run(
10031
+ severity,
10032
+ event.event_type,
10033
+ event.summary,
10034
+ event.event_id,
10035
+ event.occurred_at,
10036
+ event.expires_at ?? null,
10037
+ existing.attention_id
10038
+ );
10039
+ }
10040
+ requireAttentionItem(attentionId) {
10041
+ const item = this.getAttentionItem(attentionId);
10042
+ if (!item) throw new ProposalStoreError("ATTENTION_ITEM_NOT_FOUND", `attention item not found: ${attentionId}`);
10043
+ return item;
10044
+ }
10045
+ requireAttentionEvent(eventId) {
10046
+ const event = rowToAttentionEvent(this.db.prepare("SELECT * FROM attention_events WHERE event_id = ?").get(eventId));
10047
+ if (!event) throw new ProposalStoreError("ATTENTION_EVENT_NOT_FOUND", `attention event not found: ${eventId}`);
10048
+ return event;
10049
+ }
10050
+ requireNotificationDelivery(deliveryId) {
10051
+ const delivery = this.getNotificationDelivery(deliveryId);
10052
+ if (!delivery) {
10053
+ throw new ProposalStoreError("NOTIFICATION_DELIVERY_NOT_FOUND", `notification delivery not found: ${deliveryId}`);
10054
+ }
10055
+ return delivery;
10056
+ }
10057
+ assertNotificationLease(deliveryId, owner, leaseId, now) {
10058
+ const item = this.requireNotificationDelivery(deliveryId);
10059
+ if (item.status !== "leased" || item.lease_owner !== owner || item.lease_id !== leaseId) {
10060
+ throw new ProposalStoreError(
10061
+ "NOTIFICATION_LEASE_MISMATCH",
10062
+ `dispatcher ${owner} does not hold lease ${leaseId} for ${deliveryId}`
10063
+ );
10064
+ }
10065
+ if (!item.lease_expires_at || Date.parse(item.lease_expires_at) <= Date.parse(now)) {
10066
+ throw new ProposalStoreError("NOTIFICATION_LEASE_EXPIRED", `notification lease ${leaseId} has expired`);
10067
+ }
10068
+ return item;
10069
+ }
8261
10070
  enqueueCloudOutbox(input) {
8262
10071
  const eventId = input.event_id.trim();
8263
10072
  if (!eventId) throw new ProposalStoreError("CLOUD_OUTBOX_EVENT_ID_REQUIRED", "cloud outbox event_id is required");
@@ -8433,6 +10242,92 @@ var ProposalStore = class {
8433
10242
  if (!item) throw new ProposalStoreError("CLOUD_OUTBOX_EVENT_NOT_FOUND", `cloud outbox event not found: ${eventId}`);
8434
10243
  return item;
8435
10244
  }
10245
+ workerControlState() {
10246
+ const raw = this.getRunnerState("supervised_worker_control");
10247
+ if (!raw) return defaultWorkerControlState();
10248
+ return parseWorkerControlState(raw);
10249
+ }
10250
+ updateWorkerControl(input) {
10251
+ const now = input.now ?? (/* @__PURE__ */ new Date()).toISOString();
10252
+ const current = this.workerControlState();
10253
+ assertWorkerControlTarget(input);
10254
+ assertWorkerControlOperatorDecision(
10255
+ current,
10256
+ input,
10257
+ input.actor,
10258
+ input.identity,
10259
+ input.require_verified_identity === true
10260
+ );
10261
+ let mode = current.mode;
10262
+ let controls = [...current.capability_controls];
10263
+ if (input.action === "pause") mode = "paused";
10264
+ else if (input.action === "resume") mode = "active";
10265
+ else if (input.action === "drain") mode = "draining";
10266
+ else {
10267
+ const capability = input.capability;
10268
+ const contractDigest = input.contract_digest;
10269
+ const keyMatches = (entry) => entry.capability === capability && entry.contract_digest === contractDigest;
10270
+ const existing = controls.find(keyMatches);
10271
+ if (existing?.status === "revoked" && input.action === "capability_enable") {
10272
+ throw new ProposalStoreError(
10273
+ "WORKER_DIGEST_REVOKED",
10274
+ `revoked supervised-worker digest ${contractDigest} cannot be re-enabled`
10275
+ );
10276
+ }
10277
+ const status = input.action === "capability_enable" ? "enabled" : input.action === "capability_disable" ? "disabled" : "revoked";
10278
+ controls = [
10279
+ ...controls.filter((entry) => !keyMatches(entry)),
10280
+ {
10281
+ capability,
10282
+ contract_digest: contractDigest,
10283
+ status,
10284
+ updated_at: now,
10285
+ actor: input.actor
10286
+ }
10287
+ ].sort((left, right) => left.capability.localeCompare(right.capability) || left.contract_digest.localeCompare(right.contract_digest));
10288
+ }
10289
+ const unsigned = {
10290
+ schema_version: "synapsor.worker-control.v1",
10291
+ mode,
10292
+ revision: current.revision + 1,
10293
+ capability_controls: controls,
10294
+ ...input.identity ? { last_decision: input.identity } : {},
10295
+ updated_at: now
10296
+ };
10297
+ const updated = {
10298
+ ...unsigned,
10299
+ integrity_hash: canonicalJsonDigest(unsigned)
10300
+ };
10301
+ assertNoSecretMaterial(updated, "runner_state.supervised_worker_control");
10302
+ this.transaction(() => {
10303
+ this.db.prepare(`
10304
+ INSERT INTO runner_state (key, value_json, updated_at)
10305
+ VALUES ('supervised_worker_control', ?, ?)
10306
+ ON CONFLICT(key) DO UPDATE SET value_json = excluded.value_json, updated_at = excluded.updated_at
10307
+ `).run(JSON.stringify(updated), now);
10308
+ const global = input.action === "pause" || input.action === "resume" || input.action === "drain";
10309
+ const eventType = input.action === "resume" || input.action === "capability_enable" ? "capability.activated" : global ? "worker.paused" : "capability.revoked";
10310
+ this.recordAttentionEventInternal({
10311
+ event_type: eventType,
10312
+ severity: "informational",
10313
+ environment: input.environment ? boundedSafeLabel(input.environment, "worker control environment", 64) : "unknown",
10314
+ ...input.capability ? { capability: input.capability } : {},
10315
+ ...input.contract_digest ? { contract_digest: input.contract_digest } : {},
10316
+ attention_required: false,
10317
+ immediate_default: false,
10318
+ summary: workerControlSummary(input),
10319
+ worker_state: mode,
10320
+ details: {
10321
+ control_action: input.action,
10322
+ control_revision: updated.revision,
10323
+ source_database_changed: false
10324
+ },
10325
+ source_event_key: `worker-control:${updated.revision}:${updated.integrity_hash}`,
10326
+ now
10327
+ });
10328
+ });
10329
+ return updated;
10330
+ }
8436
10331
  setRunnerState(key, value) {
8437
10332
  assertNoSecretMaterial(value, `runner_state.${key}`);
8438
10333
  this.db.prepare(`
@@ -8464,6 +10359,9 @@ var ProposalStore = class {
8464
10359
  { table: "shadow_study_cases", kind: "shadow_study_case", key: "case_id", created: "created_at", proposal: "proposal_id", tenant: "tenant_id", capability: "capability" },
8465
10360
  { table: "shadow_outcomes", kind: "shadow_outcome", key: "outcome_id", created: "created_at", proposal: "proposal_id", tenant: "tenant_id" },
8466
10361
  { table: "worker_queue", kind: "worker_queue_item", key: "proposal_id", created: "created_at", proposal: "proposal_id" },
10362
+ { table: "attention_events", kind: "attention_event", key: "event_id", created: "created_at", proposal: "proposal_id", capability: "capability" },
10363
+ { table: "attention_items", kind: "attention_item", key: "attention_id", created: "first_seen_at", capability: "capability" },
10364
+ { table: "notification_deliveries", kind: "notification_delivery", key: "delivery_id", created: "created_at" },
8467
10365
  { table: "runner_state", kind: "runner_state", key: "key", created: "updated_at" },
8468
10366
  { table: "policy_recommendations", kind: "policy_recommendation", key: "recommendation_id", created: "created_at", tenant: "tenant_id", capability: "capability" },
8469
10367
  { table: "cloud_outbox", kind: "cloud_outbox_event", key: "event_id", created: "created_at", proposal: "proposal_id" },
@@ -9036,10 +10934,52 @@ var ProposalStore = class {
9036
10934
  });
9037
10935
  }
9038
10936
  appendEvent(proposalId, kind, actor, payload) {
10937
+ const append = () => {
10938
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
10939
+ const result = this.db.prepare(`
10940
+ INSERT INTO proposal_events (proposal_id, kind, actor, payload_json, created_at)
10941
+ VALUES (?, ?, ?, ?, ?)
10942
+ `).run(proposalId, kind, actor, JSON.stringify(payload), createdAt);
10943
+ const proposal = this.requireProposal(proposalId);
10944
+ for (const event of attentionEventsForProposalEvent({
10945
+ proposal,
10946
+ proposal_event_id: String(result.lastInsertRowid),
10947
+ kind,
10948
+ actor,
10949
+ payload,
10950
+ environment: this.attentionEnvironment(),
10951
+ occurred_at: createdAt
10952
+ })) {
10953
+ this.recordAttentionEventInternal(event);
10954
+ }
10955
+ if (proposal.state !== "pending_review") {
10956
+ this.resolveSatisfiedProposalReviewAttention(proposal, createdAt);
10957
+ }
10958
+ };
10959
+ if (this.db.isTransaction) append();
10960
+ else this.transaction(append);
10961
+ }
10962
+ attentionEnvironment() {
10963
+ const context = this.getRunnerState("attention_context");
10964
+ const environment = context?.environment;
10965
+ return typeof environment === "string" && /^(development|staging|production|unknown)$/.test(environment) ? environment : "unknown";
10966
+ }
10967
+ resolveSatisfiedProposalReviewAttention(proposal, now) {
10968
+ const attentionKey = proposalReviewAttentionKey(proposal, this.attentionEnvironment());
10969
+ const pending = this.db.prepare(`
10970
+ SELECT COUNT(DISTINCT p.proposal_id) AS count
10971
+ FROM attention_events ae
10972
+ JOIN proposals p ON p.proposal_id = ae.proposal_id
10973
+ WHERE ae.attention_key = ?
10974
+ AND p.state = 'pending_review'
10975
+ `).get(attentionKey);
10976
+ if (isRecord3(pending) && Number(pending.count ?? 0) > 0) return;
9039
10977
  this.db.prepare(`
9040
- INSERT INTO proposal_events (proposal_id, kind, actor, payload_json, created_at)
9041
- VALUES (?, ?, ?, ?, ?)
9042
- `).run(proposalId, kind, actor, JSON.stringify(payload), (/* @__PURE__ */ new Date()).toISOString());
10978
+ UPDATE attention_items
10979
+ SET status = 'resolved', resolved_at = ?, last_seen_at = MAX(last_seen_at, ?)
10980
+ WHERE attention_key = ?
10981
+ AND status IN ('open', 'acknowledged')
10982
+ `).run(now, now, attentionKey);
9043
10983
  }
9044
10984
  queryAudit(proposalId) {
9045
10985
  const rows = this.db.prepare("SELECT * FROM query_audit WHERE proposal_id = ? ORDER BY audit_id ASC").all(proposalId);
@@ -9628,174 +11568,1009 @@ function assertProposalIdentity(proposal, hash, version) {
9628
11568
  if (proposal.proposal_hash !== hash) {
9629
11569
  throw new ProposalStoreError("PROPOSAL_HASH_MISMATCH", `proposal ${proposal.proposal_id} hash mismatch`);
9630
11570
  }
9631
- if (proposal.proposal_version !== version) {
9632
- throw new ProposalStoreError("PROPOSAL_VERSION_MISMATCH", `proposal ${proposal.proposal_id} version mismatch`);
11571
+ if (proposal.proposal_version !== version) {
11572
+ throw new ProposalStoreError("PROPOSAL_VERSION_MISMATCH", `proposal ${proposal.proposal_id} version mismatch`);
11573
+ }
11574
+ }
11575
+ function assertWritebackAllowed(proposal, operation) {
11576
+ if (proposal.change_set.mode === "shadow") {
11577
+ throw new ProposalStoreError(
11578
+ "SHADOW_WRITEBACK_DISABLED",
11579
+ `shadow proposal ${proposal.proposal_id} cannot be ${operation}; shadow mode stores proposals, evidence, query audit, and replay only and never mutates the source database`
11580
+ );
11581
+ }
11582
+ if (proposal.change_set.mode === "read_only") {
11583
+ throw new ProposalStoreError(
11584
+ "READ_ONLY_WRITEBACK_DISABLED",
11585
+ `read-only proposal ${proposal.proposal_id} cannot be ${operation}; read-only mode does not allow proposal writeback`
11586
+ );
11587
+ }
11588
+ }
11589
+ var secretKeyPattern = /(^|[_-])(password|passwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|cookie|credential|connection[_-]?string|database[_-]?url|read[_-]?url|write[_-]?url)($|[_-])/i;
11590
+ var secretValuePattern = /(postgres(?:ql)?:\/\/|mysql:\/\/|Bearer\s+[A-Za-z0-9._~+/=-]+|syn_wbr_[A-Za-z0-9._~+/=-]+|-----BEGIN [A-Z ]*PRIVATE KEY-----)/i;
11591
+ function assertNoSecretMaterial(value, path3) {
11592
+ if (Array.isArray(value)) {
11593
+ value.forEach((item, index) => assertNoSecretMaterial(item, `${path3}[${index}]`));
11594
+ return;
11595
+ }
11596
+ if (isRecord3(value)) {
11597
+ for (const [key, entry] of Object.entries(value)) {
11598
+ const entryPath = `${path3}.${key}`;
11599
+ if (secretKeyPattern.test(key)) {
11600
+ throw new ProposalStoreError(
11601
+ "SECRET_MATERIAL_REJECTED",
11602
+ `refusing to persist secret-like field ${entryPath}; remove it from reviewed visible/evidence/query-audit data`
11603
+ );
11604
+ }
11605
+ assertNoSecretMaterial(entry, entryPath);
11606
+ }
11607
+ return;
11608
+ }
11609
+ if (typeof value === "string" && secretValuePattern.test(value)) {
11610
+ throw new ProposalStoreError(
11611
+ "SECRET_MATERIAL_REJECTED",
11612
+ `refusing to persist secret-like value at ${path3}; remove it from reviewed visible/evidence/query-audit data`
11613
+ );
11614
+ }
11615
+ }
11616
+ function rowToProposal(row) {
11617
+ if (!isRecord3(row)) return void 0;
11618
+ return {
11619
+ proposal_id: String(row.proposal_id),
11620
+ proposal_version: Number(row.proposal_version),
11621
+ proposal_hash: String(row.proposal_hash),
11622
+ action: String(row.action),
11623
+ state: String(row.state),
11624
+ tenant_id: String(row.tenant_id),
11625
+ principal: row.principal == null ? void 0 : String(row.principal),
11626
+ capability: row.capability == null ? void 0 : String(row.capability),
11627
+ interaction_id: row.interaction_id == null ? void 0 : String(row.interaction_id),
11628
+ tool_call_id: row.tool_call_id == null ? void 0 : String(row.tool_call_id),
11629
+ business_object: String(row.business_object),
11630
+ object_id: String(row.object_id),
11631
+ source_kind: String(row.source_kind),
11632
+ source_id: String(row.source_id),
11633
+ source_schema: String(row.source_schema),
11634
+ source_table: String(row.source_table),
11635
+ source_database_mutated: Number(row.source_database_mutated) === 1,
11636
+ change_set: parseChangeSet(JSON.parse(String(row.change_set_json))),
11637
+ created_at: String(row.created_at),
11638
+ updated_at: String(row.updated_at)
11639
+ };
11640
+ }
11641
+ function rowToEvent(row) {
11642
+ if (!isRecord3(row)) return void 0;
11643
+ return {
11644
+ event_id: Number(row.event_id),
11645
+ proposal_id: String(row.proposal_id),
11646
+ kind: String(row.kind),
11647
+ actor: String(row.actor),
11648
+ payload: JSON.parse(String(row.payload_json)),
11649
+ created_at: String(row.created_at)
11650
+ };
11651
+ }
11652
+ function rowToApproval(row) {
11653
+ if (!isRecord3(row)) return void 0;
11654
+ const status = String(row.status);
11655
+ if (status !== "approved" && status !== "rejected") return void 0;
11656
+ return {
11657
+ approval_id: Number(row.approval_id),
11658
+ proposal_id: String(row.proposal_id),
11659
+ proposal_version: Number(row.proposal_version),
11660
+ proposal_hash: String(row.proposal_hash),
11661
+ approver: String(row.approver),
11662
+ status,
11663
+ reason: row.reason == null ? void 0 : String(row.reason),
11664
+ identity: row.identity_json == null ? void 0 : JSON.parse(String(row.identity_json)),
11665
+ decision_hash: row.decision_hash == null ? void 0 : String(row.decision_hash),
11666
+ signature: row.signature == null ? void 0 : String(row.signature),
11667
+ integrity_hash: row.integrity_hash == null ? void 0 : String(row.integrity_hash),
11668
+ freshness_proof_digest: row.freshness_proof_digest == null ? void 0 : String(row.freshness_proof_digest),
11669
+ created_at: String(row.created_at)
11670
+ };
11671
+ }
11672
+ function rowToPolicyRecommendation(row) {
11673
+ if (!isRecord3(row)) return void 0;
11674
+ let unsigned;
11675
+ try {
11676
+ unsigned = JSON.parse(String(row.payload_json));
11677
+ } catch {
11678
+ throw new ProposalStoreError("POLICY_RECOMMENDATION_TAMPERED", "policy recommendation payload is not valid JSON");
11679
+ }
11680
+ const recommendation = { ...unsigned, integrity_hash: String(row.integrity_hash) };
11681
+ assertPolicyRecommendationShape(recommendation);
11682
+ if (recommendation.recommendation_id !== String(row.recommendation_id) || recommendation.tenant_id !== String(row.tenant_id) || recommendation.capability !== String(row.capability) || recommendation.policy !== String(row.policy) || recommendation.base_contract_digest !== String(row.base_contract_digest) || recommendation.status !== String(row.status)) throw new ProposalStoreError("POLICY_RECOMMENDATION_TAMPERED", `policy recommendation ${String(row.recommendation_id)} index fields do not match its signed payload`);
11683
+ const expected = canonicalJsonDigest(policyRecommendationUnsigned(recommendation));
11684
+ if (recommendation.integrity_hash !== expected) throw new ProposalStoreError("POLICY_RECOMMENDATION_TAMPERED", `policy recommendation ${recommendation.recommendation_id} failed its integrity check`);
11685
+ return recommendation;
11686
+ }
11687
+ function policyRecommendationUnsigned(recommendation) {
11688
+ const { integrity_hash: _integrityHash, ...unsigned } = recommendation;
11689
+ return unsigned;
11690
+ }
11691
+ function assertPolicyRecommendationShape(recommendation) {
11692
+ if (recommendation.schema_version !== "synapsor.policy-recommendation.v1") throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "unsupported policy recommendation schema version");
11693
+ if (!/^ptr_[a-f0-9]{20}$/.test(recommendation.recommendation_id)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation id is invalid");
11694
+ for (const [name, value] of [["tenant", recommendation.tenant_id], ["capability", recommendation.capability], ["policy", recommendation.policy], ["field", recommendation.field]]) {
11695
+ if (!value || value.length > 256) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", `policy recommendation ${name} is invalid`);
11696
+ }
11697
+ if (!/^sha256:[a-f0-9]{64}$/.test(recommendation.base_contract_digest)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation base digest is invalid");
11698
+ if (!/^sha256:[a-f0-9]{64}$/.test(recommendation.integrity_hash)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation integrity digest is invalid");
11699
+ if (!Number.isFinite(recommendation.current_threshold) || !Number.isFinite(recommendation.proposed_threshold) || !Number.isFinite(recommendation.maximum_increment) || !Number.isFinite(recommendation.absolute_ceiling)) {
11700
+ throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation thresholds must be finite numbers");
11701
+ }
11702
+ if (recommendation.proposed_threshold <= recommendation.current_threshold || recommendation.proposed_threshold - recommendation.current_threshold > recommendation.maximum_increment || recommendation.proposed_threshold > recommendation.absolute_ceiling) {
11703
+ throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation exceeds its reviewed increment or ceiling");
11704
+ }
11705
+ if (!Array.isArray(recommendation.evidence_proposal_ids) || recommendation.evidence_proposal_ids.length === 0 || recommendation.evidence_proposal_ids.length > 1e4) {
11706
+ throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation requires bounded proposal evidence");
11707
+ }
11708
+ if (!isRecord3(recommendation.metrics) || !isRecord3(recommendation.criteria)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation criteria and metrics are required");
11709
+ if (!["pending_review", "approved", "rejected", "exported"].includes(recommendation.status)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation status is invalid");
11710
+ }
11711
+ function assertPolicyRecommendationIdentity(recommendation, input) {
11712
+ const proof = input.identity;
11713
+ if (!proof.verified || proof.provider === "dev_env") throw new ProposalStoreError("POLICY_RECOMMENDATION_VERIFIED_IDENTITY_REQUIRED", "policy recommendation decisions require a cryptographically verified operator identity");
11714
+ if (proof.subject !== input.actor || proof.decision.subject !== input.actor) throw new ProposalStoreError("POLICY_RECOMMENDATION_IDENTITY_MISMATCH", "policy recommendation actor does not match the verified identity");
11715
+ if (proof.decision.action !== input.action || proof.decision.proposal_id !== recommendation.recommendation_id || proof.decision.proposal_hash !== recommendation.integrity_hash || proof.decision.proposal_version !== 1) {
11716
+ throw new ProposalStoreError("POLICY_RECOMMENDATION_IDENTITY_MISMATCH", "verified operator decision is not bound to this policy recommendation version");
11717
+ }
11718
+ const { integrity_hash: _integrityHash, ...core } = proof;
11719
+ const canonicalCore = JSON.parse(JSON.stringify(core));
11720
+ if (proof.integrity_hash !== canonicalJsonDigest(canonicalCore)) throw new ProposalStoreError("POLICY_RECOMMENDATION_IDENTITY_TAMPERED", "verified operator identity proof failed its integrity check");
11721
+ }
11722
+ function attentionEventsForProposalEvent(input) {
11723
+ const proposal = input.proposal;
11724
+ const contractDigest = proposal.change_set.contract?.digest;
11725
+ const scopeDigest = canonicalJsonDigest({
11726
+ tenant_id: proposal.tenant_id,
11727
+ principal: proposal.principal ?? null
11728
+ });
11729
+ const sourceKey = (eventType) => `proposal:${proposal.proposal_id}:event:${input.proposal_event_id}:${eventType}`;
11730
+ const base = (eventType, severity) => ({
11731
+ event_type: eventType,
11732
+ severity,
11733
+ environment: input.environment,
11734
+ proposal_id: proposal.proposal_id,
11735
+ capability: proposal.capability ?? proposal.action,
11736
+ ...contractDigest ? { contract_digest: contractDigest } : {},
11737
+ source_event_key: sourceKey(eventType),
11738
+ now: input.occurred_at
11739
+ });
11740
+ const safeCode = safeAttentionPayloadString(input.payload, "safe_error_code") ?? safeAttentionPayloadString(input.payload, "error_code") ?? safeAttentionPayloadString(input.payload, "reason_code");
11741
+ const workerDetails = safeAttentionDetails(input.payload, [
11742
+ "attempt",
11743
+ "max_attempts",
11744
+ "execution_mode",
11745
+ "outcome"
11746
+ ]);
11747
+ const requiredRole = typeof proposal.change_set.approval.required_role === "string" ? proposal.change_set.approval.required_role : "reviewer";
11748
+ const reviewAttention = (summary) => {
11749
+ const attentionKey = proposalReviewAttentionKey(proposal, input.environment);
11750
+ return {
11751
+ ...base("proposal.review_required", "warning"),
11752
+ attention_required: true,
11753
+ immediate_default: true,
11754
+ attention_key: attentionKey,
11755
+ summary: summary ?? `${proposal.capability ?? proposal.action} proposals need ${requiredRole} review`,
11756
+ workbench_path: `/attention/${attentionItemId(attentionKey)}`,
11757
+ details: {
11758
+ required_role: requiredRole,
11759
+ ...safeCode ? { failure_class: safeCode } : {}
11760
+ },
11761
+ ...safeCode ? { failure_class: safeCode } : {}
11762
+ };
11763
+ };
11764
+ const criticalWorkerAttention = (eventType, summary) => {
11765
+ const failureClass = safeCode ?? (eventType === "worker.dead_lettered" ? "RETRY_BUDGET_EXHAUSTED" : eventType === "worker.unknown_outcome" ? "OUTCOME_UNKNOWN" : "RECONCILIATION_REQUIRED");
11766
+ const attentionKey = [
11767
+ input.environment,
11768
+ eventType,
11769
+ proposal.capability ?? proposal.action,
11770
+ contractDigest ?? "no_digest",
11771
+ failureClass,
11772
+ scopeDigest
11773
+ ].join(":");
11774
+ return {
11775
+ ...base(eventType, "critical"),
11776
+ attention_required: true,
11777
+ immediate_default: true,
11778
+ attention_key: attentionKey,
11779
+ summary,
11780
+ workbench_path: `/attention/${attentionItemId(attentionKey)}`,
11781
+ worker_state: eventType.slice("worker.".length),
11782
+ failure_class: failureClass,
11783
+ details: {
11784
+ ...workerDetails,
11785
+ failure_class: failureClass
11786
+ }
11787
+ };
11788
+ };
11789
+ if (input.kind === "proposal_created") {
11790
+ const events = [{
11791
+ ...base("proposal.created", "informational"),
11792
+ attention_required: false,
11793
+ immediate_default: false,
11794
+ details: { source_database_changed: false }
11795
+ }];
11796
+ const approvalMode = proposal.change_set.approval.mode;
11797
+ if (proposal.state === "pending_review" && approvalMode !== "policy") events.push(reviewAttention());
11798
+ return events;
11799
+ }
11800
+ if (input.kind === "proposal_approved") {
11801
+ const policyApproved = input.actor.startsWith("policy:");
11802
+ return [{
11803
+ ...base(policyApproved ? "proposal.auto_approved" : "proposal.approved", "informational"),
11804
+ attention_required: false,
11805
+ immediate_default: false,
11806
+ approval_source: policyApproved ? "policy_auto" : "human",
11807
+ details: safeAttentionDetails(input.payload, ["approvals", "required_approvals", "remaining_approvals"])
11808
+ }];
11809
+ }
11810
+ if (input.kind === "policy_auto_approval_deferred" || input.kind === "proposal_approval_blocked_freshness") {
11811
+ return [reviewAttention(
11812
+ input.kind === "proposal_approval_blocked_freshness" ? `${proposal.capability ?? proposal.action} needs review because its source freshness check failed` : `${proposal.capability ?? proposal.action} exceeded automatic-approval policy and needs human review`
11813
+ )];
11814
+ }
11815
+ if (input.kind === "proposal_rejected" || input.kind === "proposal_failed") {
11816
+ return [{
11817
+ ...base("proposal.refused", "warning"),
11818
+ attention_required: false,
11819
+ immediate_default: false,
11820
+ ...safeCode ? { failure_class: safeCode, details: { failure_class: safeCode } } : {}
11821
+ }];
11822
+ }
11823
+ if (input.kind === "proposal_canceled" || input.kind === "writeback_canceled") {
11824
+ return [{
11825
+ ...base("proposal.cancelled", "informational"),
11826
+ attention_required: false,
11827
+ immediate_default: false
11828
+ }];
11829
+ }
11830
+ if (input.kind === "proposal_conflict" || input.kind === "writeback_conflict" || input.kind === "writeback_intent_conflict") {
11831
+ return [{
11832
+ ...base("proposal.conflict", "warning"),
11833
+ attention_required: false,
11834
+ immediate_default: false,
11835
+ ...safeCode ? { failure_class: safeCode, details: { failure_class: safeCode } } : {}
11836
+ }];
11837
+ }
11838
+ if (input.kind === "proposal_pending_worker" || input.kind === "writeback_worker_queued") {
11839
+ return [{
11840
+ ...base("proposal.queued", "informational"),
11841
+ attention_required: false,
11842
+ immediate_default: false,
11843
+ worker_state: "queued",
11844
+ details: safeAttentionDetails(input.payload, ["execution_mode", "max_attempts"])
11845
+ }];
11846
+ }
11847
+ if (input.kind === "writeback_retry_scheduled") {
11848
+ return [{
11849
+ ...base("worker.retry_scheduled", "warning"),
11850
+ attention_required: false,
11851
+ immediate_default: false,
11852
+ worker_state: "retry_wait",
11853
+ ...safeCode ? { failure_class: safeCode } : {},
11854
+ details: {
11855
+ ...workerDetails,
11856
+ ...safeCode ? { failure_class: safeCode } : {}
11857
+ }
11858
+ }];
11859
+ }
11860
+ if (input.kind === "writeback_dead_lettered") {
11861
+ return [criticalWorkerAttention("worker.dead_lettered")];
11862
+ }
11863
+ if (input.kind === "writeback_worker_blocked") {
11864
+ const failureClass = safeCode ?? "WORKER_POLICY_BLOCKED";
11865
+ if (/PROPOSAL_.*EXPIRED|PROPOSAL_EXPIRED/i.test(failureClass)) {
11866
+ return [{
11867
+ ...base("proposal.expired", "warning"),
11868
+ attention_required: false,
11869
+ immediate_default: false,
11870
+ worker_state: "blocked",
11871
+ failure_class: failureClass,
11872
+ details: {
11873
+ ...workerDetails,
11874
+ failure_class: failureClass
11875
+ }
11876
+ }];
11877
+ }
11878
+ const digestStale = /(?:CONTRACT|DIGEST|GENERATION_LOCK|SCHEMA|POLICY)_.*(?:STALE|MISMATCH|CHANGED)|ACTIVE_CONTRACT_DIGEST/i.test(failureClass);
11879
+ const limitExceeded = /(?:LIMIT|RATE|QUEUE|BUDGET)_.*(?:EXCEEDED|STALE|MISMATCH)|POLICY_LIMIT/i.test(failureClass);
11880
+ const eventType = digestStale ? "contract.digest_stale" : limitExceeded ? "policy.limit_exceeded" : "proposal.refused";
11881
+ const severity = digestStale || limitExceeded ? "critical" : "warning";
11882
+ const attentionRequired = digestStale || limitExceeded;
11883
+ const attentionKey = [
11884
+ input.environment,
11885
+ eventType,
11886
+ proposal.capability ?? proposal.action,
11887
+ contractDigest ?? "no_digest",
11888
+ failureClass,
11889
+ scopeDigest
11890
+ ].join(":");
11891
+ return [{
11892
+ ...base(eventType, severity),
11893
+ attention_required: attentionRequired,
11894
+ immediate_default: attentionRequired,
11895
+ ...attentionRequired ? {
11896
+ attention_key: attentionKey,
11897
+ workbench_path: `/attention/${attentionItemId(attentionKey)}`
11898
+ } : {},
11899
+ worker_state: "blocked",
11900
+ failure_class: failureClass,
11901
+ details: {
11902
+ ...workerDetails,
11903
+ failure_class: failureClass
11904
+ }
11905
+ }];
11906
+ }
11907
+ if (input.kind === "policy_limit_near") {
11908
+ return [{
11909
+ ...base("policy.limit_near", "warning"),
11910
+ attention_required: false,
11911
+ immediate_default: false,
11912
+ summary: `${proposal.capability ?? proposal.action} is approaching a reviewed policy limit`,
11913
+ details: safeAttentionDetails(input.payload, [
11914
+ "kind",
11915
+ "scope",
11916
+ "observed",
11917
+ "proposed",
11918
+ "projected",
11919
+ "max",
11920
+ "window_start",
11921
+ "window_end"
11922
+ ])
11923
+ }];
11924
+ }
11925
+ if (input.kind === "writeback_reconciliation_required" || input.kind === "writeback_intent_reconciliation_required") {
11926
+ return [criticalWorkerAttention("worker.reconciliation_required")];
11927
+ }
11928
+ if ((input.kind === "writeback_failed" || input.kind === "writeback_intent_failed") && (safeCode === "OUTCOME_UNKNOWN" || safeCode === "RECONCILIATION_REQUIRED")) {
11929
+ return [criticalWorkerAttention("worker.unknown_outcome")];
11930
+ }
11931
+ if (input.kind === "writeback_applied" || input.kind === "writeback_already_applied" || input.kind === "writeback_intent_applied" || input.kind === "writeback_intent_already_applied") {
11932
+ return [{
11933
+ ...base("proposal.applied", "informational"),
11934
+ attention_required: false,
11935
+ immediate_default: false,
11936
+ worker_state: "completed",
11937
+ details: safeAttentionDetails(input.payload, ["rows_affected", "source_database_mutated"])
11938
+ }];
11939
+ }
11940
+ if (input.kind === "writeback_worker_completed") {
11941
+ const outcome = safeAttentionPayloadString(input.payload, "outcome");
11942
+ if (outcome === "conflict") {
11943
+ return [{
11944
+ ...base("proposal.conflict", "warning"),
11945
+ attention_required: false,
11946
+ immediate_default: false,
11947
+ worker_state: "completed",
11948
+ details: { outcome }
11949
+ }];
11950
+ }
11951
+ if (outcome === "applied" || outcome === "already_applied") {
11952
+ return [{
11953
+ ...base("proposal.applied", "informational"),
11954
+ attention_required: false,
11955
+ immediate_default: false,
11956
+ worker_state: "completed",
11957
+ details: { outcome }
11958
+ }];
11959
+ }
11960
+ }
11961
+ if (input.kind === "proposal_reconciliation_required") {
11962
+ return [criticalWorkerAttention("worker.reconciliation_required")];
11963
+ }
11964
+ return [];
11965
+ }
11966
+ function proposalReviewAttentionKey(proposal, environment) {
11967
+ const contractDigest = proposal.change_set.contract?.digest;
11968
+ const requiredRole = typeof proposal.change_set.approval.required_role === "string" ? proposal.change_set.approval.required_role : "reviewer";
11969
+ const scopeDigest = canonicalJsonDigest({
11970
+ tenant_id: proposal.tenant_id,
11971
+ principal: proposal.principal ?? null
11972
+ });
11973
+ return [
11974
+ environment,
11975
+ "proposal.review_required",
11976
+ proposal.capability ?? proposal.action,
11977
+ contractDigest ?? "no_digest",
11978
+ requiredRole,
11979
+ scopeDigest
11980
+ ].join(":");
11981
+ }
11982
+ function safeAttentionPayloadString(payload, key) {
11983
+ const value = payload[key];
11984
+ if (typeof value !== "string" || !value.trim()) return void 0;
11985
+ return value.trim().slice(0, 128);
11986
+ }
11987
+ function safeAttentionDetails(payload, keys) {
11988
+ const details = {};
11989
+ for (const key of keys) {
11990
+ const value = payload[key];
11991
+ if (value === null || typeof value === "boolean") details[key] = value;
11992
+ else if (typeof value === "number" && Number.isFinite(value)) details[key] = value;
11993
+ else if (typeof value === "string" && value.trim()) details[key] = value.trim().slice(0, 128);
11994
+ }
11995
+ return details;
11996
+ }
11997
+ var attentionEventTypes = /* @__PURE__ */ new Set([
11998
+ "proposal.created",
11999
+ "proposal.review_required",
12000
+ "proposal.auto_approved",
12001
+ "proposal.approved",
12002
+ "proposal.queued",
12003
+ "proposal.expiring",
12004
+ "proposal.expired",
12005
+ "proposal.cancelled",
12006
+ "proposal.applied",
12007
+ "proposal.conflict",
12008
+ "proposal.refused",
12009
+ "worker.started",
12010
+ "worker.paused",
12011
+ "worker.unhealthy",
12012
+ "worker.recovered",
12013
+ "worker.queue_backlog",
12014
+ "worker.retry_scheduled",
12015
+ "worker.dead_lettered",
12016
+ "worker.unknown_outcome",
12017
+ "worker.reconciliation_required",
12018
+ "capability.review_required",
12019
+ "capability.activated",
12020
+ "capability.revoked",
12021
+ "contract.digest_stale",
12022
+ "schema.drift_detected",
12023
+ "credential.posture_changed",
12024
+ "policy.limit_near",
12025
+ "policy.limit_exceeded",
12026
+ "sensitive_override_activated",
12027
+ "notification.replayed",
12028
+ "notification.digest"
12029
+ ]);
12030
+ var defaultAttentionEventTypes = /* @__PURE__ */ new Set([
12031
+ "proposal.review_required",
12032
+ "proposal.expiring",
12033
+ "proposal.expired",
12034
+ "worker.unhealthy",
12035
+ "worker.queue_backlog",
12036
+ "worker.dead_lettered",
12037
+ "worker.unknown_outcome",
12038
+ "worker.reconciliation_required",
12039
+ "capability.review_required",
12040
+ "contract.digest_stale",
12041
+ "schema.drift_detected",
12042
+ "credential.posture_changed",
12043
+ "policy.limit_exceeded",
12044
+ "sensitive_override_activated"
12045
+ ]);
12046
+ var defaultImmediateAttentionEventTypes = /* @__PURE__ */ new Set([
12047
+ "proposal.review_required",
12048
+ "proposal.expiring",
12049
+ "worker.unhealthy",
12050
+ "worker.queue_backlog",
12051
+ "worker.dead_lettered",
12052
+ "worker.unknown_outcome",
12053
+ "worker.reconciliation_required",
12054
+ "contract.digest_stale",
12055
+ "schema.drift_detected",
12056
+ "credential.posture_changed",
12057
+ "policy.limit_exceeded"
12058
+ ]);
12059
+ function attentionEventTitle(eventType) {
12060
+ const titles = {
12061
+ "proposal.created": "Proposal created",
12062
+ "proposal.review_required": "Proposal needs human review",
12063
+ "proposal.auto_approved": "Proposal approved by reviewed policy",
12064
+ "proposal.approved": "Proposal approved",
12065
+ "proposal.queued": "Proposal queued for trusted execution",
12066
+ "proposal.expiring": "Approved proposal is approaching expiry",
12067
+ "proposal.expired": "Approved proposal expired without execution",
12068
+ "proposal.cancelled": "Proposal cancelled",
12069
+ "proposal.applied": "Proposal applied",
12070
+ "proposal.conflict": "Guarded writeback conflict",
12071
+ "proposal.refused": "Proposal refused",
12072
+ "worker.started": "Trusted worker started",
12073
+ "worker.paused": "Trusted worker paused",
12074
+ "worker.unhealthy": "Trusted worker needs attention",
12075
+ "worker.recovered": "Trusted worker supervision recovered",
12076
+ "worker.queue_backlog": "Trusted worker queue backlog",
12077
+ "worker.retry_scheduled": "Trusted worker retry scheduled",
12078
+ "worker.dead_lettered": "Trusted worker job dead-lettered",
12079
+ "worker.unknown_outcome": "Database transaction outcome is unknown",
12080
+ "worker.reconciliation_required": "Operator reconciliation is required",
12081
+ "capability.review_required": "Capability needs human review",
12082
+ "capability.activated": "Capability activated",
12083
+ "capability.revoked": "Capability revoked",
12084
+ "contract.digest_stale": "Active contract or policy authority is stale",
12085
+ "schema.drift_detected": "Schema drift blocks active authority",
12086
+ "credential.posture_changed": "Credential posture changed",
12087
+ "policy.limit_near": "Policy limit is approaching",
12088
+ "policy.limit_exceeded": "Policy limit exceeded",
12089
+ "sensitive_override_activated": "Sensitive-field override activated",
12090
+ "notification.replayed": "Notification delivery requeued by a verified operator",
12091
+ "notification.digest": "Runner activity digest"
12092
+ };
12093
+ return titles[eventType];
12094
+ }
12095
+ function assertAttentionEventInput(input) {
12096
+ if (!attentionEventTypes.has(input.event_type)) {
12097
+ throw new ProposalStoreError("ATTENTION_EVENT_TYPE_INVALID", `unsupported attention event type: ${String(input.event_type)}`);
12098
+ }
12099
+ if (!["informational", "warning", "critical"].includes(input.severity)) {
12100
+ throw new ProposalStoreError("ATTENTION_EVENT_SEVERITY_INVALID", `unsupported attention severity: ${String(input.severity)}`);
12101
+ }
12102
+ if (input.contract_digest && !/^sha256:[a-f0-9]{64}$/.test(input.contract_digest)) {
12103
+ throw new ProposalStoreError("ATTENTION_EVENT_DIGEST_INVALID", "attention event contract digest must be a lowercase sha256 digest");
12104
+ }
12105
+ for (const [label, value] of [["now", input.now], ["expires_at", input.expires_at]]) {
12106
+ if (value !== void 0 && !Number.isFinite(Date.parse(value))) {
12107
+ throw new ProposalStoreError("ATTENTION_EVENT_TIME_INVALID", `attention event ${label} must be an ISO timestamp`);
12108
+ }
12109
+ }
12110
+ const entries = Object.entries(input.details ?? {});
12111
+ if (entries.length > 32) {
12112
+ throw new ProposalStoreError("ATTENTION_EVENT_DETAILS_TOO_LARGE", "attention event details may contain at most 32 safe fields");
12113
+ }
12114
+ for (const [key, value] of entries) {
12115
+ if (!/^[a-z][a-z0-9_]{0,63}$/.test(key)) {
12116
+ throw new ProposalStoreError("ATTENTION_EVENT_DETAIL_KEY_INVALID", `attention event detail key is invalid: ${key}`);
12117
+ }
12118
+ if (typeof value === "string") boundedSafeLabel(value, `attention detail ${key}`, 256);
12119
+ else if (typeof value === "number" && !Number.isFinite(value)) {
12120
+ throw new ProposalStoreError("ATTENTION_EVENT_DETAIL_VALUE_INVALID", `attention event detail ${key} must be finite`);
12121
+ } else if (value !== null && typeof value !== "number" && typeof value !== "boolean") {
12122
+ throw new ProposalStoreError("ATTENTION_EVENT_DETAIL_VALUE_INVALID", `attention event detail ${key} must be scalar`);
12123
+ }
12124
+ }
12125
+ assertNoSecretMaterial(input.details ?? {}, "attention_event.details");
12126
+ }
12127
+ function boundedSafeLabel(value, label, maximum) {
12128
+ const trimmed = value.trim();
12129
+ if (!trimmed || trimmed.length > maximum || /[\u0000-\u001f\u007f]/.test(trimmed)) {
12130
+ throw new ProposalStoreError("ATTENTION_EVENT_FIELD_INVALID", `${label} must be a non-empty bounded single-line value`);
12131
+ }
12132
+ assertNoSecretMaterial({ value: trimmed }, label);
12133
+ return trimmed;
12134
+ }
12135
+ function boundedWorkbenchPath(value) {
12136
+ const path3 = boundedSafeLabel(value, "attention Workbench path", 512);
12137
+ if (!path3.startsWith("/") || path3.includes("?") || path3.includes("#") || path3.includes("://") || path3.includes("..")) {
12138
+ throw new ProposalStoreError(
12139
+ "ATTENTION_WORKBENCH_PATH_INVALID",
12140
+ "attention Workbench path must be a local absolute path without query, fragment, traversal, or authority"
12141
+ );
12142
+ }
12143
+ return path3;
12144
+ }
12145
+ function defaultAttentionKey(input) {
12146
+ const failureClass = typeof input.details.failure_class === "string" ? input.details.failure_class : typeof input.details.reason_code === "string" ? input.details.reason_code : "none";
12147
+ return [
12148
+ input.environment,
12149
+ input.event_type,
12150
+ input.capability ?? "global",
12151
+ input.contract_digest ?? "no_digest",
12152
+ failureClass
12153
+ ].join(":");
12154
+ }
12155
+ function attentionEventId(sourceEventKey, eventType) {
12156
+ const digest = canonicalJsonDigest({ source_event_key: sourceEventKey, event_type: eventType });
12157
+ return `aev_${digest.slice("sha256:".length)}`;
12158
+ }
12159
+ function attentionItemId(attentionKey) {
12160
+ const digest = canonicalJsonDigest({ attention_key: attentionKey });
12161
+ return `attn_${digest.slice("sha256:".length, "sha256:".length + 32)}`;
12162
+ }
12163
+ function boundedSinkId(value) {
12164
+ const sinkId = boundedSafeLabel(value, "notification sink id", 128);
12165
+ if (!/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(sinkId)) {
12166
+ throw new ProposalStoreError(
12167
+ "NOTIFICATION_SINK_ID_INVALID",
12168
+ "notification sink id must use letters, numbers, underscore, period, or hyphen"
12169
+ );
12170
+ }
12171
+ return sinkId;
12172
+ }
12173
+ function boundedSafeErrorCode(value) {
12174
+ const errorCode = boundedSafeLabel(value, "notification error code", 128);
12175
+ if (!/^[A-Z][A-Z0-9_]*$/.test(errorCode)) {
12176
+ throw new ProposalStoreError(
12177
+ "NOTIFICATION_ERROR_CODE_INVALID",
12178
+ "notification error code must be an uppercase safe code"
12179
+ );
12180
+ }
12181
+ return errorCode;
12182
+ }
12183
+ function notificationDeliveryId(sinkId, eventId) {
12184
+ const digest = canonicalJsonDigest({ sink_id: sinkId, event_id: eventId });
12185
+ return `ndl_${digest.slice("sha256:".length, "sha256:".length + 32)}`;
12186
+ }
12187
+ function notificationLeaseId(deliveryId, owner, attempt, now) {
12188
+ const digest = canonicalJsonDigest({
12189
+ delivery_id: deliveryId,
12190
+ owner,
12191
+ attempt,
12192
+ now
12193
+ });
12194
+ return `nlease_${digest.slice("sha256:".length, "sha256:".length + 32)}`;
12195
+ }
12196
+ function attentionSeverityRank(severity) {
12197
+ if (severity === "critical") return 3;
12198
+ if (severity === "warning") return 2;
12199
+ return 1;
12200
+ }
12201
+ function workerControlDecisionSubject(state, target) {
12202
+ assertWorkerControlTarget(target);
12203
+ const targetDigest = canonicalJsonDigest({
12204
+ schema_version: "synapsor.worker-control-decision-subject.v1",
12205
+ current_revision: state.revision,
12206
+ current_integrity_hash: state.integrity_hash,
12207
+ action: target.action,
12208
+ capability: target.capability ?? null,
12209
+ contract_digest: target.contract_digest ?? null
12210
+ });
12211
+ return {
12212
+ proposal_id: `worker_control_${targetDigest.slice("sha256:".length, "sha256:".length + 32)}`,
12213
+ proposal_version: state.revision + 1,
12214
+ proposal_hash: targetDigest
12215
+ };
12216
+ }
12217
+ function defaultWorkerControlState() {
12218
+ const unsigned = {
12219
+ schema_version: "synapsor.worker-control.v1",
12220
+ mode: "active",
12221
+ revision: 0,
12222
+ capability_controls: [],
12223
+ updated_at: "1970-01-01T00:00:00.000Z"
12224
+ };
12225
+ return { ...unsigned, integrity_hash: canonicalJsonDigest(unsigned) };
12226
+ }
12227
+ function parseWorkerControlState(value) {
12228
+ if (!isRecord3(value) || value.schema_version !== "synapsor.worker-control.v1" || !["active", "paused", "draining"].includes(String(value.mode)) || !Number.isSafeInteger(value.revision) || Number(value.revision) < 0 || !Array.isArray(value.capability_controls) || typeof value.updated_at !== "string" || typeof value.integrity_hash !== "string") {
12229
+ throw new ProposalStoreError("WORKER_CONTROL_STATE_INVALID", "stored supervised-worker control state is invalid");
12230
+ }
12231
+ const controls = value.capability_controls.map((entry) => {
12232
+ if (!isRecord3(entry) || typeof entry.capability !== "string" || !/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(entry.capability) || typeof entry.contract_digest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(entry.contract_digest) || !["enabled", "disabled", "revoked"].includes(String(entry.status)) || typeof entry.updated_at !== "string" || typeof entry.actor !== "string") {
12233
+ throw new ProposalStoreError("WORKER_CONTROL_STATE_INVALID", "stored supervised-worker capability control is invalid");
12234
+ }
12235
+ return {
12236
+ capability: entry.capability,
12237
+ contract_digest: entry.contract_digest,
12238
+ status: entry.status,
12239
+ updated_at: entry.updated_at,
12240
+ actor: entry.actor
12241
+ };
12242
+ });
12243
+ const lastDecision = isRecord3(value.last_decision) ? value.last_decision : void 0;
12244
+ const unsigned = {
12245
+ schema_version: "synapsor.worker-control.v1",
12246
+ mode: value.mode,
12247
+ revision: Number(value.revision),
12248
+ capability_controls: controls,
12249
+ ...lastDecision ? { last_decision: lastDecision } : {},
12250
+ updated_at: value.updated_at
12251
+ };
12252
+ const integrityHash = value.integrity_hash;
12253
+ if (integrityHash !== canonicalJsonDigest(unsigned)) {
12254
+ throw new ProposalStoreError("WORKER_CONTROL_STATE_TAMPERED", "stored supervised-worker control state failed integrity validation");
12255
+ }
12256
+ return { ...unsigned, integrity_hash: integrityHash };
12257
+ }
12258
+ function assertWorkerControlTarget(target) {
12259
+ const capabilityAction = target.action === "capability_enable" || target.action === "capability_disable" || target.action === "digest_revoke";
12260
+ if (capabilityAction) {
12261
+ if (!target.capability || !/^[A-Za-z_][A-Za-z0-9_.-]*$/.test(target.capability)) {
12262
+ throw new ProposalStoreError("WORKER_CONTROL_CAPABILITY_REQUIRED", `${target.action} requires a fixed capability name`);
12263
+ }
12264
+ if (!target.contract_digest || !/^sha256:[a-f0-9]{64}$/.test(target.contract_digest)) {
12265
+ throw new ProposalStoreError("WORKER_CONTROL_DIGEST_REQUIRED", `${target.action} requires an exact sha256 contract digest`);
12266
+ }
12267
+ } else if (target.capability || target.contract_digest) {
12268
+ throw new ProposalStoreError("WORKER_CONTROL_TARGET_INVALID", `${target.action} is a global control and cannot carry capability authority`);
12269
+ }
12270
+ }
12271
+ function workerControlOperatorAction(action) {
12272
+ if (action === "pause") return "worker_pause";
12273
+ if (action === "resume") return "worker_resume";
12274
+ if (action === "drain") return "worker_drain";
12275
+ if (action === "capability_enable") return "worker_capability_enable";
12276
+ if (action === "capability_disable") return "worker_capability_disable";
12277
+ return "worker_digest_revoke";
12278
+ }
12279
+ function assertWorkerControlOperatorDecision(state, target, actor, identity, requireVerified) {
12280
+ if (requireVerified && (!identity || !identity.verified || identity.provider === "dev_env")) {
12281
+ throw new ProposalStoreError(
12282
+ "VERIFIED_OPERATOR_IDENTITY_REQUIRED",
12283
+ `verified operator identity is required to ${target.action} supervised execution`
12284
+ );
12285
+ }
12286
+ if (!identity) return;
12287
+ if (identity.subject !== actor || identity.decision.subject !== actor) {
12288
+ throw new ProposalStoreError("OPERATOR_IDENTITY_MISMATCH", "worker-control identity does not match its actor");
12289
+ }
12290
+ const subject = workerControlDecisionSubject(state, target);
12291
+ if (identity.decision.action !== workerControlOperatorAction(target.action) || identity.decision.proposal_id !== subject.proposal_id || identity.decision.proposal_version !== subject.proposal_version || identity.decision.proposal_hash !== subject.proposal_hash) {
12292
+ throw new ProposalStoreError("OPERATOR_DECISION_MISMATCH", "operator proof is not bound to this exact worker-control revision");
12293
+ }
12294
+ if (identity.decision_hash !== canonicalJsonDigest(identity.decision)) {
12295
+ throw new ProposalStoreError("OPERATOR_IDENTITY_TAMPERED", "worker-control decision hash failed integrity validation");
12296
+ }
12297
+ const { integrity_hash: _integrityHash, ...identityCore } = identity;
12298
+ if (identity.integrity_hash !== canonicalJsonDigest(identityCore)) {
12299
+ throw new ProposalStoreError("OPERATOR_IDENTITY_TAMPERED", "worker-control identity proof failed integrity validation");
12300
+ }
12301
+ }
12302
+ function workerControlSummary(target) {
12303
+ if (target.action === "pause") return "Supervised execution paused by an operator";
12304
+ if (target.action === "resume") return "Supervised execution resumed by an operator";
12305
+ if (target.action === "drain") return "Supervised execution is draining without new leases";
12306
+ if (target.action === "capability_enable") return `${target.capability} supervised execution enabled for the exact reviewed digest`;
12307
+ if (target.action === "capability_disable") return `${target.capability} supervised execution disabled for the exact reviewed digest`;
12308
+ return `${target.capability} supervised execution digest revoked`;
12309
+ }
12310
+ function attentionDecisionSubject(item) {
12311
+ return {
12312
+ proposal_id: item.attention_id,
12313
+ proposal_version: item.occurrence_count,
12314
+ proposal_hash: canonicalJsonDigest({
12315
+ schema_version: "synapsor.attention-decision-subject.v1",
12316
+ attention_id: item.attention_id,
12317
+ attention_key: item.attention_key,
12318
+ status: item.status,
12319
+ severity: item.severity,
12320
+ environment: item.environment,
12321
+ event_type: item.event_type,
12322
+ capability: item.capability ?? null,
12323
+ contract_digest: item.contract_digest ?? null,
12324
+ occurrence_count: item.occurrence_count,
12325
+ latest_event_id: item.latest_event_id,
12326
+ last_seen_at: item.last_seen_at
12327
+ })
12328
+ };
12329
+ }
12330
+ function notificationReplayDecisionSubject(delivery) {
12331
+ return {
12332
+ proposal_id: delivery.delivery_id,
12333
+ proposal_version: Math.max(1, delivery.attempts),
12334
+ proposal_hash: canonicalJsonDigest({
12335
+ schema_version: "synapsor.notification-replay-decision-subject.v1",
12336
+ delivery_id: delivery.delivery_id,
12337
+ sink_id: delivery.sink_id,
12338
+ event_id: delivery.event_id,
12339
+ attention_id: delivery.attention_id ?? null,
12340
+ status: delivery.status,
12341
+ attempts: delivery.attempts,
12342
+ max_attempts: delivery.max_attempts,
12343
+ last_error_code: delivery.last_error_code ?? null,
12344
+ updated_at: delivery.updated_at
12345
+ })
12346
+ };
12347
+ }
12348
+ function assertNotificationReplayOperatorDecision(delivery, identity, reason) {
12349
+ if (!identity.verified || identity.provider === "dev_env") {
12350
+ throw new ProposalStoreError(
12351
+ "VERIFIED_OPERATOR_IDENTITY_REQUIRED",
12352
+ `verified operator identity is required to replay notification delivery ${delivery.delivery_id}`
12353
+ );
12354
+ }
12355
+ if (identity.subject !== identity.decision.subject) {
12356
+ throw new ProposalStoreError(
12357
+ "OPERATOR_IDENTITY_MISMATCH",
12358
+ "notification replay identity does not match its signed decision subject"
12359
+ );
12360
+ }
12361
+ const subject = notificationReplayDecisionSubject(delivery);
12362
+ if (identity.decision.action !== "notification_replay" || identity.decision.proposal_id !== subject.proposal_id || identity.decision.proposal_version !== subject.proposal_version || identity.decision.proposal_hash !== subject.proposal_hash || identity.decision.reason !== reason) {
12363
+ throw new ProposalStoreError(
12364
+ "OPERATOR_DECISION_MISMATCH",
12365
+ "operator proof is not bound to this exact notification delivery revision and replay reason"
12366
+ );
9633
12367
  }
9634
- }
9635
- function assertWritebackAllowed(proposal, operation) {
9636
- if (proposal.change_set.mode === "shadow") {
12368
+ if (identity.decision_hash !== canonicalJsonDigest(identity.decision)) {
9637
12369
  throw new ProposalStoreError(
9638
- "SHADOW_WRITEBACK_DISABLED",
9639
- `shadow proposal ${proposal.proposal_id} cannot be ${operation}; shadow mode stores proposals, evidence, query audit, and replay only and never mutates the source database`
12370
+ "OPERATOR_IDENTITY_TAMPERED",
12371
+ "notification replay decision hash failed its integrity check"
9640
12372
  );
9641
12373
  }
9642
- if (proposal.change_set.mode === "read_only") {
12374
+ const { integrity_hash: _integrityHash, ...core } = identity;
12375
+ const canonicalCore = JSON.parse(JSON.stringify(core));
12376
+ if (identity.integrity_hash !== canonicalJsonDigest(canonicalCore)) {
9643
12377
  throw new ProposalStoreError(
9644
- "READ_ONLY_WRITEBACK_DISABLED",
9645
- `read-only proposal ${proposal.proposal_id} cannot be ${operation}; read-only mode does not allow proposal writeback`
12378
+ "OPERATOR_IDENTITY_TAMPERED",
12379
+ "notification replay identity proof failed its integrity check"
9646
12380
  );
9647
12381
  }
9648
12382
  }
9649
- var secretKeyPattern = /(^|[_-])(password|passwd|secret|token|api[_-]?key|access[_-]?key|private[_-]?key|cookie|credential|connection[_-]?string|database[_-]?url|read[_-]?url|write[_-]?url)($|[_-])/i;
9650
- var secretValuePattern = /(postgres(?:ql)?:\/\/|mysql:\/\/|Bearer\s+[A-Za-z0-9._~+/=-]+|syn_wbr_[A-Za-z0-9._~+/=-]+|-----BEGIN [A-Z ]*PRIVATE KEY-----)/i;
9651
- function assertNoSecretMaterial(value, path3) {
9652
- if (Array.isArray(value)) {
9653
- value.forEach((item, index) => assertNoSecretMaterial(item, `${path3}[${index}]`));
9654
- return;
12383
+ function assertAttentionOperatorDecision(item, actor, identity, requireVerified) {
12384
+ if (requireVerified && (!identity || !identity.verified || identity.provider === "dev_env")) {
12385
+ throw new ProposalStoreError(
12386
+ "VERIFIED_OPERATOR_IDENTITY_REQUIRED",
12387
+ `verified operator identity is required to acknowledge attention item ${item.attention_id}`
12388
+ );
9655
12389
  }
9656
- if (isRecord3(value)) {
9657
- for (const [key, entry] of Object.entries(value)) {
9658
- const entryPath = `${path3}.${key}`;
9659
- if (secretKeyPattern.test(key)) {
9660
- throw new ProposalStoreError(
9661
- "SECRET_MATERIAL_REJECTED",
9662
- `refusing to persist secret-like field ${entryPath}; remove it from reviewed visible/evidence/query-audit data`
9663
- );
9664
- }
9665
- assertNoSecretMaterial(entry, entryPath);
9666
- }
9667
- return;
12390
+ if (!identity) return;
12391
+ if (identity.subject !== actor || identity.decision.subject !== actor) {
12392
+ throw new ProposalStoreError(
12393
+ "OPERATOR_IDENTITY_MISMATCH",
12394
+ `operator identity ${identity.subject} does not match attention acknowledgement actor ${actor}`
12395
+ );
9668
12396
  }
9669
- if (typeof value === "string" && secretValuePattern.test(value)) {
12397
+ const subject = attentionDecisionSubject(item);
12398
+ if (identity.decision.action !== "attention_acknowledge" || identity.decision.proposal_id !== subject.proposal_id || identity.decision.proposal_version !== subject.proposal_version || identity.decision.proposal_hash !== subject.proposal_hash) {
9670
12399
  throw new ProposalStoreError(
9671
- "SECRET_MATERIAL_REJECTED",
9672
- `refusing to persist secret-like value at ${path3}; remove it from reviewed visible/evidence/query-audit data`
12400
+ "OPERATOR_DECISION_MISMATCH",
12401
+ "operator proof is not bound to this exact attention item version"
12402
+ );
12403
+ }
12404
+ if (identity.decision_hash !== canonicalJsonDigest(identity.decision)) {
12405
+ throw new ProposalStoreError(
12406
+ "OPERATOR_IDENTITY_TAMPERED",
12407
+ "attention acknowledgement decision hash failed its integrity check"
12408
+ );
12409
+ }
12410
+ const { integrity_hash: _integrityHash, ...core } = identity;
12411
+ const canonicalCore = JSON.parse(JSON.stringify(core));
12412
+ if (identity.integrity_hash !== canonicalJsonDigest(canonicalCore)) {
12413
+ throw new ProposalStoreError(
12414
+ "OPERATOR_IDENTITY_TAMPERED",
12415
+ "attention acknowledgement identity proof failed its integrity check"
9673
12416
  );
9674
12417
  }
9675
12418
  }
9676
- function rowToProposal(row) {
12419
+ function rowToAttentionEvent(row) {
9677
12420
  if (!isRecord3(row)) return void 0;
9678
- return {
9679
- proposal_id: String(row.proposal_id),
9680
- proposal_version: Number(row.proposal_version),
9681
- proposal_hash: String(row.proposal_hash),
9682
- action: String(row.action),
9683
- state: String(row.state),
9684
- tenant_id: String(row.tenant_id),
9685
- principal: row.principal == null ? void 0 : String(row.principal),
9686
- capability: row.capability == null ? void 0 : String(row.capability),
9687
- interaction_id: row.interaction_id == null ? void 0 : String(row.interaction_id),
9688
- tool_call_id: row.tool_call_id == null ? void 0 : String(row.tool_call_id),
9689
- business_object: String(row.business_object),
9690
- object_id: String(row.object_id),
9691
- source_kind: String(row.source_kind),
9692
- source_id: String(row.source_id),
9693
- source_schema: String(row.source_schema),
9694
- source_table: String(row.source_table),
9695
- source_database_mutated: Number(row.source_database_mutated) === 1,
9696
- change_set: parseChangeSet(JSON.parse(String(row.change_set_json))),
9697
- created_at: String(row.created_at),
9698
- updated_at: String(row.updated_at)
12421
+ const eventType = String(row.event_type);
12422
+ const severity = String(row.severity);
12423
+ if (!attentionEventTypes.has(eventType) || !["informational", "warning", "critical"].includes(severity)) return void 0;
12424
+ let details;
12425
+ try {
12426
+ const parsed = JSON.parse(String(row.details_json));
12427
+ if (!isRecord3(parsed)) return void 0;
12428
+ details = {};
12429
+ for (const [key, value] of Object.entries(parsed)) {
12430
+ if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") return void 0;
12431
+ details[key] = value;
12432
+ }
12433
+ } catch {
12434
+ return void 0;
12435
+ }
12436
+ const event = {
12437
+ schema_version: "synapsor.attention-event.v1",
12438
+ event_id: String(row.event_id),
12439
+ event_type: eventType,
12440
+ severity,
12441
+ occurred_at: String(row.occurred_at),
12442
+ environment: String(row.environment),
12443
+ ...row.proposal_id == null ? {} : { proposal_id: String(row.proposal_id) },
12444
+ ...row.job_id == null ? {} : { job_id: String(row.job_id) },
12445
+ ...row.operation_id == null ? {} : { operation_id: String(row.operation_id) },
12446
+ ...row.correlation_id == null ? {} : { correlation_id: String(row.correlation_id) },
12447
+ ...row.capability == null ? {} : { capability: String(row.capability) },
12448
+ ...row.contract_digest == null ? {} : { contract_digest: String(row.contract_digest) },
12449
+ ...row.attention_key == null ? {} : { attention_key: String(row.attention_key) },
12450
+ attention_required: Number(row.attention_required) === 1,
12451
+ immediate_default: Number(row.immediate_default) === 1,
12452
+ summary: String(row.summary),
12453
+ ...row.approval_source === "human" || row.approval_source === "policy_auto" ? { approval_source: row.approval_source } : {},
12454
+ ...row.worker_state == null ? {} : { worker_state: String(row.worker_state) },
12455
+ ...row.failure_class == null ? {} : { failure_class: String(row.failure_class) },
12456
+ ...row.expires_at == null ? {} : { expires_at: String(row.expires_at) },
12457
+ ...row.workbench_path == null ? {} : { workbench_path: String(row.workbench_path) },
12458
+ details,
12459
+ payload_hash: String(row.payload_hash)
9699
12460
  };
12461
+ const { payload_hash: _payloadHash, ...unsigned } = event;
12462
+ if (event.payload_hash !== canonicalJsonDigest(unsigned)) {
12463
+ throw new ProposalStoreError("ATTENTION_EVENT_CORRUPT", `attention event ${event.event_id} failed its integrity check`);
12464
+ }
12465
+ return event;
9700
12466
  }
9701
- function rowToEvent(row) {
12467
+ function rowToAttentionItem(row) {
9702
12468
  if (!isRecord3(row)) return void 0;
12469
+ const status = String(row.status);
12470
+ const severity = String(row.severity);
12471
+ const eventType = String(row.event_type);
12472
+ if (!["open", "acknowledged", "resolved", "expired"].includes(status)) return void 0;
12473
+ if (!["informational", "warning", "critical"].includes(severity) || !attentionEventTypes.has(eventType)) return void 0;
12474
+ let acknowledgementIdentity;
12475
+ if (row.acknowledgement_identity_json != null) {
12476
+ try {
12477
+ const parsed = JSON.parse(String(row.acknowledgement_identity_json));
12478
+ if (!isRecord3(parsed) || !isRecord3(parsed.decision)) return void 0;
12479
+ acknowledgementIdentity = parsed;
12480
+ } catch {
12481
+ return void 0;
12482
+ }
12483
+ }
9703
12484
  return {
9704
- event_id: Number(row.event_id),
9705
- proposal_id: String(row.proposal_id),
9706
- kind: String(row.kind),
9707
- actor: String(row.actor),
9708
- payload: JSON.parse(String(row.payload_json)),
9709
- created_at: String(row.created_at)
12485
+ attention_id: String(row.attention_id),
12486
+ attention_key: String(row.attention_key),
12487
+ status,
12488
+ severity,
12489
+ environment: String(row.environment),
12490
+ event_type: eventType,
12491
+ capability: row.capability == null ? void 0 : String(row.capability),
12492
+ contract_digest: row.contract_digest == null ? void 0 : String(row.contract_digest),
12493
+ title: String(row.title),
12494
+ occurrence_count: Number(row.occurrence_count),
12495
+ first_event_id: String(row.first_event_id),
12496
+ latest_event_id: String(row.latest_event_id),
12497
+ first_seen_at: String(row.first_seen_at),
12498
+ last_seen_at: String(row.last_seen_at),
12499
+ acknowledged_by: row.acknowledged_by == null ? void 0 : String(row.acknowledged_by),
12500
+ acknowledged_at: row.acknowledged_at == null ? void 0 : String(row.acknowledged_at),
12501
+ acknowledgement_identity: acknowledgementIdentity,
12502
+ resolved_at: row.resolved_at == null ? void 0 : String(row.resolved_at),
12503
+ expires_at: row.expires_at == null ? void 0 : String(row.expires_at)
9710
12504
  };
9711
12505
  }
9712
- function rowToApproval(row) {
12506
+ function rowToNotificationDelivery(row) {
9713
12507
  if (!isRecord3(row)) return void 0;
9714
12508
  const status = String(row.status);
9715
- if (status !== "approved" && status !== "rejected") return void 0;
12509
+ if (![
12510
+ "pending",
12511
+ "leased",
12512
+ "delivered",
12513
+ "retry_wait",
12514
+ "dead_letter",
12515
+ "suppressed",
12516
+ "batched"
12517
+ ].includes(status)) return void 0;
9716
12518
  return {
9717
- approval_id: Number(row.approval_id),
9718
- proposal_id: String(row.proposal_id),
9719
- proposal_version: Number(row.proposal_version),
9720
- proposal_hash: String(row.proposal_hash),
9721
- approver: String(row.approver),
12519
+ delivery_id: String(row.delivery_id),
12520
+ sink_id: String(row.sink_id),
12521
+ event_id: String(row.event_id),
12522
+ ...row.attention_id == null ? {} : { attention_id: String(row.attention_id) },
9722
12523
  status,
9723
- reason: row.reason == null ? void 0 : String(row.reason),
9724
- identity: row.identity_json == null ? void 0 : JSON.parse(String(row.identity_json)),
9725
- decision_hash: row.decision_hash == null ? void 0 : String(row.decision_hash),
9726
- signature: row.signature == null ? void 0 : String(row.signature),
9727
- integrity_hash: row.integrity_hash == null ? void 0 : String(row.integrity_hash),
9728
- freshness_proof_digest: row.freshness_proof_digest == null ? void 0 : String(row.freshness_proof_digest),
9729
- created_at: String(row.created_at)
12524
+ attempts: Number(row.attempts),
12525
+ max_attempts: Number(row.max_attempts),
12526
+ next_attempt_at: String(row.next_attempt_at),
12527
+ ...row.lease_owner == null ? {} : { lease_owner: String(row.lease_owner) },
12528
+ ...row.lease_id == null ? {} : { lease_id: String(row.lease_id) },
12529
+ ...row.lease_expires_at == null ? {} : { lease_expires_at: String(row.lease_expires_at) },
12530
+ ...row.last_error_code == null ? {} : { last_error_code: String(row.last_error_code) },
12531
+ ...row.external_reference == null ? {} : { external_reference: String(row.external_reference) },
12532
+ ...row.delivered_at == null ? {} : { delivered_at: String(row.delivered_at) },
12533
+ created_at: String(row.created_at),
12534
+ updated_at: String(row.updated_at)
9730
12535
  };
9731
12536
  }
9732
- function rowToPolicyRecommendation(row) {
9733
- if (!isRecord3(row)) return void 0;
9734
- let unsigned;
9735
- try {
9736
- unsigned = JSON.parse(String(row.payload_json));
9737
- } catch {
9738
- throw new ProposalStoreError("POLICY_RECOMMENDATION_TAMPERED", "policy recommendation payload is not valid JSON");
9739
- }
9740
- const recommendation = { ...unsigned, integrity_hash: String(row.integrity_hash) };
9741
- assertPolicyRecommendationShape(recommendation);
9742
- if (recommendation.recommendation_id !== String(row.recommendation_id) || recommendation.tenant_id !== String(row.tenant_id) || recommendation.capability !== String(row.capability) || recommendation.policy !== String(row.policy) || recommendation.base_contract_digest !== String(row.base_contract_digest) || recommendation.status !== String(row.status)) throw new ProposalStoreError("POLICY_RECOMMENDATION_TAMPERED", `policy recommendation ${String(row.recommendation_id)} index fields do not match its signed payload`);
9743
- const expected = canonicalJsonDigest(policyRecommendationUnsigned(recommendation));
9744
- if (recommendation.integrity_hash !== expected) throw new ProposalStoreError("POLICY_RECOMMENDATION_TAMPERED", `policy recommendation ${recommendation.recommendation_id} failed its integrity check`);
9745
- return recommendation;
9746
- }
9747
- function policyRecommendationUnsigned(recommendation) {
9748
- const { integrity_hash: _integrityHash, ...unsigned } = recommendation;
9749
- return unsigned;
9750
- }
9751
- function assertPolicyRecommendationShape(recommendation) {
9752
- if (recommendation.schema_version !== "synapsor.policy-recommendation.v1") throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "unsupported policy recommendation schema version");
9753
- if (!/^ptr_[a-f0-9]{20}$/.test(recommendation.recommendation_id)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation id is invalid");
9754
- for (const [name, value] of [["tenant", recommendation.tenant_id], ["capability", recommendation.capability], ["policy", recommendation.policy], ["field", recommendation.field]]) {
9755
- if (!value || value.length > 256) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", `policy recommendation ${name} is invalid`);
9756
- }
9757
- if (!/^sha256:[a-f0-9]{64}$/.test(recommendation.base_contract_digest)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation base digest is invalid");
9758
- if (!/^sha256:[a-f0-9]{64}$/.test(recommendation.integrity_hash)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation integrity digest is invalid");
9759
- if (!Number.isFinite(recommendation.current_threshold) || !Number.isFinite(recommendation.proposed_threshold) || !Number.isFinite(recommendation.maximum_increment) || !Number.isFinite(recommendation.absolute_ceiling)) {
9760
- throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation thresholds must be finite numbers");
9761
- }
9762
- if (recommendation.proposed_threshold <= recommendation.current_threshold || recommendation.proposed_threshold - recommendation.current_threshold > recommendation.maximum_increment || recommendation.proposed_threshold > recommendation.absolute_ceiling) {
9763
- throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation exceeds its reviewed increment or ceiling");
9764
- }
9765
- if (!Array.isArray(recommendation.evidence_proposal_ids) || recommendation.evidence_proposal_ids.length === 0 || recommendation.evidence_proposal_ids.length > 1e4) {
9766
- throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation requires bounded proposal evidence");
9767
- }
9768
- if (!isRecord3(recommendation.metrics) || !isRecord3(recommendation.criteria)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation criteria and metrics are required");
9769
- if (!["pending_review", "approved", "rejected", "exported"].includes(recommendation.status)) throw new ProposalStoreError("POLICY_RECOMMENDATION_INVALID", "policy recommendation status is invalid");
9770
- }
9771
- function assertPolicyRecommendationIdentity(recommendation, input) {
9772
- const proof = input.identity;
9773
- if (!proof.verified || proof.provider === "dev_env") throw new ProposalStoreError("POLICY_RECOMMENDATION_VERIFIED_IDENTITY_REQUIRED", "policy recommendation decisions require a cryptographically verified operator identity");
9774
- if (proof.subject !== input.actor || proof.decision.subject !== input.actor) throw new ProposalStoreError("POLICY_RECOMMENDATION_IDENTITY_MISMATCH", "policy recommendation actor does not match the verified identity");
9775
- if (proof.decision.action !== input.action || proof.decision.proposal_id !== recommendation.recommendation_id || proof.decision.proposal_hash !== recommendation.integrity_hash || proof.decision.proposal_version !== 1) {
9776
- throw new ProposalStoreError("POLICY_RECOMMENDATION_IDENTITY_MISMATCH", "verified operator decision is not bound to this policy recommendation version");
9777
- }
9778
- const { integrity_hash: _integrityHash, ...core } = proof;
9779
- const canonicalCore = JSON.parse(JSON.stringify(core));
9780
- if (proof.integrity_hash !== canonicalJsonDigest(canonicalCore)) throw new ProposalStoreError("POLICY_RECOMMENDATION_IDENTITY_TAMPERED", "verified operator identity proof failed its integrity check");
9781
- }
9782
12537
  function rowToWorkerQueueItem(row) {
9783
12538
  if (!isRecord3(row)) return void 0;
9784
12539
  const status = String(row.status);
9785
- if (!["queued", "leased", "retry_wait", "completed", "dead_letter", "discarded"].includes(status)) return void 0;
12540
+ if (![
12541
+ "queued",
12542
+ "leased",
12543
+ "retry_wait",
12544
+ "completed",
12545
+ "dead_letter",
12546
+ "discarded",
12547
+ "cancelled",
12548
+ "blocked",
12549
+ "reconciliation_required"
12550
+ ].includes(status)) return void 0;
12551
+ const executionMode = row.execution_mode == null ? "legacy" : String(row.execution_mode);
12552
+ if (executionMode !== "legacy" && executionMode !== "supervised_worker") return void 0;
9786
12553
  return {
9787
12554
  proposal_id: String(row.proposal_id),
9788
12555
  status,
12556
+ execution_mode: executionMode,
12557
+ contract_digest: row.contract_digest == null ? void 0 : String(row.contract_digest),
9789
12558
  attempts: Number(row.attempts),
9790
12559
  max_attempts: Number(row.max_attempts),
9791
12560
  next_attempt_at: String(row.next_attempt_at),
9792
12561
  lease_owner: row.lease_owner == null ? void 0 : String(row.lease_owner),
12562
+ lease_id: row.lease_id == null ? void 0 : String(row.lease_id),
9793
12563
  lease_expires_at: row.lease_expires_at == null ? void 0 : String(row.lease_expires_at),
9794
12564
  last_error_code: row.last_error_code == null ? void 0 : String(row.last_error_code),
12565
+ terminal_outcome: row.terminal_outcome == null ? void 0 : String(row.terminal_outcome),
9795
12566
  created_at: String(row.created_at),
9796
12567
  updated_at: String(row.updated_at)
9797
12568
  };
9798
12569
  }
12570
+ function workerLeaseId(proposalId, workerId, attempt, now) {
12571
+ const prefixLength = "sha256:".length;
12572
+ return `wlease_${canonicalJsonDigest({ proposal_id: proposalId, worker_id: workerId, attempt, now }).slice(prefixLength, prefixLength + 32)}`;
12573
+ }
9799
12574
  function rowToCloudOutboxItem(row) {
9800
12575
  if (!isRecord3(row)) return void 0;
9801
12576
  const kind = String(row.kind);
@@ -9943,7 +12718,17 @@ function rowToWritebackIntent(row) {
9943
12718
  };
9944
12719
  }
9945
12720
  function isStoredWritebackOperation(operation) {
9946
- return ["single_row_update", "single_row_insert", "single_row_delete", "set_update", "set_delete", "batch_insert"].includes(operation);
12721
+ return [
12722
+ "single_row_update",
12723
+ "single_row_insert",
12724
+ "single_row_delete",
12725
+ "set_update",
12726
+ "set_delete",
12727
+ "batch_insert",
12728
+ "restore_update",
12729
+ "remove_insert",
12730
+ "restore_insert"
12731
+ ].includes(operation);
9947
12732
  }
9948
12733
  function rowToQueryAudit(row) {
9949
12734
  if (!isRecord3(row)) return void 0;
@@ -10054,7 +12839,9 @@ var sharedLedgerJsonColumns = /* @__PURE__ */ new Set([
10054
12839
  "patch_json",
10055
12840
  "selected_capabilities_json",
10056
12841
  "proposed_effect_json",
10057
- "actual_effect_json"
12842
+ "actual_effect_json",
12843
+ "details_json",
12844
+ "acknowledgement_identity_json"
10058
12845
  ]);
10059
12846
  var sharedLedgerKindToTable = {
10060
12847
  proposal: "proposals",
@@ -10073,6 +12860,9 @@ var sharedLedgerKindToTable = {
10073
12860
  shadow_study_case: "shadow_study_cases",
10074
12861
  shadow_outcome: "shadow_outcomes",
10075
12862
  worker_queue_item: "worker_queue",
12863
+ attention_event: "attention_events",
12864
+ attention_item: "attention_items",
12865
+ notification_delivery: "notification_deliveries",
10076
12866
  runner_state: "runner_state",
10077
12867
  policy_recommendation: "policy_recommendations",
10078
12868
  cloud_outbox_event: "cloud_outbox",
@@ -10115,7 +12905,142 @@ var sharedLedgerRestoreSpecs = {
10115
12905
  shadow_studies: restoreSpec("study_id", ["study_id", "name", "description", "selected_capabilities_json", "starts_at", "ends_at", "status", "created_at", "updated_at"], ["study_id", "name", "selected_capabilities_json", "status", "created_at", "updated_at"]),
10116
12906
  shadow_study_cases: restoreSpec("case_id", ["case_id", "study_id", "request_id", "proposal_id", "tenant_id", "principal", "capability", "business_object", "object_id", "evidence_bundle_id", "proposed_effect_json", "agent_result", "decision_reason", "risk_score", "amount_value", "created_at"], ["case_id", "study_id", "request_id", "tenant_id", "capability", "business_object", "object_id", "agent_result", "created_at"]),
10117
12907
  shadow_outcomes: restoreSpec("outcome_id", ["outcome_id", "study_id", "request_id", "proposal_id", "tenant_id", "business_object", "object_id", "actor", "disposition", "actual_effect_json", "occurred_at", "source", "reference", "reason", "created_at"], ["outcome_id", "study_id", "request_id", "tenant_id", "business_object", "object_id", "actor", "disposition", "occurred_at", "source", "created_at"]),
10118
- worker_queue: restoreSpec("proposal_id", ["proposal_id", "status", "attempts", "max_attempts", "next_attempt_at", "lease_owner", "lease_expires_at", "last_error_code", "created_at", "updated_at"], ["proposal_id", "status", "attempts", "max_attempts", "next_attempt_at", "created_at", "updated_at"]),
12908
+ worker_queue: restoreSpec(
12909
+ "proposal_id",
12910
+ [
12911
+ "proposal_id",
12912
+ "status",
12913
+ "execution_mode",
12914
+ "contract_digest",
12915
+ "attempts",
12916
+ "max_attempts",
12917
+ "next_attempt_at",
12918
+ "lease_owner",
12919
+ "lease_id",
12920
+ "lease_expires_at",
12921
+ "last_error_code",
12922
+ "terminal_outcome",
12923
+ "created_at",
12924
+ "updated_at"
12925
+ ],
12926
+ ["proposal_id", "status", "attempts", "max_attempts", "next_attempt_at", "created_at", "updated_at"]
12927
+ ),
12928
+ attention_events: restoreSpec(
12929
+ "event_id",
12930
+ [
12931
+ "event_id",
12932
+ "schema_version",
12933
+ "event_type",
12934
+ "severity",
12935
+ "occurred_at",
12936
+ "environment",
12937
+ "proposal_id",
12938
+ "job_id",
12939
+ "operation_id",
12940
+ "correlation_id",
12941
+ "capability",
12942
+ "contract_digest",
12943
+ "attention_key",
12944
+ "attention_required",
12945
+ "immediate_default",
12946
+ "summary",
12947
+ "approval_source",
12948
+ "worker_state",
12949
+ "failure_class",
12950
+ "expires_at",
12951
+ "workbench_path",
12952
+ "details_json",
12953
+ "payload_hash",
12954
+ "created_at"
12955
+ ],
12956
+ [
12957
+ "event_id",
12958
+ "schema_version",
12959
+ "event_type",
12960
+ "severity",
12961
+ "occurred_at",
12962
+ "environment",
12963
+ "attention_required",
12964
+ "immediate_default",
12965
+ "summary",
12966
+ "details_json",
12967
+ "payload_hash",
12968
+ "created_at"
12969
+ ]
12970
+ ),
12971
+ attention_items: restoreSpec(
12972
+ "attention_id",
12973
+ [
12974
+ "attention_id",
12975
+ "attention_key",
12976
+ "status",
12977
+ "severity",
12978
+ "environment",
12979
+ "event_type",
12980
+ "capability",
12981
+ "contract_digest",
12982
+ "title",
12983
+ "occurrence_count",
12984
+ "first_event_id",
12985
+ "latest_event_id",
12986
+ "first_seen_at",
12987
+ "last_seen_at",
12988
+ "acknowledged_by",
12989
+ "acknowledged_at",
12990
+ "acknowledgement_identity_json",
12991
+ "acknowledgement_decision_hash",
12992
+ "acknowledgement_signature",
12993
+ "acknowledgement_integrity_hash",
12994
+ "resolved_at",
12995
+ "expires_at"
12996
+ ],
12997
+ [
12998
+ "attention_id",
12999
+ "attention_key",
13000
+ "status",
13001
+ "severity",
13002
+ "environment",
13003
+ "event_type",
13004
+ "title",
13005
+ "occurrence_count",
13006
+ "first_event_id",
13007
+ "latest_event_id",
13008
+ "first_seen_at",
13009
+ "last_seen_at"
13010
+ ]
13011
+ ),
13012
+ notification_deliveries: restoreSpec(
13013
+ "delivery_id",
13014
+ [
13015
+ "delivery_id",
13016
+ "sink_id",
13017
+ "event_id",
13018
+ "attention_id",
13019
+ "status",
13020
+ "attempts",
13021
+ "max_attempts",
13022
+ "next_attempt_at",
13023
+ "lease_owner",
13024
+ "lease_id",
13025
+ "lease_expires_at",
13026
+ "last_error_code",
13027
+ "external_reference",
13028
+ "delivered_at",
13029
+ "created_at",
13030
+ "updated_at"
13031
+ ],
13032
+ [
13033
+ "delivery_id",
13034
+ "sink_id",
13035
+ "event_id",
13036
+ "status",
13037
+ "attempts",
13038
+ "max_attempts",
13039
+ "next_attempt_at",
13040
+ "created_at",
13041
+ "updated_at"
13042
+ ]
13043
+ ),
10119
13044
  runner_state: restoreSpec("key", ["key", "value_json", "updated_at"], ["key", "value_json", "updated_at"]),
10120
13045
  policy_recommendations: restoreSpec("recommendation_id", ["recommendation_id", "tenant_id", "capability", "policy", "base_contract_digest", "status", "payload_json", "integrity_hash", "created_at", "updated_at"], ["recommendation_id", "tenant_id", "capability", "policy", "base_contract_digest", "status", "payload_json", "integrity_hash", "created_at", "updated_at"]),
10121
13046
  cloud_outbox: restoreSpec("event_id", ["event_id", "proposal_id", "sequence", "kind", "status", "payload_hash", "payload_json", "attempts", "max_attempts", "next_attempt_at", "lease_owner", "lease_expires_at", "last_error_code", "sent_at", "acknowledged_at", "created_at", "updated_at"], ["event_id", "sequence", "kind", "status", "payload_hash", "payload_json", "attempts", "max_attempts", "next_attempt_at", "created_at", "updated_at"]),
@@ -10164,6 +13089,9 @@ function sharedLedgerRestoreRank(entry) {
10164
13089
  "shadow_human_actions",
10165
13090
  "shadow_outcomes",
10166
13091
  "worker_queue",
13092
+ "attention_events",
13093
+ "attention_items",
13094
+ "notification_deliveries",
10167
13095
  "runner_state",
10168
13096
  "policy_recommendations",
10169
13097
  "cloud_outbox",
@@ -10478,6 +13406,214 @@ function isRecord3(value) {
10478
13406
  // packages/schema-inspector/src/index.ts
10479
13407
  import mysql from "mysql2/promise";
10480
13408
  import { Pool as Pool2 } from "pg";
13409
+
13410
+ // packages/schema-inspector/src/sensitivity.ts
13411
+ var HIGH_CONFIDENCE_RULES = [
13412
+ rule(
13413
+ "credential_or_secret",
13414
+ "The field name or description indicates credentials, authentication material, or a secret.",
13415
+ [
13416
+ "password",
13417
+ "passwordhash",
13418
+ "passwd",
13419
+ "passphrase",
13420
+ "secret",
13421
+ "token",
13422
+ "apikey",
13423
+ "accesskey",
13424
+ "privatekey",
13425
+ "credential",
13426
+ "oauth",
13427
+ "cookie",
13428
+ "sessiontoken"
13429
+ ]
13430
+ ),
13431
+ rule(
13432
+ "payment_or_bank_detail",
13433
+ "The field name or description indicates a payment instrument or bank detail.",
13434
+ [
13435
+ "paymentmethod",
13436
+ "paymentinstrument",
13437
+ "paymentdetails",
13438
+ "cardnumber",
13439
+ "creditcard",
13440
+ "debitcard",
13441
+ "cardtoken",
13442
+ "cvv",
13443
+ "cvc",
13444
+ "bankaccount",
13445
+ "accountnumber",
13446
+ "routingnumber",
13447
+ "iban",
13448
+ "swiftcode"
13449
+ ]
13450
+ ),
13451
+ rule(
13452
+ "government_identifier",
13453
+ "The field name or description indicates a government-issued or tax identifier.",
13454
+ [
13455
+ "ssn",
13456
+ "socialsecurity",
13457
+ "socialsecuritynumber",
13458
+ "taxid",
13459
+ "nationalid",
13460
+ "passport",
13461
+ "passportnumber",
13462
+ "driverslicense",
13463
+ "driverlicense"
13464
+ ]
13465
+ ),
13466
+ rule(
13467
+ "birth_information",
13468
+ "The field name or description indicates a date of birth or birth information.",
13469
+ ["dateofbirth", "birthdate", "dob"]
13470
+ ),
13471
+ rule(
13472
+ "medical_or_health_information",
13473
+ "The field name or description indicates medical or health information.",
13474
+ [
13475
+ "medical",
13476
+ "health",
13477
+ "diagnosis",
13478
+ "treatment",
13479
+ "medication",
13480
+ "prescription",
13481
+ "allergy",
13482
+ "clinical",
13483
+ "patientnote",
13484
+ "waivernote"
13485
+ ]
13486
+ ),
13487
+ rule(
13488
+ "direct_contact_or_address",
13489
+ "The field name or description indicates direct contact or address information.",
13490
+ [
13491
+ "email",
13492
+ "emailaddress",
13493
+ "phone",
13494
+ "phonenumber",
13495
+ "telephone",
13496
+ "streetaddress",
13497
+ "homeaddress",
13498
+ "mailingaddress",
13499
+ "postalcode",
13500
+ "zipcode"
13501
+ ]
13502
+ ),
13503
+ rule(
13504
+ "biometric_or_precise_location",
13505
+ "The field name or description indicates biometric or precise-location information.",
13506
+ [
13507
+ "biometric",
13508
+ "fingerprint",
13509
+ "faceprint",
13510
+ "voiceprint",
13511
+ "geolocation",
13512
+ "gpscoordinate",
13513
+ "latitude",
13514
+ "longitude",
13515
+ "preciselocation"
13516
+ ]
13517
+ ),
13518
+ rule(
13519
+ "private_or_risk_information",
13520
+ "The field name or description indicates private notes, internal risk data, or a risk score.",
13521
+ ["privatenote", "privatedata", "internalrisk", "riskscore"]
13522
+ )
13523
+ ];
13524
+ var UNRESOLVED_NAME_TOKENS = /* @__PURE__ */ new Set([
13525
+ "note",
13526
+ "notes",
13527
+ "comment",
13528
+ "comments",
13529
+ "description",
13530
+ "memo",
13531
+ "message",
13532
+ "payload",
13533
+ "body",
13534
+ "content",
13535
+ "details",
13536
+ "remarks",
13537
+ "metadata",
13538
+ "freeform"
13539
+ ]);
13540
+ var UNRESOLVED_DATA_TYPES = /* @__PURE__ */ new Set(["json", "jsonb", "xml", "object", "array"]);
13541
+ function classifySensitivity(input) {
13542
+ const evidence = normalizeEvidence(input);
13543
+ const matched = HIGH_CONFIDENCE_RULES.filter((candidate) => candidate.matches(evidence));
13544
+ if (input.writeOnly) {
13545
+ matched.push({
13546
+ code: "write_only_input",
13547
+ reason: "The source marks this field as write-only, so it must not become model-visible.",
13548
+ matches: () => true
13549
+ });
13550
+ }
13551
+ if (matched.length > 0) {
13552
+ return classification(
13553
+ "high_confidence_sensitive",
13554
+ matched.map((item) => item.code),
13555
+ matched.map((item) => item.reason),
13556
+ input.source
13557
+ );
13558
+ }
13559
+ const unresolved = [];
13560
+ if ([...evidence.tokens].some((token) => UNRESOLVED_NAME_TOKENS.has(token))) {
13561
+ unresolved.push({
13562
+ code: "unconstrained_free_text_name",
13563
+ reason: "The field name indicates unconstrained notes, comments, descriptions, or payload content."
13564
+ });
13565
+ }
13566
+ if (UNRESOLVED_DATA_TYPES.has(evidence.dataType)) {
13567
+ unresolved.push({
13568
+ code: "unstructured_data_type",
13569
+ reason: `The ${evidence.dataType} data type can contain unreviewed nested or free-form content.`
13570
+ });
13571
+ }
13572
+ if (unresolved.length > 0) {
13573
+ return classification(
13574
+ "unresolved_free_text",
13575
+ unresolved.map((item) => item.code),
13576
+ unresolved.map((item) => item.reason),
13577
+ input.source
13578
+ );
13579
+ }
13580
+ return classification(
13581
+ "structurally_low_risk",
13582
+ ["no_sensitive_structural_signal"],
13583
+ ["No deterministic sensitive or unconstrained free-text signal was found in the available structural metadata."],
13584
+ input.source
13585
+ );
13586
+ }
13587
+ function classification(state, reasonCodes, reasons, evidenceSource) {
13588
+ return {
13589
+ state,
13590
+ reason_codes: [...new Set(reasonCodes)].sort(),
13591
+ reasons: [...new Set(reasons)].sort(),
13592
+ evidence_source: evidenceSource
13593
+ };
13594
+ }
13595
+ function rule(code, reason, patterns) {
13596
+ return {
13597
+ code,
13598
+ reason,
13599
+ matches: (value) => patterns.some((pattern) => value.compact.includes(pattern) || value.descriptionCompact.includes(pattern))
13600
+ };
13601
+ }
13602
+ function normalizeEvidence(input) {
13603
+ const identifier = splitIdentifier(input.name);
13604
+ const description = splitIdentifier(input.description ?? "");
13605
+ return {
13606
+ compact: identifier.replaceAll("_", ""),
13607
+ tokens: new Set(identifier.split("_").filter(Boolean)),
13608
+ descriptionCompact: description.replaceAll("_", ""),
13609
+ dataType: String(input.dataType ?? "").trim().toLowerCase()
13610
+ };
13611
+ }
13612
+ function splitIdentifier(value) {
13613
+ return value.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
13614
+ }
13615
+
13616
+ // packages/schema-inspector/src/index.ts
10481
13617
  function schemaFingerprintForInspection(inspection) {
10482
13618
  return canonicalJsonDigest({
10483
13619
  engine: inspection.engine,
@@ -10536,30 +13672,6 @@ function rolePostureFingerprint(inspection) {
10536
13672
  var TENANT_COLUMNS = /* @__PURE__ */ new Set(["tenant_id", "account_id", "organization_id", "org_id", "workspace_id", "customer_id"]);
10537
13673
  var CONFLICT_COLUMNS = /* @__PURE__ */ new Set(["updated_at", "modified_at", "row_version", "version", "lock_version", "etag"]);
10538
13674
  var IMMUTABLE_COLUMNS = /* @__PURE__ */ new Set(["id", "uuid", "created_at", "created_by"]);
10539
- var SENSITIVE_PATTERNS = [
10540
- /password/i,
10541
- /password_hash/i,
10542
- /secret/i,
10543
- /token/i,
10544
- /api[_-]?key/i,
10545
- /access[_-]?key/i,
10546
- /private[_-]?key/i,
10547
- /session/i,
10548
- /cookie/i,
10549
- /\bssn\b/i,
10550
- /social[_-]?security/i,
10551
- /credit[_-]?card/i,
10552
- /card[_-]?number/i,
10553
- /\bcvv\b/i,
10554
- /refresh[_-]?token/i,
10555
- /oauth/i,
10556
- /(?:^|[_-])email(?:$|[_-])/i,
10557
- /(?:^|[_-])phone(?:$|[_-])/i,
10558
- /(?:^|[_-])(?:street_)?address(?:$|[_-])/i,
10559
- /(?:^|[_-])(?:date_of_birth|birth_date|dob)(?:$|[_-])/i,
10560
- /(?:^|[_-])risk[_-]?score(?:$|[_-])/i,
10561
- /(?:^|[_-])private[_-]?(?:note|notes|data)(?:$|[_-])/i
10562
- ];
10563
13675
  var LARGE_OR_BINARY_TYPES = /* @__PURE__ */ new Set([
10564
13676
  "bytea",
10565
13677
  "blob",
@@ -10877,20 +13989,26 @@ async function inspectMysql(options) {
10877
13989
  [schemaParam, schemaParam]
10878
13990
  );
10879
13991
  const [grantRows] = await connection.query("SHOW GRANTS FOR CURRENT_USER");
10880
- const mysqlRole = mysqlRolePosture(grantRows.map((row) => String(Object.values(row)[0] ?? "")));
13992
+ const mysqlGrants = mysqlGrantPosture(
13993
+ grantRows.map((row) => String(Object.values(row)[0] ?? "")),
13994
+ tableRows.map((row) => ({
13995
+ schema: String(row.schema),
13996
+ table: String(row.name)
13997
+ }))
13998
+ );
10881
13999
  const relationRolePosture = tableRows.map((row) => ({
10882
14000
  schema: String(row.schema),
10883
14001
  table_name: String(row.name),
10884
14002
  owner: "",
10885
14003
  current_role_is_owner: false,
10886
14004
  current_role_can_assume_owner: false,
10887
- can_select: mysqlRole.canSelect,
10888
- can_insert: mysqlRole.canWrite,
10889
- can_update: mysqlRole.canWrite,
10890
- can_delete: mysqlRole.canWrite,
10891
- can_truncate: mysqlRole.canWrite,
10892
- can_references: mysqlRole.canWrite,
10893
- can_trigger: mysqlRole.canWrite,
14005
+ can_select: mysqlGrants.relations[`${String(row.schema)}.${String(row.name)}`]?.select ?? false,
14006
+ can_insert: mysqlGrants.relations[`${String(row.schema)}.${String(row.name)}`]?.insert ?? false,
14007
+ can_update: mysqlGrants.relations[`${String(row.schema)}.${String(row.name)}`]?.update ?? false,
14008
+ can_delete: mysqlGrants.relations[`${String(row.schema)}.${String(row.name)}`]?.delete ?? false,
14009
+ can_truncate: mysqlGrants.relations[`${String(row.schema)}.${String(row.name)}`]?.truncate ?? false,
14010
+ can_references: mysqlGrants.relations[`${String(row.schema)}.${String(row.name)}`]?.references ?? false,
14011
+ can_trigger: mysqlGrants.relations[`${String(row.schema)}.${String(row.name)}`]?.trigger ?? false,
10894
14012
  row_security_forced: false
10895
14013
  }));
10896
14014
  await connection.query("COMMIT").catch(() => void 0);
@@ -10899,8 +14017,8 @@ async function inspectMysql(options) {
10899
14017
  server_version: String(versionRows[0]?.version ?? "unknown"),
10900
14018
  current_user: String(versionRows[0]?.current_user ?? "unknown"),
10901
14019
  role: {
10902
- verified: true,
10903
- superuser: "unsupported",
14020
+ verified: mysqlGrants.verified,
14021
+ superuser: mysqlGrants.elevated ? true : "unsupported",
10904
14022
  bypass_rls: "unsupported"
10905
14023
  },
10906
14024
  schemas: schemaRows.map((row) => String(row.schema_name)),
@@ -11028,6 +14146,12 @@ function normalizeColumn(column, primaryKey) {
11028
14146
  const name = String(column.name);
11029
14147
  const lower = name.toLowerCase();
11030
14148
  const type = String(column.udt_name || column.data_type || "unknown").toLowerCase();
14149
+ const sensitivity = classifySensitivity({
14150
+ name,
14151
+ dataType: String(column.data_type || column.udt_name || "unknown"),
14152
+ description: column.comment ?? void 0,
14153
+ source: "database"
14154
+ });
11031
14155
  return {
11032
14156
  name,
11033
14157
  data_type: String(column.data_type || column.udt_name || "unknown"),
@@ -11041,7 +14165,8 @@ function normalizeColumn(column, primaryKey) {
11041
14165
  suggestions: {
11042
14166
  tenant: TENANT_COLUMNS.has(lower),
11043
14167
  conflict: CONFLICT_COLUMNS.has(lower),
11044
- sensitive: SENSITIVE_PATTERNS.some((pattern) => pattern.test(lower)),
14168
+ sensitive: sensitivity.state !== "structurally_low_risk",
14169
+ sensitivity,
11045
14170
  immutable: primaryKey.includes(name) || TENANT_COLUMNS.has(lower) || IMMUTABLE_COLUMNS.has(lower),
11046
14171
  large_or_binary: LARGE_OR_BINARY_TYPES.has(type) || /blob|binary|bytea|vector/i.test(type)
11047
14172
  }
@@ -11073,13 +14198,139 @@ function relationIsWriteCapable(table) {
11073
14198
  if (!privileges) return true;
11074
14199
  return privileges.insert || privileges.update || privileges.delete || privileges.truncate || privileges.trigger;
11075
14200
  }
11076
- function mysqlRolePosture(grants) {
11077
- const normalized = grants.map((grant) => grant.toUpperCase());
11078
- const canSelect = normalized.some((grant) => /\bGRANT\b[\s\S]*\b(?:SELECT|ALL PRIVILEGES)\b/.test(grant));
11079
- const canWrite = normalized.some(
11080
- (grant) => /\bGRANT\b[\s\S]*\b(?:ALL PRIVILEGES|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|TRIGGER|EXECUTE|CREATE VIEW)\b/.test(grant)
11081
- );
11082
- return { canSelect, canWrite };
14201
+ function mysqlGrantPosture(grants, relations) {
14202
+ const parsed = [];
14203
+ let verified = true;
14204
+ let elevated = false;
14205
+ for (const raw of grants) {
14206
+ const grant = raw.trim();
14207
+ const match = grant.match(/^GRANT\s+(.+?)\s+ON\s+(.+?)\s+TO\s+/i);
14208
+ if (!match) {
14209
+ if (/^GRANT\s+/i.test(grant)) verified = false;
14210
+ continue;
14211
+ }
14212
+ const target = parseMysqlGrantTarget(match[2]);
14213
+ const privileges = parseMysqlGrantPrivileges(match[1]);
14214
+ if (!target || !privileges.verified) {
14215
+ verified = false;
14216
+ continue;
14217
+ }
14218
+ elevated ||= privileges.elevated || /\bWITH\s+GRANT\s+OPTION\b/i.test(grant);
14219
+ parsed.push({
14220
+ ...target,
14221
+ privileges: privileges.relation
14222
+ });
14223
+ }
14224
+ const relationMap = {};
14225
+ for (const relation of relations) {
14226
+ const posture = {
14227
+ select: false,
14228
+ insert: false,
14229
+ update: false,
14230
+ delete: false,
14231
+ truncate: false,
14232
+ references: false,
14233
+ trigger: false
14234
+ };
14235
+ for (const grant of parsed) {
14236
+ if ((grant.schema === "*" || grant.schema === relation.schema) && (grant.table === "*" || grant.table === relation.table)) {
14237
+ for (const privilege of Object.keys(posture)) {
14238
+ posture[privilege] ||= grant.privileges[privilege];
14239
+ }
14240
+ }
14241
+ }
14242
+ relationMap[`${relation.schema}.${relation.table}`] = posture;
14243
+ }
14244
+ return { verified, elevated, relations: relationMap };
14245
+ }
14246
+ function parseMysqlGrantTarget(value) {
14247
+ const identifier = String.raw`(?:\*|` + "`(?:``|[^`])+`" + String.raw`|[A-Za-z0-9_$-]+)`;
14248
+ const match = value.trim().match(new RegExp(`^(${identifier})\\s*\\.\\s*(${identifier})$`));
14249
+ if (!match) return void 0;
14250
+ return {
14251
+ schema: unquoteMysqlGrantIdentifier(match[1]),
14252
+ table: unquoteMysqlGrantIdentifier(match[2])
14253
+ };
14254
+ }
14255
+ function unquoteMysqlGrantIdentifier(value) {
14256
+ if (value === "*") return value;
14257
+ return value.startsWith("`") && value.endsWith("`") ? value.slice(1, -1).replaceAll("``", "`") : value;
14258
+ }
14259
+ function parseMysqlGrantPrivileges(value) {
14260
+ const relation = {
14261
+ select: false,
14262
+ insert: false,
14263
+ update: false,
14264
+ delete: false,
14265
+ truncate: false,
14266
+ references: false,
14267
+ trigger: false
14268
+ };
14269
+ const normalized = value.replace(/\([^)]*\)/g, "").split(",").map((entry) => entry.trim().replace(/\s+/g, " ").toUpperCase()).filter(Boolean);
14270
+ const allowed = /* @__PURE__ */ new Set([
14271
+ "USAGE",
14272
+ "SELECT",
14273
+ "INSERT",
14274
+ "UPDATE",
14275
+ "DELETE",
14276
+ "REFERENCES",
14277
+ "TRIGGER",
14278
+ "ALL",
14279
+ "ALL PRIVILEGES"
14280
+ ]);
14281
+ const elevatedPrivileges = /* @__PURE__ */ new Set([
14282
+ "ALTER",
14283
+ "ALTER ROUTINE",
14284
+ "CREATE",
14285
+ "CREATE ROLE",
14286
+ "CREATE ROUTINE",
14287
+ "CREATE TABLESPACE",
14288
+ "CREATE TEMPORARY TABLES",
14289
+ "CREATE USER",
14290
+ "CREATE VIEW",
14291
+ "DROP",
14292
+ "EVENT",
14293
+ "EXECUTE",
14294
+ "FILE",
14295
+ "GRANT OPTION",
14296
+ "INDEX",
14297
+ "LOCK TABLES",
14298
+ "PROCESS",
14299
+ "RELOAD",
14300
+ "REPLICATION CLIENT",
14301
+ "REPLICATION SLAVE",
14302
+ "SHOW DATABASES",
14303
+ "SHOW VIEW",
14304
+ "SHUTDOWN",
14305
+ "SUPER",
14306
+ "SYSTEM_USER"
14307
+ ]);
14308
+ let verified = true;
14309
+ let elevated = false;
14310
+ for (const privilege of normalized) {
14311
+ const all = privilege === "ALL" || privilege === "ALL PRIVILEGES";
14312
+ if (all) {
14313
+ relation.select = true;
14314
+ relation.insert = true;
14315
+ relation.update = true;
14316
+ relation.delete = true;
14317
+ relation.truncate = true;
14318
+ relation.references = true;
14319
+ relation.trigger = true;
14320
+ elevated = true;
14321
+ continue;
14322
+ }
14323
+ if (privilege === "SELECT") relation.select = true;
14324
+ else if (privilege === "INSERT") relation.insert = true;
14325
+ else if (privilege === "UPDATE") relation.update = true;
14326
+ else if (privilege === "DELETE") relation.delete = true;
14327
+ else if (privilege === "REFERENCES") relation.references = true;
14328
+ else if (privilege === "TRIGGER") relation.trigger = true;
14329
+ else if (privilege === "DROP") relation.truncate = true;
14330
+ if (elevatedPrivileges.has(privilege)) elevated = true;
14331
+ else if (!allowed.has(privilege)) verified = false;
14332
+ }
14333
+ return { verified, elevated, relation };
11083
14334
  }
11084
14335
  function constraintColumns(rows, kind) {
11085
14336
  const grouped = groupBy(rows.filter((row) => row.constraint_type === kind), (row) => row.constraint_name);
@@ -13204,6 +16455,51 @@ function asRecord(value) {
13204
16455
  }
13205
16456
 
13206
16457
  // packages/mcp-server/src/index.ts
16458
+ function resolveSupervisedWorkerEligibility(config, capability, options = {}) {
16459
+ const reasons = [];
16460
+ const deployment = config.supervised_worker;
16461
+ const digest = capability.contract_provenance?.digest;
16462
+ if (!deployment?.enabled) reasons.push("deployment_disabled");
16463
+ if (config.mode !== "review") reasons.push("review_mode_required");
16464
+ if (config.governance?.mode === "cloud_linked") reasons.push("cloud_linked_local_execution_forbidden");
16465
+ if (capability.kind !== "proposal") reasons.push("proposal_capability_required");
16466
+ if (capability.execution?.supervised_worker !== "allowed") reasons.push("contract_permission_missing");
16467
+ if (!digest) reasons.push("active_contract_digest_missing");
16468
+ const policy = digest ? deployment?.capabilities.find((entry) => entry.capability === capability.name && entry.contract_digest === digest && entry.mode === "supervised_worker") : void 0;
16469
+ if (!policy) reasons.push("deployment_allowlist_mismatch");
16470
+ if (options.phase !== "queue" && policy?.worker_identity && policy.worker_identity !== options.workerIdentity) {
16471
+ reasons.push("worker_identity_mismatch");
16472
+ }
16473
+ const source = config.sources?.[capability.source];
16474
+ if (!source || source.read_only === true || !source.write_url_env) {
16475
+ reasons.push("writable_source_unavailable");
16476
+ } else {
16477
+ if (policy && policy.write_url_env !== source.write_url_env) reasons.push("writer_reference_mismatch");
16478
+ if (!source.receipts) reasons.push("receipt_authority_missing");
16479
+ }
16480
+ if (!capability.target.tenant_key) reasons.push("trusted_tenant_scope_missing");
16481
+ if (capability.writeback?.mode !== "direct_sql") reasons.push("direct_sql_required");
16482
+ if ((capability.operation?.cardinality ?? "single") !== "single") reasons.push("single_row_required");
16483
+ const operation = capability.operation?.kind ?? "update";
16484
+ if (operation === "delete") reasons.push("delete_ineligible");
16485
+ if (capability.reversibility) reasons.push("reversibility_ineligible");
16486
+ if (operation === "update" && (!capability.conflict_guard?.column || capability.conflict_guard.weak_guard_ack === true)) {
16487
+ reasons.push("exact_conflict_guard_required");
16488
+ }
16489
+ if (operation === "insert" && !capability.operation?.deduplication?.components.length) {
16490
+ reasons.push("insert_deduplication_required");
16491
+ }
16492
+ const uniqueReasons = [...new Set(reasons)];
16493
+ return {
16494
+ eligible: uniqueReasons.length === 0,
16495
+ code: uniqueReasons.length === 0 ? "SUPERVISED_WORKER_ELIGIBLE" : uniqueReasons[0].toUpperCase(),
16496
+ reasons: uniqueReasons,
16497
+ capability: capability.name,
16498
+ ...digest ? { contract_digest: digest } : {},
16499
+ ...deployment ? { profile: deployment.profile } : {},
16500
+ ...policy ? { policy } : {}
16501
+ };
16502
+ }
13207
16503
  function describeIsolationAssurance(config) {
13208
16504
  return Object.entries(config.sources ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([sourceName, source]) => {
13209
16505
  const providers = trustedContextProvidersForSource(config, sourceName);
@@ -13744,8 +17040,8 @@ var CloudLinkedSynchronizer = class {
13744
17040
  acknowledged += 1;
13745
17041
  } catch (error) {
13746
17042
  failed += 1;
13747
- const classification = classifyCloudSyncFailure(error);
13748
- await this.store.failCloudOutbox({ event_id: item.event_id, owner: this.owner, ...classification });
17043
+ const classification2 = classifyCloudSyncFailure(error);
17044
+ await this.store.failCloudOutbox({ event_id: item.event_id, owner: this.owner, ...classification2 });
13749
17045
  }
13750
17046
  }
13751
17047
  await this.reconcileOnce().catch((error) => {
@@ -13979,6 +17275,7 @@ function runtimeCapabilityFromSpec(capability, resources, config, provenance) {
13979
17275
  runtime.operation = capability.proposal.operation;
13980
17276
  runtime.conflict_guard = capability.proposal.conflict_guard;
13981
17277
  runtime.approval = capability.proposal.approval;
17278
+ runtime.execution = capability.proposal.execution;
13982
17279
  runtime.writeback = {
13983
17280
  mode: capability.proposal.writeback?.mode ?? "direct_sql",
13984
17281
  ...capability.proposal.writeback?.executor ? { executor: capability.proposal.writeback.executor } : {}
@@ -14024,7 +17321,7 @@ function createMcpRuntime(config, options = {}) {
14024
17321
  return {
14025
17322
  config,
14026
17323
  store,
14027
- listTools: () => config.mode === "cloud" ? cloudTools : listedLocalCapabilities(config).map((capability) => toolMetadata(capability)),
17324
+ listTools: () => config.mode === "cloud" ? cloudTools : listedLocalCapabilities(config).map((capability) => toolMetadata(capability, config)),
14028
17325
  callTool: async (name, args) => {
14029
17326
  const capability = config.mode === "cloud" ? void 0 : localCapabilities(config).find((item) => item.name === name);
14030
17327
  try {
@@ -14133,6 +17430,8 @@ async function preflightPostgresDatabaseScope(inputConfig, env = process.env, cr
14133
17430
  }
14134
17431
  }
14135
17432
  }
17433
+ var SUPPORTED_GENERATED_AUTHORITY_COMPILER_VERSIONS = /* @__PURE__ */ new Set(["1.6.0", "1.6.3"]);
17434
+ var SUPPORTED_GENERATED_AUTHORITY_SPEC_VERSIONS = /* @__PURE__ */ new Set(["1.5.0", "1.5.1", "1.6.0"]);
14136
17435
  async function preflightGeneratedAuthority(inputConfig, env = process.env, inspect = inspectDatabase) {
14137
17436
  const config = resolveRuntimeConfig(inputConfig);
14138
17437
  const protectedCapabilities = localCapabilities(config).filter((capability) => capability.protected_read);
@@ -14198,7 +17497,7 @@ async function preflightGeneratedAuthority(inputConfig, env = process.env, inspe
14198
17497
  }
14199
17498
  function assertGeneratedAuthorityLockShape(value) {
14200
17499
  const digest = /^sha256:[a-f0-9]{64}$/;
14201
- if (!value || value.schema_version !== "synapsor.generation-lock.v1" || value.compiler_version !== "1.6.0" || value.spec_version !== "1.5.0" || value.engine !== "postgres" && value.engine !== "mysql" || !/^[A-Z_][A-Z0-9_]*$/.test(value.source_env) || !digest.test(value.schema_fingerprint) || !digest.test(value.role_posture_fingerprint) || !digest.test(value.evidence_fingerprint) || !digest.test(value.generated_contract_digest) || !digest.test(value.reviewed_overrides_digest) || !Array.isArray(value.protected_authority) || value.protected_authority.some((item) => typeof item !== "string")) {
17500
+ if (!value || value.schema_version !== "synapsor.generation-lock.v1" || !SUPPORTED_GENERATED_AUTHORITY_COMPILER_VERSIONS.has(value.compiler_version) || !SUPPORTED_GENERATED_AUTHORITY_SPEC_VERSIONS.has(value.spec_version) || value.engine !== "postgres" && value.engine !== "mysql" || !/^[A-Z_][A-Z0-9_]*$/.test(value.source_env) || !digest.test(value.schema_fingerprint) || !digest.test(value.role_posture_fingerprint) || !digest.test(value.evidence_fingerprint) || !digest.test(value.generated_contract_digest) || !digest.test(value.reviewed_overrides_digest) || !Array.isArray(value.protected_authority) || value.protected_authority.some((item) => typeof item !== "string")) {
14202
17501
  throw new McpRuntimeError(
14203
17502
  "GENERATION_LOCK_INVALID",
14204
17503
  "The generated-authority lock is malformed or belongs to an unsupported compiler/spec version."
@@ -14291,7 +17590,7 @@ function createSynapsorMcpServer(runtime, options = {}) {
14291
17590
  for (const exposedName of exposedNames.get(capability.name) ?? [capability.name]) {
14292
17591
  const toolConfig = {
14293
17592
  title: capability.name,
14294
- description: capabilityDescription(capability, exposedName),
17593
+ description: capabilityDescription(capability, runtime.config, exposedName),
14295
17594
  inputSchema: zodInputShape(capability),
14296
17595
  annotations: {
14297
17596
  readOnlyHint: capability.kind === "read" || capability.kind === "aggregate_read",
@@ -15955,6 +19254,31 @@ async function callConfiguredTool(input) {
15955
19254
  }
15956
19255
  });
15957
19256
  }
19257
+ let supervisedWorker;
19258
+ if (input.config.governance?.mode !== "cloud_linked" && approvalResult.proposal.state === "approved") {
19259
+ const eligibility = resolveSupervisedWorkerEligibility(input.config, capability, { phase: "queue" });
19260
+ if (eligibility.eligible && eligibility.policy && eligibility.contract_digest) {
19261
+ if (!input.store.enqueueWorkerProposal) {
19262
+ throw new McpRuntimeError(
19263
+ "SUPERVISED_WORKER_LEDGER_REQUIRED",
19264
+ "Supervised execution requires a durable worker queue in the authoritative proposal store."
19265
+ );
19266
+ }
19267
+ await input.store.enqueueWorkerProposal({
19268
+ proposal_id: approvalResult.proposal.proposal_id,
19269
+ execution_mode: "supervised_worker",
19270
+ contract_digest: eligibility.contract_digest,
19271
+ max_attempts: eligibility.policy.max_attempts,
19272
+ queue_limit: eligibility.policy.queue_limit
19273
+ });
19274
+ supervisedWorker = {
19275
+ status: "queued",
19276
+ mode: "supervised_worker",
19277
+ capability: capability.name,
19278
+ contract_digest: eligibility.contract_digest
19279
+ };
19280
+ }
19281
+ }
15958
19282
  await enqueueCloudLinkedProposal({
15959
19283
  config: input.config,
15960
19284
  store: input.store,
@@ -15964,7 +19288,7 @@ async function callConfiguredTool(input) {
15964
19288
  env: input.env
15965
19289
  });
15966
19290
  return {
15967
- status: input.config.governance?.mode === "cloud_linked" ? "pending_cloud_sync" : input.config.mode === "shadow" ? "shadow_proposal_created" : approvalResult.freshness?.status === "stale" ? "freshness_conflict" : approvalResult.proposal.state === "approved" ? "approved" : "review_required",
19291
+ status: input.config.governance?.mode === "cloud_linked" ? "pending_cloud_sync" : input.config.mode === "shadow" ? "shadow_proposal_created" : approvalResult.freshness?.status === "stale" ? "freshness_conflict" : supervisedWorker ? "queued_for_trusted_execution" : approvalResult.proposal.state === "approved" ? "approved" : "review_required",
15968
19292
  action: capability.name,
15969
19293
  proposal_id: approvalResult.proposal.proposal_id,
15970
19294
  proposal_version: approvalResult.proposal.proposal_version,
@@ -15992,6 +19316,13 @@ async function callConfiguredTool(input) {
15992
19316
  ...approvalResult.freshness ? { freshness: approvalResult.freshness } : {},
15993
19317
  governance: input.config.governance?.mode === "cloud_linked" ? { authority: "synapsor_cloud", state: "pending_cloud_sync", evidence_residency: "metadata_only" } : { authority: "local" },
15994
19318
  writeback: changeSet.writeback,
19319
+ ...supervisedWorker ? {
19320
+ execution: {
19321
+ ...supervisedWorker,
19322
+ approval_source: "policy_auto",
19323
+ model_can_approve_or_apply: false
19324
+ }
19325
+ } : {},
15995
19326
  source_database_changed: false,
15996
19327
  source_database_mutated: false
15997
19328
  };
@@ -16525,9 +19856,9 @@ function approvalPolicyByName(config, policyName) {
16525
19856
  }
16526
19857
  function evaluateApprovalPolicy(capability, policy, patch) {
16527
19858
  const ruleByField = /* @__PURE__ */ new Map();
16528
- for (const rule of policy.rules ?? []) {
16529
- const field = typeof rule.field === "string" ? rule.field : void 0;
16530
- const max = typeof rule.max === "number" && Number.isInteger(rule.max) ? rule.max : void 0;
19859
+ for (const rule2 of policy.rules ?? []) {
19860
+ const field = typeof rule2.field === "string" ? rule2.field : void 0;
19861
+ const max = typeof rule2.max === "number" && Number.isInteger(rule2.max) ? rule2.max : void 0;
16531
19862
  if (field && max !== void 0) ruleByField.set(field, max);
16532
19863
  }
16533
19864
  const numericFields = Object.keys(patch).filter((field) => isNumericRuntimeProposalField(capability, field));
@@ -17517,10 +20848,10 @@ var RuntimeRateLimiter = class {
17517
20848
  sharedSchema;
17518
20849
  migration;
17519
20850
  async consume(context, capability) {
17520
- const rule = this.config.rate_limits?.capabilities?.[capability] ?? this.config.rate_limits?.default;
17521
- if (!rule) return;
20851
+ const rule2 = this.config.rate_limits?.capabilities?.[capability] ?? this.config.rate_limits?.default;
20852
+ if (!rule2) return;
17522
20853
  const now = this.clock();
17523
- const windowMs = rule.window_seconds * 1e3;
20854
+ const windowMs = rule2.window_seconds * 1e3;
17524
20855
  const windowStart = Math.floor(now / windowMs) * windowMs;
17525
20856
  let count;
17526
20857
  if (this.sharedPool && this.sharedSchema) {
@@ -17536,7 +20867,7 @@ var RuntimeRateLimiter = class {
17536
20867
  [bucketKey, windowStart]
17537
20868
  );
17538
20869
  count = Number(result.rows[0]?.request_count ?? 0);
17539
- if (count > rule.requests) {
20870
+ if (count > rule2.requests) {
17540
20871
  await this.sharedPool.query(
17541
20872
  `UPDATE ${table} SET rejected_count = rejected_count + 1, updated_at = now() WHERE bucket_key = $1 AND window_start = $2`,
17542
20873
  [bucketKey, windowStart]
@@ -17550,7 +20881,7 @@ var RuntimeRateLimiter = class {
17550
20881
  this.local.set(key, bucket);
17551
20882
  count = bucket.count;
17552
20883
  }
17553
- if (count <= rule.requests) return;
20884
+ if (count <= rule2.requests) return;
17554
20885
  const retryAfterMs = Math.max(1, windowStart + windowMs - now);
17555
20886
  const metricKey = `${context.tenant_id}\0${capability}`;
17556
20887
  const metric = this.rejected.get(metricKey) ?? { tenant: context.tenant_id, capability, rejected: 0 };
@@ -18100,11 +21431,11 @@ function zodScalarArg(spec) {
18100
21431
  if (spec.required === false) schema = schema.optional();
18101
21432
  return schema;
18102
21433
  }
18103
- function toolMetadata(capability) {
21434
+ function toolMetadata(capability, config) {
18104
21435
  return {
18105
21436
  name: capability.name,
18106
21437
  title: capability.name,
18107
- description: capabilityDescription(capability),
21438
+ description: capabilityDescription(capability, config),
18108
21439
  kind: capability.kind,
18109
21440
  input_schema: Object.fromEntries(Object.entries(capability.args).map(([name, spec]) => [name, {
18110
21441
  type: spec.type === "object_array" ? "array" : spec.type,
@@ -18127,7 +21458,7 @@ function toolMetadata(capability) {
18127
21458
  }
18128
21459
  };
18129
21460
  }
18130
- function capabilityDescription(capability, exposedName) {
21461
+ function capabilityDescription(capability, config, exposedName) {
18131
21462
  const lines = [];
18132
21463
  if (exposedName && exposedName !== capability.name) {
18133
21464
  lines.push(`Canonical Synapsor capability: ${capability.name}.`);
@@ -18137,7 +21468,20 @@ function capabilityDescription(capability, exposedName) {
18137
21468
  } else if (capability.kind === "read") {
18138
21469
  lines.push(`Read ${capability.target.schema}.${capability.target.table} through a reviewed Synapsor capability with trusted tenant context and evidence.`);
18139
21470
  } else {
18140
- lines.push(`Create an evidence-backed Synapsor proposal for ${capability.target.schema}.${capability.target.table}; the source database is not mutated by this tool.`);
21471
+ const supervised = config ? resolveSupervisedWorkerEligibility(config, capability, { phase: "queue" }) : void 0;
21472
+ if (supervised?.eligible) {
21473
+ if (capability.approval?.mode === "policy") {
21474
+ lines.push(
21475
+ `Create an evidence-backed Synapsor proposal for ${capability.target.schema}.${capability.target.table}. If it satisfies the reviewed automatic-approval policy, a separately trusted Runner worker may later apply it without a per-request human click. The model cannot approve, apply, start the worker, or change that policy.`
21476
+ );
21477
+ } else {
21478
+ lines.push(
21479
+ `Create an evidence-backed Synapsor proposal for ${capability.target.schema}.${capability.target.table}. After the required human approval, a separately trusted Runner worker may later apply it. The model cannot approve, apply, start the worker, or change that execution policy.`
21480
+ );
21481
+ }
21482
+ } else {
21483
+ lines.push(`Create an evidence-backed Synapsor proposal for ${capability.target.schema}.${capability.target.table}; the source database is not mutated by this tool.`);
21484
+ }
18141
21485
  }
18142
21486
  if (capability.returns_hint) {
18143
21487
  lines.push(capability.returns_hint);