@sema-agent/server 7.142.0 → 7.143.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/MIGRATION.md +442 -0
  2. package/USAGE.md +41 -24
  3. package/dist/adoption/plan.js +22 -0
  4. package/dist/approval-ask-audit-store.d.ts +7 -5
  5. package/dist/approval-ask-audit-store.js +12 -6
  6. package/dist/approval-ask-machine.d.ts +15 -7
  7. package/dist/approval-ask-machine.js +10 -4
  8. package/dist/approval-card.d.ts +124 -25
  9. package/dist/approval-card.js +103 -23
  10. package/dist/approval-queue-kind.d.ts +27 -0
  11. package/dist/approval-queue-kind.js +6 -0
  12. package/dist/approval-reconciler.d.ts +8 -8
  13. package/dist/approval-reconciler.js +3 -3
  14. package/dist/audit.js +4 -2
  15. package/dist/boot/budget-tracing.d.ts +1 -1
  16. package/dist/boot/governance-seams.d.ts +26 -41
  17. package/dist/boot/governance-seams.js +61 -28
  18. package/dist/boot/late-binds.d.ts +7 -4
  19. package/dist/boot/late-binds.js +3 -1
  20. package/dist/boot/leader.js +2 -4
  21. package/dist/boot/memory-consolidation.d.ts +0 -8
  22. package/dist/boot/memory-consolidation.js +3 -3
  23. package/dist/boot/reapers.js +5 -4
  24. package/dist/boot/resolve-spec.d.ts +1 -1
  25. package/dist/boot/resolve-spec.js +8 -8
  26. package/dist/boot/runner-deps.d.ts +10 -7
  27. package/dist/boot/runner-deps.js +5 -17
  28. package/dist/boot/shutdown.d.ts +9 -0
  29. package/dist/boot/shutdown.js +16 -2
  30. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  31. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  32. package/dist/boot/stage-03-store-backend.js +1 -2
  33. package/dist/boot/stage-04-stores.d.ts +5 -2
  34. package/dist/boot/stage-04-stores.js +3 -5
  35. package/dist/boot/stage-05-execution-env.d.ts +5 -2
  36. package/dist/boot/stage-06-runners.d.ts +5 -2
  37. package/dist/boot/stage-06-runners.js +1 -3
  38. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  39. package/dist/boot/stage-07-capability-layer.js +5 -1
  40. package/dist/boot/stage-08-reapers.d.ts +5 -2
  41. package/dist/boot/stage-09-leader.d.ts +5 -2
  42. package/dist/boot/stage-10-http-server.d.ts +5 -2
  43. package/dist/boot/stage-10-http-server.js +9 -1
  44. package/dist/boot/stores.d.ts +5 -2
  45. package/dist/boot/stores.js +7 -7
  46. package/dist/boot/web-admin.d.ts +5 -1
  47. package/dist/boot/web-admin.js +31 -19
  48. package/dist/capabilities/hands-lane.d.ts +9 -39
  49. package/dist/capabilities/hands-lane.js +8 -13
  50. package/dist/capabilities/mcp-probe.d.ts +3 -2
  51. package/dist/capabilities/scenarios.d.ts +11 -1
  52. package/dist/capabilities/scenarios.js +1 -0
  53. package/dist/capabilities/skills.d.ts +1 -17
  54. package/dist/capabilities/skills.js +0 -8
  55. package/dist/capability-facts.d.ts +8 -8
  56. package/dist/capability-facts.js +4 -4
  57. package/dist/card-void.d.ts +6 -9
  58. package/dist/config-catalog.js +1 -1
  59. package/dist/config-dir-trust.js +58 -29
  60. package/dist/config-invariants.d.ts +7 -1
  61. package/dist/config-invariants.js +7 -0
  62. package/dist/config-provider.d.ts +10 -0
  63. package/dist/config-provider.js +15 -4
  64. package/dist/config-root-migration.d.ts +13 -0
  65. package/dist/config-root-migration.js +99 -0
  66. package/dist/config-types.d.ts +5 -5
  67. package/dist/config.d.ts +4 -3
  68. package/dist/config.js +1 -1
  69. package/dist/delegated-session-owner.d.ts +5 -3
  70. package/dist/delegated-session-owner.js +1 -1
  71. package/dist/device-ws-protocol.d.ts +1 -0
  72. package/dist/device-ws-protocol.js +2 -1
  73. package/dist/env-model-table-gate.d.ts +53 -0
  74. package/dist/env-model-table-gate.js +53 -0
  75. package/dist/fleet/fleet-bus.d.ts +28 -52
  76. package/dist/fleet/fleet-bus.js +35 -100
  77. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  78. package/dist/fleet/fleet-reconciler.js +1 -1
  79. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  80. package/dist/fleet/subagent-tail-bus.js +15 -28
  81. package/dist/hooks/hook-runner.js +28 -7
  82. package/dist/host-decision.d.ts +54 -20
  83. package/dist/host-decision.js +42 -14
  84. package/dist/http/admission.d.ts +1 -1
  85. package/dist/http/admission.js +7 -3
  86. package/dist/http/approval-sweeps.js +11 -8
  87. package/dist/http/caller-trust.d.ts +1 -1
  88. package/dist/http/durable-run-leg.js +2 -2
  89. package/dist/http/engine-refusal.d.ts +16 -0
  90. package/dist/http/engine-refusal.js +17 -0
  91. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  92. package/dist/http/memory-compliance-failure-reply.js +4 -6
  93. package/dist/http/owner-access.d.ts +11 -0
  94. package/dist/http/owner-access.js +13 -0
  95. package/dist/http/resume-legs.d.ts +21 -7
  96. package/dist/http/resume-legs.js +71 -49
  97. package/dist/http/resume-restatement.d.ts +39 -0
  98. package/dist/http/resume-restatement.js +25 -0
  99. package/dist/http/route-ctx.d.ts +35 -10
  100. package/dist/http/route-ctx.js +1 -1
  101. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  102. package/dist/http/routes/approvals-assistant.js +142 -31
  103. package/dist/http/routes/capabilities.js +11 -2
  104. package/dist/http/routes/memory-policy.d.ts +2 -2
  105. package/dist/http/routes/memory-policy.js +1 -1
  106. package/dist/http/routes/notify-wake.js +23 -25
  107. package/dist/http/routes/rules.js +48 -13
  108. package/dist/http/routes/runs.js +9 -8
  109. package/dist/http/routes/session-memory-sync.d.ts +34 -6
  110. package/dist/http/routes/session-memory-sync.js +27 -10
  111. package/dist/http/routes/session-sync.js +13 -20
  112. package/dist/http/routes/sessions.d.ts +2 -2
  113. package/dist/http/routes/sessions.js +22 -23
  114. package/dist/http/routes/tasks.js +27 -49
  115. package/dist/http/server.d.ts +13 -7
  116. package/dist/http/server.js +2 -2
  117. package/dist/http/stored-body-replay.js +15 -4
  118. package/dist/http/wire-types.d.ts +17 -7
  119. package/dist/leader/leader.js +5 -2
  120. package/dist/leader/wire.d.ts +7 -1
  121. package/dist/leader/wire.js +6 -5
  122. package/dist/leg-facts.d.ts +37 -0
  123. package/dist/leg-facts.js +15 -0
  124. package/dist/memory-bundle-engine.d.ts +1 -4
  125. package/dist/memory-bundle-engine.js +2 -2
  126. package/dist/memory-operator-faces.d.ts +30 -66
  127. package/dist/memory-operator-faces.js +17 -88
  128. package/dist/memory-scope-stores.d.ts +38 -40
  129. package/dist/memory-scope-stores.js +51 -36
  130. package/dist/memory-scope.d.ts +0 -9
  131. package/dist/memory-scope.js +0 -9
  132. package/dist/memory-sync-client.js +14 -3
  133. package/dist/memory-sync.d.ts +20 -4
  134. package/dist/memory-sync.js +23 -4
  135. package/dist/model-plane-face.d.ts +13 -7
  136. package/dist/model-plane-face.js +2 -1
  137. package/dist/observability/fail-open.d.ts +20 -4
  138. package/dist/observability/fail-open.js +17 -4
  139. package/dist/observability/run-terminal.js +4 -0
  140. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  141. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  142. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  143. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  144. package/dist/orchestration/workflow-park-decision.js +5 -3
  145. package/dist/org-delivery-gate.d.ts +11 -0
  146. package/dist/org-delivery-gate.js +19 -1
  147. package/dist/parked-decide.d.ts +13 -9
  148. package/dist/parked-decide.js +7 -7
  149. package/dist/parked-revive-surface.js +1 -1
  150. package/dist/peer-inbound-face.d.ts +127 -0
  151. package/dist/peer-inbound-face.js +158 -0
  152. package/dist/permission-rule-list.d.ts +13 -9
  153. package/dist/permission-rule-list.js +7 -3
  154. package/dist/plugins/approval-ask-store-file.js +18 -6
  155. package/dist/plugins/approval-ask-store-memory.js +8 -6
  156. package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
  157. package/dist/plugins/approval-ask-store-sql.js +30 -11
  158. package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
  159. package/dist/plugins/checkpoint-store-sql.js +9 -7
  160. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  161. package/dist/plugins/file-history-store-sql.js +24 -19
  162. package/dist/plugins/file-run-store.d.ts +29 -0
  163. package/dist/plugins/file-run-store.js +97 -8
  164. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  165. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  166. package/dist/plugins/local-checkpoint-store.d.ts +1 -1
  167. package/dist/plugins/local-checkpoint-store.js +2 -2
  168. package/dist/plugins/local-session-store.d.ts +6 -2
  169. package/dist/plugins/local-session-store.js +8 -2
  170. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  171. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  172. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  173. package/dist/plugins/mailbox-store-sql.js +76 -7
  174. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  175. package/dist/plugins/memory-sync-store-pg.js +21 -0
  176. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  177. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  178. package/dist/plugins/one-time-migrations.d.ts +32 -2
  179. package/dist/plugins/one-time-migrations.js +106 -3
  180. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  181. package/dist/plugins/org-delivery-quota-store.js +2 -12
  182. package/dist/plugins/park-commit-watch.d.ts +1 -1
  183. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  184. package/dist/plugins/permission-rule-store-file.js +15 -2
  185. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  186. package/dist/plugins/permission-rule-store-sql.js +35 -2
  187. package/dist/plugins/remote-env-device.d.ts +8 -11
  188. package/dist/plugins/remote-env-device.js +6 -3
  189. package/dist/plugins/remote-env-k8s.d.ts +37 -24
  190. package/dist/plugins/remote-env-k8s.js +50 -17
  191. package/dist/plugins/retention-store-sql.d.ts +2 -2
  192. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  193. package/dist/plugins/sandbox-read-floor.js +2 -7
  194. package/dist/plugins/sql-errors.d.ts +10 -1
  195. package/dist/plugins/sql-errors.js +13 -0
  196. package/dist/plugins/store-backend.d.ts +23 -28
  197. package/dist/plugins/store-backend.js +9 -1
  198. package/dist/plugins/store-contracts.d.ts +29 -5
  199. package/dist/plugins/store-contracts.js +4 -1
  200. package/dist/plugins/task-attachment-store.d.ts +2 -2
  201. package/dist/plugins/task-attachment-store.js +37 -52
  202. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  203. package/dist/plugins/tool-result-store-sql.js +23 -2
  204. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  205. package/dist/plugins/wiring-section-codec.js +55 -0
  206. package/dist/plugins/workspace-fill.d.ts +1 -1
  207. package/dist/rule-entry-list.d.ts +3 -2
  208. package/dist/rule-entry-list.js +5 -1
  209. package/dist/rules-consent.d.ts +28 -5
  210. package/dist/rules-consent.js +40 -28
  211. package/dist/run-local.js +9 -5
  212. package/dist/runs.d.ts +12 -8
  213. package/dist/runs.js +6 -7
  214. package/dist/runtime-governance.d.ts +1 -1
  215. package/dist/server-secret-env.d.ts +4 -0
  216. package/dist/session-error-wire.d.ts +12 -0
  217. package/dist/session-error-wire.js +6 -0
  218. package/dist/session-incarnation.d.ts +1 -1
  219. package/dist/session-notify-door.d.ts +106 -0
  220. package/dist/session-notify-door.js +93 -0
  221. package/dist/session-sync.d.ts +9 -14
  222. package/dist/session-sync.js +5 -5
  223. package/dist/session-workspace.d.ts +3 -13
  224. package/dist/session-workspace.js +0 -9
  225. package/dist/standing-answer-grant.d.ts +5 -0
  226. package/dist/standing-answer-grant.js +10 -0
  227. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  228. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  229. package/dist/task-mcp.d.ts +1 -1
  230. package/dist/task-settings.d.ts +11 -8
  231. package/dist/task-settings.js +31 -11
  232. package/dist/tool-approval.d.ts +52 -34
  233. package/dist/tool-approval.js +66 -57
  234. package/dist/trace/core-keyset-guard.d.ts +11 -8
  235. package/dist/trace/engine-notice-wire.d.ts +1 -1
  236. package/dist/trace/engine-notice-wire.js +3 -1
  237. package/dist/trace/exit-seat.d.ts +7 -1
  238. package/dist/trace/exit-seat.js +1 -0
  239. package/dist/trace/ledger-sink.d.ts +2 -2
  240. package/dist/trace/model-segment.d.ts +55 -25
  241. package/dist/trace/model-segment.js +101 -33
  242. package/dist/trace/project.d.ts +7 -4
  243. package/dist/trace/project.js +6 -0
  244. package/dist/trace/redact.d.ts +61 -9
  245. package/dist/trace/redact.js +1490 -468
  246. package/dist/trace/self-minted-wire.d.ts +510 -38
  247. package/dist/trace/self-minted-wire.js +165 -17
  248. package/dist/trace/wire-projection-faces.d.ts +1 -1
  249. package/dist/trace/wire-projection-faces.js +1 -1
  250. package/dist/verifier-pause-card.d.ts +33 -5
  251. package/dist/verifier-pause-card.js +15 -10
  252. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  253. package/dist/web-admin-client/apply-effective.js +90 -10
  254. package/dist/workspace-space.d.ts +19 -3
  255. package/dist/workspace-space.js +8 -4
  256. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  257. package/package.json +3 -2
  258. package/wire-changes/server@7.143.0.json +1167 -0
@@ -1,8 +1,8 @@
1
1
  import { expandTiers, modelOriginOf, resolveTaskModel as coreResolveTaskModel } from "@sema-agent/core";
2
- import { CATALOG_REPLACES_ENV_MODELS_CODE, SandboxGitConfig, WEB_ADMIN_ENV, formatConfigPath, narrowAtMention, resolveActiveTiers, resolveCatalogDefaultModel, sanitizeZodError } from "@sema-agent/settings-schema";
2
+ import { CATALOG_REPLACES_ENV_MODELS_CODE, SandboxGitConfig, WEB_ADMIN_ENV, envModelsDecisionTag, formatConfigPath, narrowAtMention, resolveActiveTiers, resolveCatalogDefaultModel, sanitizeZodError } from "@sema-agent/settings-schema";
3
3
  import { modelKeyPoison } from "../key-resolver.js";
4
- import { buildEnvModelIdNotServedNotice, mintCapabilityKeys, readAtMention, servedModelTableLeg } from "../model-select.js";
5
- import { applyAutoCompactWindow, buildModelCostKey, ownCredentialHitsInForce, parseAutonomy, unmatchableToolNamesRefusal } from "../config.js";
4
+ import { DEFAULT_MODEL_REF, buildEnvModelIdNotServedNotice, mintCapabilityKeys, readAtMention, servedModelTableLeg } from "../model-select.js";
5
+ import { applyAutoCompactWindow, buildModelCostKey, isRosterGateArmed, ownCredentialHitsInForce, parseAutonomy, unmatchableToolNamesRefusal } from "../config.js";
6
6
  import { NAME_TABLE_BEHAVIOR } from "../namespaced-rule-name.js";
7
7
  import { buildModelCompat, readModelCompat } from "../model-compat.js";
8
8
  import { buildModelHeadersKey } from "../model-entry-headers.js";
@@ -17,6 +17,7 @@ import { gateIntentUnserviceableReason } from "../approval.js";
17
17
  import { refusedGroups } from "./apply-ledger.js";
18
18
  import { judgeConfigCandidate } from "./candidate-gate.js";
19
19
  import { declareServerWithheldEnv, withdrawServerWithheldEnv } from "../server-secret-env.js";
20
+ import { MODEL_ROUTE_ENV_KEYS } from "../model-route-gap.js";
20
21
  const charsPerTokenOf = (v) => (typeof v === "number" && Number.isFinite(v) && v > 0 ? v : undefined);
21
22
  function envDerivedModel(config) {
22
23
  const m = config.envModel;
@@ -65,6 +66,35 @@ export function mutateInPlace(target, source) {
65
66
  }
66
67
  const appliedGeneration = new WeakMap();
67
68
  const envModelPlane = new WeakMap();
69
+ function envModelTableOf(config) {
70
+ const table = envModelPlaneOf(config).models;
71
+ const mainId = envDerivedModel(config).id;
72
+ const out = [];
73
+ if (mainId !== "" && Object.hasOwn(table, mainId))
74
+ out.push({ id: mainId, slot: "main", env: MODEL_ROUTE_ENV_KEYS.mainId });
75
+ for (const key of Object.keys(table))
76
+ if (key !== DEFAULT_MODEL_REF && key !== mainId)
77
+ out.push({ id: key, slot: "cheap", env: MODEL_ROUTE_ENV_KEYS.cheapId });
78
+ return out;
79
+ }
80
+ const unreadableDecisionsSaid = new WeakMap();
81
+ function warnUnreadableEnvModelsDecision(config, source, logger) {
82
+ const decision = source.envModels;
83
+ if (decision.kind !== "unreadable" || logger === undefined)
84
+ return;
85
+ const said = unreadableDecisionsSaid.get(config) ?? new Set();
86
+ unreadableDecisionsSaid.set(config, said);
87
+ const tag = envModelsDecisionTag(decision);
88
+ if (said.has(tag))
89
+ return;
90
+ said.add(tag);
91
+ logger.warn("config_env_models_unreadable", {
92
+ dir: source.root,
93
+ reason: decision.reason,
94
+ ...(decision.reason === "io" ? { errno: decision.errno } : { digest: decision.digest }),
95
+ note: "the config root's env-model-table decision does not read — it is never read as a confirmation, so a local catalog that manages the model plane does not replace the declared env model table; fix or rewrite the file (a supported writer overwrites it whole), said once per content",
96
+ });
97
+ }
68
98
  function envModelPlaneOf(config) {
69
99
  let snap = envModelPlane.get(config);
70
100
  if (snap === undefined) {
@@ -164,6 +194,8 @@ export function applyEffective(config, eff, logger, opts) {
164
194
  if (notServed !== undefined)
165
195
  logger?.warn(notServed.event, notServed.fields);
166
196
  }
197
+ if (mp !== undefined)
198
+ warnDanglingEnvBindings(config, staged.modelPlaneGroup.envBindingsDangling ?? [], logger);
167
199
  if (staged.approvalGate.next !== undefined)
168
200
  logger?.info("web_admin_runtime", { version: staged.version, approvalRequire: staged.approvalGate.next });
169
201
  if (Object.keys(staged.hot.infoLine).length > 0)
@@ -254,8 +286,8 @@ export const PLANE_REFUSAL_INVARIANT_OF = {
254
286
  "config.model_refused_name_minted": "refused-names-not-minted",
255
287
  "config.model_route_endpoint_missing": "model-route-endpoint",
256
288
  "config.gateway_fallback_without_primary": "model-route-endpoint",
289
+ [CATALOG_REPLACES_ENV_MODELS_CODE]: "env-table-replacement-acknowledged",
257
290
  };
258
- export const ENGINE_MINTED_PLANE_REFUSAL_CODES = [CATALOG_REPLACES_ENV_MODELS_CODE];
259
291
  function modelPlaneGroupReport(group) {
260
292
  return {
261
293
  applied: MODEL_PLANE_DOMAINS.flatMap((d) => (group.stages[d] !== undefined ? MODEL_PLANE_SURFACES[d] : [])),
@@ -263,6 +295,46 @@ function modelPlaneGroupReport(group) {
263
295
  };
264
296
  }
265
297
  const ROLE_BINDING_UNAVAILABLE_HINT = "alias/tier expansion cannot mint this name, so the engine does not hold it: a strict seat (default, classifier) refuses the task loud; any other role reads as unset and falls back along its own chain with a model.role_defaulted notice (refused only when nothing on that chain resolves) — bind the role to a usable model, or make the entry usable (see the entry's own warn)";
298
+ function danglingEnvBindingsOf(envRoles, centerRoles, envTable, served) {
299
+ const out = [];
300
+ for (const [role, spec] of Object.entries(envRoles)) {
301
+ if (Object.hasOwn(centerRoles, role))
302
+ continue;
303
+ const name = typeof spec === "string" ? spec : spec !== null && typeof spec === "object" && "model" in spec && typeof spec.model === "string" ? spec.model : undefined;
304
+ if (name === undefined || Object.hasOwn(served, name))
305
+ continue;
306
+ const entry = envTable.find((e) => e.id === name);
307
+ if (entry !== undefined)
308
+ out.push({ role, name, env: entry.env });
309
+ }
310
+ return out;
311
+ }
312
+ const ROLE_DEFAULTED_NOTICE = "model.role_defaulted";
313
+ const danglingEnvNamesSaid = new WeakMap();
314
+ function warnDanglingEnvBindings(config, dangling, logger) {
315
+ if (logger === undefined || dangling.length === 0)
316
+ return;
317
+ const said = danglingEnvNamesSaid.get(config) ?? new Set();
318
+ danglingEnvNamesSaid.set(config, said);
319
+ const byName = new Map();
320
+ for (const d of dangling) {
321
+ const row = byName.get(d.name) ?? { env: d.env, roles: [] };
322
+ row.roles.push(d.role);
323
+ byName.set(d.name, row);
324
+ }
325
+ for (const [name, row] of byName) {
326
+ if (said.has(name))
327
+ continue;
328
+ said.add(name);
329
+ logger.warn("config_env_role_binding_dangling", {
330
+ model: name,
331
+ env: row.env,
332
+ roles: row.roles,
333
+ engineNotice: ROLE_DEFAULTED_NOTICE,
334
+ note: `the env role base binds ${row.roles.join(", ")} to ${row.env} "${name}", but the adopted catalog serves no entry by that name (tier expansion included) — the engine reads ${row.roles.length > 1 ? "these roles" : "that role"} as unset and falls back along the role chain (each task says ${ROLE_DEFAULTED_NOTICE}); bind the role in the catalog's roles, or name a catalog entry "${name}" — said once per process`,
335
+ });
336
+ }
337
+ }
266
338
  export const NO_CENTER_EFFECTIVE = deepFreeze({ version: 0, updatedAt: "", models: { models: [], roles: {} }, mcp: { servers: [] } });
267
339
  export const isNoCenterEffective = (eff) => eff === NO_CENTER_EFFECTIVE;
268
340
  function deepFreeze(v) {
@@ -292,7 +364,7 @@ function stageEnvModelPlane(config) {
292
364
  },
293
365
  };
294
366
  }
295
- function stageModelPlane(config, eff, logger, sealedKeys) {
367
+ function stageModelPlane(config, eff, source, logger, sealedKeys) {
296
368
  if (isNoCenterEffective(eff))
297
369
  return stageEnvModelPlane(config);
298
370
  const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
@@ -313,6 +385,13 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
313
385
  });
314
386
  return { stages: {}, rejected: { keys: MODEL_PLANE_GROUP_KEYS, reason, invariant, code }, refused: verdict.refused };
315
387
  };
388
+ const envTable = envModelTableOf(config);
389
+ const envTableRefusal = crossInvariant("env-table-replacement-acknowledged").check({ live: config, next: config, candidate: { envTable, envTableIsPlaceholder: isRosterGateArmed(config.webAdmin), source } });
390
+ if (envTableRefusal !== undefined) {
391
+ if (source.kind === "local")
392
+ warnUnreadableEnvModelsDecision(config, source, logger);
393
+ return refuse(CATALOG_REPLACES_ENV_MODELS_CODE, envTableRefusal, { ...(source.kind === "local" ? { decision: source.envModels.kind } : {}), envModels: envTable.map((e) => ({ id: e.id, env: e.env })) });
394
+ }
316
395
  if (verdict.malformed.length > 0)
317
396
  return refuse(REFUSED_NAME_MINTED_CODE, malformedDroppedCarrierRefusal(verdict.malformed), { entries: [...verdict.malformed] });
318
397
  const usable = verdict.usable;
@@ -429,6 +508,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
429
508
  }
430
509
  const atMention = stageAtMention(eff.models?.atMention, new Set(usable.map((m) => m.name)), logger);
431
510
  const defaultModel = models.default;
511
+ const envBindingsDangling = danglingEnvBindingsOf(envPlane.roles, centerRoles, envModelTableOf(config), expandTiers(models, activeTiers) ?? models);
432
512
  const plane = {
433
513
  models,
434
514
  model: defaultModel,
@@ -442,7 +522,7 @@ function stageModelPlane(config, eff, logger, sealedKeys) {
442
522
  atMention,
443
523
  infoLine: { count: usable.length, ...(Object.keys(verdict.refused).length > 0 ? refusedNamesLogFields("unavailable", Object.keys(verdict.refused)) : {}), default: defaultModel.id, defaultSource: picked.source, roles: Object.keys(roles), tiers: Object.keys(activeTiers), atMention: atMention.mode, sealedKeys: Object.values(modelApiKeys).filter((v) => typeof v === "string").length, sealedPoisoned: Object.values(modelApiKeys).filter((v) => typeof v !== "string").length },
444
524
  };
445
- return { stages: { models: plane }, defaultName: picked.name, refused: verdict.refused };
525
+ return { stages: { models: plane }, defaultName: picked.name, refused: verdict.refused, ...(envBindingsDangling.length > 0 ? { envBindingsDangling } : {}) };
446
526
  }
447
527
  }
448
528
  export function collabTemplatesNotExecuted(eff) {
@@ -456,7 +536,7 @@ function stageEffective(config, eff, logger, opts) {
456
536
  note: "empty/unpublished effective config — using env + built-in fallback; publish a config or unset CONFIG_PUBLISH_MODE",
457
537
  });
458
538
  }
459
- const modelPlaneGroup = stageModelPlane(config, eff, logger, opts.sealedKeys);
539
+ const modelPlaneGroup = stageModelPlane(config, eff, opts.catalogSource, logger, opts.sealedKeys);
460
540
  const notExecuted = collabTemplatesNotExecuted(eff);
461
541
  if (notExecuted.length > 0)
462
542
  logger?.warn("web_admin_collab_not_executed", { version: eff.version, templates: notExecuted, note: COLLAB_NOT_EXECUTED_NOTE });
@@ -490,7 +570,7 @@ function stageEffective(config, eff, logger, opts) {
490
570
  sandboxGit,
491
571
  };
492
572
  }
493
- const SANDBOX_GIT_REFUSED_EFFECT = "the block is read as routing no git origins (the sandbox git plane reads no_origins; no new sandbox pod carries the credential broker) — the rest of this candidate applies; fix the block at the console (it is judged by the contract's SandboxGitConfig)";
573
+ const SANDBOX_GIT_REFUSED_EFFECT = "the block is read as routing no git origins (the live plane reading stays no_caller_credential — no_origins is the plane's last premise and is never reached while the caller credential is absent; no new sandbox pod carries the credential broker) — the rest of this candidate applies; fix the block at the console (it is judged by the contract's SandboxGitConfig)";
494
574
  function stageSandboxGit(eff, logger) {
495
575
  if (eff.sandboxGit === undefined)
496
576
  return undefined;
@@ -669,9 +749,9 @@ function engineDefaultOfSelect(select, models, activeTiers) {
669
749
  const name = Object.keys(models).find((n) => models[n] === origin);
670
750
  return name !== undefined ? { name } : { unresolved: "the engine picked a model object that is not an entry of the usable catalog" };
671
751
  }
672
- export function logEffectiveDiff(config, eff, logger) {
752
+ export function logEffectiveDiff(config, eff, catalogSource, logger) {
673
753
  const enabled = (eff.models?.models ?? []).filter((m) => m.enabled !== false);
674
- const plane = stageModelPlane(config, eff, undefined, undefined);
754
+ const plane = stageModelPlane(config, eff, catalogSource, undefined, undefined);
675
755
  const refused = plane.refused ?? {};
676
756
  const centerModels = enabled.map((m) => ({ name: m.name, id: m.id, provider: m.provider, apiKeyEnv: m.apiKeyEnv ?? null, tier: m.tier ?? null }));
677
757
  const envModels = Object.entries(config.models).map(([name, m]) => ({ name, id: m.id, provider: m.provider }));
@@ -105,10 +105,26 @@ export declare const WORKSPACE_SNAPSHOT_EXCLUDE_INVALID = "config.workspace_snap
105
105
  * path pattern, never a secret, so it is echoed.
106
106
  */
107
107
  export declare function parseWorkspaceSnapshotExclude(raw: string | undefined): readonly string[];
108
- /** Mint one snapshot id: `kata-<owner>-<ts36>-<rand>` (see {@link SNAPSHOT_OWNER_NONE}). The time and random parts only keep ids unique; nothing reads them back. */
109
- export declare function mintSnapshotId(incarnation: string | undefined): string;
108
+ /**
109
+ * What one snapshot id names — a closed set, spelled by the id's first segment (its family), written only by {@link mintSnapshotId} and read
110
+ * only by {@link snapshotIdHolds}:
111
+ * - `archive` — an archive was packed and put under this id (`kata-…`): a restore of it asks the store for that object, and an object that is
112
+ * not there is a dangling restore point. Every id minted before the family said more than this is one;
113
+ * - `nothing` — nothing was packed: the env held no pod when it parked (nothing had touched its hand), so no object is ever put under this id
114
+ * (`nopack-…`): there is no workspace of the park to restore, and a resume of it is a new connect (`plugins/remote-env-k8s.ts`, `resumeVM`).
115
+ * The id never reaches the wire: it lives in the engine's checkpoint and in operator lines.
116
+ */
117
+ export type SnapshotHolds = "archive" | "nothing";
118
+ /** Mint one snapshot id: `<family>-<owner>-<ts36>-<rand>` — the family says what the id names ({@link SnapshotHolds}), the owner who wrote it
119
+ * (see {@link SNAPSHOT_OWNER_NONE}). The time and random parts only keep ids unique; nothing reads them back. */
120
+ export declare function mintSnapshotId(incarnation: string | undefined, holds: SnapshotHolds): string;
121
+ /** What a snapshot id names ({@link SnapshotHolds}) — the ONE reading of the family {@link mintSnapshotId} wrote. Only an id of exactly the minted
122
+ * `nothing` form reads as `nothing`; every other id — this adapter's archives, ids minted before the family said what they name, anything else (a
123
+ * malformed `nopack-…` included) — names an archive, so its restore goes through the one key-segment judge (an id that is not a plain key segment
124
+ * is refused before any pod or request) and then asks the store and judges what comes back. */
125
+ export declare function snapshotIdHolds(id: string): SnapshotHolds;
110
126
  /** Whether an object name in the park partition (`<snapshotId>.tar.gz`, or any single-segment name) carries the mark of THIS occupant incarnation.
111
- * An empty incarnation owns nothing; `none` and unmarked names are never owned (fail closed). */
127
+ * An empty incarnation owns nothing; `none` and unmarked names are never owned (fail closed). Only an archive's id names an object. */
112
128
  export declare function snapshotNameOwnedBy(name: string, incarnation: string): boolean;
113
129
  /** One ended space, as the reclaim seat is told of it: the session whose space ended (its park lane's partition) and the space key
114
130
  * (the occupant's incarnation — the `space/` partition, the cache volume's label and, through {@link snapshotNameOwnedBy}, the owner mark of
@@ -67,18 +67,22 @@ function snapshotExcludeItemProblem(item) {
67
67
  }
68
68
  const SNAPSHOT_OWNER_NONE = "none";
69
69
  const SNAPSHOT_OWNER_HEX_LENGTH = 16;
70
- const SNAPSHOT_ID_FAMILY = "kata";
70
+ const SNAPSHOT_ID_FAMILY = { archive: "kata", nothing: "nopack" };
71
71
  function snapshotOwnerOf(incarnation) {
72
72
  if (incarnation === undefined || incarnation === "")
73
73
  return SNAPSHOT_OWNER_NONE;
74
74
  return createHash("sha256").update(incarnation).digest("hex").slice(0, SNAPSHOT_OWNER_HEX_LENGTH);
75
75
  }
76
- export function mintSnapshotId(incarnation) {
77
- return `${SNAPSHOT_ID_FAMILY}-${snapshotOwnerOf(incarnation)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
76
+ export function mintSnapshotId(incarnation, holds) {
77
+ return `${SNAPSHOT_ID_FAMILY[holds]}-${snapshotOwnerOf(incarnation)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
78
+ }
79
+ const NOTHING_ID_FORM = new RegExp(`^${SNAPSHOT_ID_FAMILY.nothing}-(?:${SNAPSHOT_OWNER_NONE}|[0-9a-f]{${SNAPSHOT_OWNER_HEX_LENGTH}})-[0-9a-z]+-[0-9a-z]*$`);
80
+ export function snapshotIdHolds(id) {
81
+ return NOTHING_ID_FORM.test(id) ? "nothing" : "archive";
78
82
  }
79
83
  export function snapshotNameOwnedBy(name, incarnation) {
80
84
  if (incarnation === "")
81
85
  return false;
82
- return name.startsWith(`${SNAPSHOT_ID_FAMILY}-${snapshotOwnerOf(incarnation)}-`);
86
+ return name.startsWith(`${SNAPSHOT_ID_FAMILY.archive}-${snapshotOwnerOf(incarnation)}-`);
83
87
  }
84
88
  //# sourceMappingURL=workspace-space.js.map
@@ -341,7 +341,7 @@ base64 inside JSON fields.
341
341
  | Constant | Value | Meaning |
342
342
  |---|---|---|
343
343
  | `DEVICE_WS_PATH` | `/v1/device/ws` | the upgrade path (prefer the `wsPath` from `/v1/capabilities`) |
344
- | `DEVICE_PROTOCOL_VERSION` | `3` | the protocol revision this server speaks (the highest it accepts) |
344
+ | `DEVICE_PROTOCOL_VERSION` | `4` | the protocol revision this server speaks (the highest it accepts) |
345
345
  | `DEVICE_MIN_PROTOCOL_VERSION` | `1` | the lowest revision it accepts; below it (or above the highest), a loud `helloReject` — never a silent downgrade |
346
346
  | `DEVICE_HELLO_DOMAIN_TAG` | `sema.device.hello.v1` | the handshake signature domain tag (§1.3) |
347
347
 
@@ -371,6 +371,7 @@ the union of the rows up to *N*, and the full set at the current revision is the
371
371
  | `1` | `aborted`, `already_exists`, `auth_failed`, `callback_error`, `invalid`, `is_directory`, `not_directory`, `not_found`, `not_supported`, `outcome_unknown`, `permission_denied`, `precondition_failed`, `shell_unavailable`, `spawn_error`, `suspended`, `target_unavailable`, `timeout`, `transport_lost`, `unknown` |
372
372
  | `2` | `liveness_no_output` |
373
373
  | `3` | `name_too_long` |
374
+ | `4` | `symlink_loop` |
374
375
 
375
376
  Rules: adding a frame or a field is additive and does not raise the revision; adding a word to a closed set the
376
377
  server *accepts from you* (today: the `errorCode` set) raises `DEVICE_PROTOCOL_VERSION` and adds a row here; a
@@ -590,6 +591,7 @@ with, and an executor sends only the words of the revision it declared (§1.2).
590
591
  | `shell_unavailable` | no shell / the executable could not be resolved — the command **never started** |
591
592
  | `spawn_error` | the process could not be spawned |
592
593
  | `suspended` | the work was suspended rather than completed |
594
+ | `symlink_loop` | the path runs into a looping chain of symbolic links (a link to itself, a cycle of links, or a chain past the platform's follow limit; POSIX `ELOOP`). It is not an absence: upstream a directory probe reads it as "this path leads to no directory", and a check of where a path lands reads it as unresolved, like `unknown`. A device that cannot tell a loop apart keeps answering `unknown`. On an exec instruction it is a protocol violation and the server reads it as `unknown` |
593
595
  | `target_unavailable` | the target could not be reached; the work **never started** |
594
596
  | `timeout` | **you** enforced `timeoutMs`, killed the work, and are reporting it |
595
597
  | `transport_lost` | a transport the device itself depends on went away |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/server",
3
- "version": "7.142.0",
3
+ "version": "7.143.0",
4
4
  "description": "Sema Server — the server/API implementation layer for Sema, wiring core, registry, model providers, and cloud agent execution. Built on @sema-agent/core.",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -69,7 +69,8 @@
69
69
  "build:binary:run-local:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/run-local.ts --outfile dist/run-local-darwin-arm64"
70
70
  },
71
71
  "dependencies": {
72
- "@sema-agent/core": "7.60.0",
72
+ "@modelcontextprotocol/server": "2.4.0",
73
+ "@sema-agent/core": "7.61.0",
73
74
  "@sema-agent/settings-schema": "16.0.0",
74
75
  "e2b": "^2.28.0",
75
76
  "libsodium-wrappers": "^0.8.4",