@sema-agent/server 7.144.0 → 7.146.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 (305) hide show
  1. package/MIGRATION.md +621 -0
  2. package/README.md +3 -0
  3. package/README.zh-CN.md +1 -0
  4. package/USAGE.md +233 -54
  5. package/deploy/facts/CHANGES.json +25 -0
  6. package/deploy/facts/README.md +57 -0
  7. package/deploy/facts/lanes.json +54 -0
  8. package/deploy/facts/lanes.schema.json +57 -0
  9. package/deploy/facts/sandbox-rbac.json +113 -0
  10. package/deploy/facts/sandbox-rbac.schema.json +74 -0
  11. package/deploy/facts/sealed-key.json +9 -0
  12. package/deploy/facts/sealed-key.schema.json +52 -0
  13. package/deploy/sema-up/chart/templates/_helpers.tpl +19 -0
  14. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +47 -7
  15. package/deploy/sema-up/chart/templates/server-v4pro.yaml +4 -0
  16. package/deploy/sema-up/chart/templates/server.yaml +4 -0
  17. package/deploy/sema-up/chart/values.yaml +7 -0
  18. package/deploy/sema-up/stack/sema-stack-cluster.yaml +30 -14
  19. package/dist/adoption/import-limits.d.ts +6 -0
  20. package/dist/adoption/import-limits.js +7 -0
  21. package/dist/adoption/import.d.ts +62 -0
  22. package/dist/adoption/import.js +107 -0
  23. package/dist/adoption/wire.d.ts +52 -0
  24. package/dist/adoption/wire.js +9 -0
  25. package/dist/approval-ask-machine.d.ts +2 -2
  26. package/dist/approval-card.d.ts +3 -12
  27. package/dist/approval-card.js +0 -4
  28. package/dist/approval-gate-currency.d.ts +1 -1
  29. package/dist/approval-gate-currency.js +1 -1
  30. package/dist/approval-reconciler.d.ts +1 -1
  31. package/dist/audit.d.ts +2 -21
  32. package/dist/audit.js +0 -9
  33. package/dist/boot/deployment-mods.d.ts +11 -1
  34. package/dist/boot/deployment-mods.js +15 -2
  35. package/dist/boot/device-lane.js +2 -0
  36. package/dist/boot/engine-lease.d.ts +193 -21
  37. package/dist/boot/engine-lease.js +146 -20
  38. package/dist/boot/execution-env.d.ts +4 -0
  39. package/dist/boot/execution-env.js +12 -0
  40. package/dist/boot/exit-boundary.d.ts +50 -0
  41. package/dist/boot/exit-boundary.js +32 -0
  42. package/dist/boot/exit-last.d.ts +20 -4
  43. package/dist/boot/exit-last.js +21 -4
  44. package/dist/boot/governance-seams.js +1 -0
  45. package/dist/boot/memory-consolidation.d.ts +29 -17
  46. package/dist/boot/memory-consolidation.js +26 -15
  47. package/dist/boot/reapers.d.ts +12 -4
  48. package/dist/boot/reapers.js +39 -15
  49. package/dist/boot/resolve-spec.d.ts +2 -0
  50. package/dist/boot/resolve-spec.js +38 -15
  51. package/dist/boot/runner-deps.js +1 -1
  52. package/dist/boot/session-faces.d.ts +1 -1
  53. package/dist/boot/session-faces.js +6 -4
  54. package/dist/boot/shutdown.js +27 -9
  55. package/dist/boot/stage-01-config.js +1 -1
  56. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  57. package/dist/boot/stage-03-store-backend.js +2 -2
  58. package/dist/boot/stage-04-stores.d.ts +1 -0
  59. package/dist/boot/stage-05-execution-env.d.ts +3 -0
  60. package/dist/boot/stage-05-execution-env.js +3 -3
  61. package/dist/boot/stage-06-runners.d.ts +3 -0
  62. package/dist/boot/stage-06-runners.js +11 -10
  63. package/dist/boot/stage-07-capability-layer.d.ts +3 -0
  64. package/dist/boot/stage-07-capability-layer.js +7 -2
  65. package/dist/boot/stage-08-reapers.d.ts +3 -0
  66. package/dist/boot/stage-08-reapers.js +3 -3
  67. package/dist/boot/stage-09-leader.d.ts +4 -1
  68. package/dist/boot/stage-10-http-server.d.ts +4 -1
  69. package/dist/boot/stage-10-http-server.js +4 -3
  70. package/dist/boot/stores.d.ts +1 -0
  71. package/dist/boot/stores.js +35 -19
  72. package/dist/boot/workflow-orchestration.d.ts +1 -1
  73. package/dist/boot/workflow-orchestration.js +2 -1
  74. package/dist/capabilities/composite-members.d.ts +3 -1
  75. package/dist/capabilities/composite-members.js +3 -1
  76. package/dist/capabilities/scenarios.d.ts +2 -2
  77. package/dist/capability-facts.d.ts +18 -1
  78. package/dist/capability-facts.js +25 -7
  79. package/dist/checkpoint-ctx-seal-m28.d.ts +1 -1
  80. package/dist/config-catalog.js +13 -4
  81. package/dist/config-d-migration.d.ts +3 -2
  82. package/dist/config-provider.d.ts +2 -2
  83. package/dist/config-types.d.ts +43 -4
  84. package/dist/config.d.ts +6 -1
  85. package/dist/config.js +67 -11
  86. package/dist/deleted-session-ids.d.ts +19 -0
  87. package/dist/deleted-session-ids.js +26 -0
  88. package/dist/deploy-facts.d.ts +155 -0
  89. package/dist/deploy-facts.js +94 -0
  90. package/dist/device-ws-hub.d.ts +48 -3
  91. package/dist/device-ws-hub.js +48 -2
  92. package/dist/error-code-family.d.ts +35 -0
  93. package/dist/error-code-family.js +26 -0
  94. package/dist/fleet/drain-entry.d.ts +39 -3
  95. package/dist/fleet/drain-entry.js +6 -0
  96. package/dist/fleet/fleet-bus.d.ts +15 -4
  97. package/dist/fleet/fleet-bus.js +17 -3
  98. package/dist/hooks/branch-transcript.d.ts +1 -1
  99. package/dist/hooks/branch-transcript.js +1 -0
  100. package/dist/hooks/classic-stations.d.ts +25 -8
  101. package/dist/hooks/classic-stations.js +29 -3
  102. package/dist/hooks/hook-runner.d.ts +33 -16
  103. package/dist/hooks/hook-runner.js +166 -73
  104. package/dist/hooks/hook-source.d.ts +14 -2
  105. package/dist/hooks/hook-source.js +8 -0
  106. package/dist/http/active-run-conflict.d.ts +1 -1
  107. package/dist/http/active-run-conflict.js +1 -1
  108. package/dist/http/admission.js +13 -2
  109. package/dist/http/checkpoint-ctx.d.ts +60 -71
  110. package/dist/http/checkpoint-ctx.js +35 -40
  111. package/dist/http/dispatch.js +18 -2
  112. package/dist/http/durable-run-leg.d.ts +5 -0
  113. package/dist/http/durable-run-leg.js +8 -6
  114. package/dist/http/execution-context.d.ts +4 -0
  115. package/dist/http/execution-context.js +9 -0
  116. package/dist/http/health-self-report.d.ts +6 -2
  117. package/dist/http/health-self-report.js +3 -2
  118. package/dist/http/memory-compliance-failure-reply.d.ts +1 -1
  119. package/dist/http/memory-compliance-failure-reply.js +2 -15
  120. package/dist/http/memory-control-plane-corrupt-reply.d.ts +25 -0
  121. package/dist/http/memory-control-plane-corrupt-reply.js +19 -0
  122. package/dist/http/principal-gate.d.ts +35 -12
  123. package/dist/http/principal-gate.js +16 -12
  124. package/dist/http/request-guards.d.ts +1 -1
  125. package/dist/http/request-guards.js +5 -5
  126. package/dist/http/resume-legs.d.ts +22 -0
  127. package/dist/http/resume-legs.js +31 -20
  128. package/dist/http/route-ctx.d.ts +26 -10
  129. package/dist/http/route-table.js +2 -0
  130. package/dist/http/routes/adoption.d.ts +11 -0
  131. package/dist/http/routes/adoption.js +80 -6
  132. package/dist/http/routes/agents-roster.js +1 -0
  133. package/dist/http/routes/approvals-assistant.d.ts +20 -1
  134. package/dist/http/routes/approvals-assistant.js +29 -7
  135. package/dist/http/routes/background.js +1 -0
  136. package/dist/http/routes/capabilities.js +4 -1
  137. package/dist/http/routes/diagnostics.d.ts +9 -14
  138. package/dist/http/routes/diagnostics.js +10 -20
  139. package/dist/http/routes/engine.d.ts +31 -0
  140. package/dist/http/routes/engine.js +81 -0
  141. package/dist/http/routes/memory-bundle.js +3 -0
  142. package/dist/http/routes/memory-consolidation.js +3 -5
  143. package/dist/http/routes/memory-origin.d.ts +1 -1
  144. package/dist/http/routes/memory-origin.js +2 -4
  145. package/dist/http/routes/memory-policy.js +5 -1
  146. package/dist/http/routes/observability.js +3 -6
  147. package/dist/http/routes/rules.d.ts +1 -1
  148. package/dist/http/routes/rules.js +5 -2
  149. package/dist/http/routes/runs.js +7 -3
  150. package/dist/http/routes/session-sync.js +24 -25
  151. package/dist/http/routes/sessions-list.d.ts +1 -1
  152. package/dist/http/routes/sessions.js +4 -0
  153. package/dist/http/routes/tasks.js +35 -14
  154. package/dist/http/routes/trace-usage.js +1 -1
  155. package/dist/http/run-concurrency-cap.d.ts +86 -0
  156. package/dist/http/run-concurrency-cap.js +54 -0
  157. package/dist/http/run-meta.d.ts +14 -7
  158. package/dist/http/run-meta.js +5 -2
  159. package/dist/http/server.d.ts +27 -16
  160. package/dist/http/server.js +21 -3
  161. package/dist/http/stored-body-replay.d.ts +9 -6
  162. package/dist/http/stored-body-replay.js +19 -5
  163. package/dist/http/submit-ctx.d.ts +12 -0
  164. package/dist/http/submit-ctx.js +23 -0
  165. package/dist/http/wire-types.d.ts +8 -3
  166. package/dist/json-body-limit.d.ts +8 -0
  167. package/dist/json-body-limit.js +2 -0
  168. package/dist/leader/diffout.d.ts +1 -1
  169. package/dist/local-data-root.d.ts +2 -2
  170. package/dist/memory-operator-faces.d.ts +9 -4
  171. package/dist/memory-scope-stores.d.ts +9 -7
  172. package/dist/memory-scope-stores.js +3 -14
  173. package/dist/memory-scope.d.ts +36 -0
  174. package/dist/memory-scope.js +32 -1
  175. package/dist/net-address.d.ts +53 -0
  176. package/dist/net-address.js +111 -0
  177. package/dist/observability/fail-open.d.ts +28 -12
  178. package/dist/observability/fail-open.js +28 -12
  179. package/dist/orchestration/workflow-notify-journal.js +2 -3
  180. package/dist/org-delivery-gate.js +1 -1
  181. package/dist/peer-directory.d.ts +6 -12
  182. package/dist/peer-directory.js +5 -1
  183. package/dist/plugins/approval-ask-store-file.d.ts +16 -2
  184. package/dist/plugins/approval-ask-store-file.js +21 -1
  185. package/dist/plugins/approval-ask-store-memory.d.ts +8 -2
  186. package/dist/plugins/approval-ask-store-memory.js +11 -8
  187. package/dist/plugins/caching-session-store.d.ts +6 -1
  188. package/dist/plugins/caching-session-store.js +6 -0
  189. package/dist/plugins/checkpoint-store-face.d.ts +426 -0
  190. package/dist/plugins/checkpoint-store-face.js +152 -0
  191. package/dist/plugins/file-run-store.d.ts +13 -7
  192. package/dist/plugins/file-run-store.js +12 -13
  193. package/dist/plugins/fork-routing-session-store.d.ts +6 -2
  194. package/dist/plugins/fork-routing-session-store.js +2 -2
  195. package/dist/plugins/imported-layer-root-retirement.d.ts +10 -8
  196. package/dist/plugins/local-checkpoint-store.d.ts +16 -5
  197. package/dist/plugins/local-checkpoint-store.js +25 -6
  198. package/dist/plugins/local-session-store.d.ts +80 -21
  199. package/dist/plugins/local-session-store.js +79 -22
  200. package/dist/plugins/memory-engine-pg.d.ts +29 -6
  201. package/dist/plugins/memory-engine-pg.js +96 -214
  202. package/dist/plugins/memory-engine-sql-apply.d.ts +121 -0
  203. package/dist/plugins/memory-engine-sql-apply.js +358 -0
  204. package/dist/plugins/memory-engine-tidb.d.ts +35 -9
  205. package/dist/plugins/memory-engine-tidb.js +97 -227
  206. package/dist/plugins/memory-key-guards.d.ts +1 -1
  207. package/dist/plugins/memory-origin-law.d.ts +3 -3
  208. package/dist/plugins/memory-run-store.d.ts +2 -0
  209. package/dist/plugins/memory-run-store.js +6 -0
  210. package/dist/plugins/one-time-migrations.d.ts +32 -56
  211. package/dist/plugins/one-time-migrations.js +27 -119
  212. package/dist/plugins/permission-rule-store-file.d.ts +17 -3
  213. package/dist/plugins/permission-rule-store-file.js +8 -1
  214. package/dist/plugins/pg-pool.js +1 -1
  215. package/dist/plugins/pg-session-storage.d.ts +17 -4
  216. package/dist/plugins/pg-session-storage.js +8 -4
  217. package/dist/plugins/run-store-sql.d.ts +4 -0
  218. package/dist/plugins/run-store-sql.js +5 -2
  219. package/dist/plugins/schema-ensure-legs.js +2 -2
  220. package/dist/plugins/session-fork.d.ts +31 -9
  221. package/dist/plugins/session-fork.js +3 -2
  222. package/dist/plugins/session-placement.d.ts +4 -2
  223. package/dist/plugins/session-store.d.ts +1 -1
  224. package/dist/plugins/sql-json-column.d.ts +5 -1
  225. package/dist/plugins/store-backend.d.ts +11 -8
  226. package/dist/plugins/store-backend.js +8 -7
  227. package/dist/plugins/store-contracts.d.ts +1 -11
  228. package/dist/plugins/store-contracts.js +0 -9
  229. package/dist/plugins/store-sweep.d.ts +63 -0
  230. package/dist/plugins/store-sweep.js +48 -0
  231. package/dist/plugins/tidb-pool.js +1 -1
  232. package/dist/plugins/tidb-session-store.d.ts +16 -3
  233. package/dist/plugins/tidb-session-store.js +8 -4
  234. package/dist/prompt-screen.d.ts +18 -0
  235. package/dist/prompt-screen.js +5 -0
  236. package/dist/rules-consent.d.ts +56 -51
  237. package/dist/rules-consent.js +86 -14
  238. package/dist/run-acceptance.d.ts +40 -10
  239. package/dist/run-acceptance.js +38 -5
  240. package/dist/run-cancel-context.d.ts +4 -1
  241. package/dist/run-cancel-context.js +2 -1
  242. package/dist/runs.js +2 -2
  243. package/dist/sandbox-egress-policy.js +6 -11
  244. package/dist/sandbox-rbac-rules.d.ts +88 -0
  245. package/dist/sandbox-rbac-rules.js +80 -0
  246. package/dist/sealed-key-layout.d.ts +17 -0
  247. package/dist/sealed-key-layout.js +4 -0
  248. package/dist/sealed-key.js +5 -5
  249. package/dist/security.d.ts +48 -9
  250. package/dist/security.js +9 -2
  251. package/dist/session-agent.d.ts +39 -0
  252. package/dist/session-agent.js +16 -0
  253. package/dist/session-error-wire.d.ts +4 -4
  254. package/dist/session-notify-door.d.ts +5 -3
  255. package/dist/session-resident-background.d.ts +57 -0
  256. package/dist/session-resident-background.js +20 -0
  257. package/dist/session-sync-kernel.d.ts +53 -14
  258. package/dist/session-sync-kernel.js +70 -1
  259. package/dist/session-sync.d.ts +5 -3
  260. package/dist/session-workspace.d.ts +1 -0
  261. package/dist/session-workspace.js +1 -0
  262. package/dist/sighup-idle.d.ts +9 -7
  263. package/dist/sighup-idle.js +4 -5
  264. package/dist/spec-fields.d.ts +16 -0
  265. package/dist/spec-fields.js +7 -0
  266. package/dist/stored-request-env-credentials-m38.d.ts +3 -0
  267. package/dist/stored-request-env-credentials-m38.js +8 -0
  268. package/dist/stored-request-prompt-profile-m30.d.ts +1 -1
  269. package/dist/{plugins → stores-sql}/checkpoint-store-sql.d.ts +33 -429
  270. package/dist/{plugins → stores-sql}/checkpoint-store-sql.js +70 -209
  271. package/dist/{plugins → stores-sql}/mailbox-store-sql.d.ts +2 -2
  272. package/dist/{plugins → stores-sql}/mailbox-store-sql.js +4 -4
  273. package/dist/{plugins → stores-sql}/roster-store-sql.d.ts +2 -2
  274. package/dist/{plugins → stores-sql}/roster-store-sql.js +1 -1
  275. package/dist/{plugins → stores-sql}/tool-result-store-sql.d.ts +2 -2
  276. package/dist/{plugins → stores-sql}/tool-result-store-sql.js +6 -6
  277. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.d.ts +7 -12
  278. package/dist/{plugins → stores-sql}/workflow-journal-store-sql.js +3 -8
  279. package/dist/task-settings.d.ts +59 -4
  280. package/dist/task-settings.js +50 -4
  281. package/dist/tool-approval.d.ts +2 -2
  282. package/dist/trace/core-keyset-guard.d.ts +8 -7
  283. package/dist/trace/engine-notice-wire.d.ts +1 -1
  284. package/dist/trace/engine-notice-wire.js +2 -0
  285. package/dist/trace/exit-seat.d.ts +6 -0
  286. package/dist/trace/exit-seat.js +1 -0
  287. package/dist/trace/model-segment.d.ts +3 -2
  288. package/dist/trace/model-segment.js +16 -8
  289. package/dist/trace/project.js +9 -0
  290. package/dist/trace/redact.d.ts +79 -4
  291. package/dist/trace/redact.js +523 -123
  292. package/dist/trace/self-minted-wire.d.ts +391 -1
  293. package/dist/trace/self-minted-wire.js +145 -0
  294. package/dist/trace/wire-projection-faces.d.ts +1 -1
  295. package/dist/trace/wire-projection-faces.js +1 -1
  296. package/dist/usage-analytics.d.ts +4 -0
  297. package/package.json +12 -4
  298. package/scripts/drain-check-local.mjs +8 -29
  299. package/scripts/drain-check.mysql.sql +3 -24
  300. package/scripts/drain-check.pg.sql +3 -24
  301. package/skills/update-config.md +1 -1
  302. package/wire-changes/server@7.145.0.json +146 -0
  303. package/wire-changes/server@7.146.0.json +3221 -0
  304. package/dist/approval-batch-census.d.ts +0 -27
  305. package/dist/approval-batch-census.js +0 -27
@@ -1,21 +1,28 @@
1
1
  /**
2
- * [ref] A9 装配缝的一片小叶子 —— 提交路径共用的两个纯函数(run 行元数据 + cascade 配置)。
2
+ * [ref] A9 装配缝的一片小叶子 —— 提交路径共用的三只小件(run 行元数据 + run 行预览的补写口 + cascade 配置)。
3
3
  *
4
- * 为什么单独成文件:`runMeta` 被 tasks 与 runs **两个**域模块调用,`cascadeConfig` 同理(还被测试直接引)。
5
- * 挂在任一域模块上都会造出 routes/* 互相值 import 的第二条边;它们又是纯函数(只依赖入参 + redact 叶子),
4
+ * 为什么单独成文件:`runMeta` 与预览补写口被 tasks 与 runs **两个**域模块调用,`cascadeConfig` 同理(还被测试直接引)。
5
+ * 挂在任一域模块上都会造出 routes/* 互相值 import 的第二条边;它们只依赖入参(受理座 / run 店两只口)+ 预览读法叶子,
6
6
  * 所以正确归处是叶子。server.ts 转出口 `cascadeConfig` 以保住既有 import 点。
7
7
  */
8
- import type { TaskSpec, CascadeConfig } from "@sema-agent/core";
8
+ import type { CascadeConfig } from "@sema-agent/core";
9
+ import type { RunAcceptance } from "../run-acceptance.js";
10
+ import type { RunStore } from "../plugins/store-backend.js";
9
11
  /** The denormalized run-row metadata captured at submit: correlation (jobId), credential-derived source,
10
- * and a redacted objective glance for lists. One place so the three submit paths can't drift. */
12
+ * and a redacted objective glance for lists. One place so the three submit paths can't drift.
13
+ *
14
+ * 预览只取受理座交出的、提交那一刻就知道的交付文本(律的属主 = `run-acceptance.ts`「交付的是哪一份」):腿上没有 prompt 屏 ⇒ 交付的就是请求原文,
15
+ * 建行即写;腿上有屏 ⇒ 建行**不写**(列 NULL = 没建过预览),引擎交付可写的那一份时经 {@link fillRunPreviewOnDelivery} 补写一次。本函数不读请求体。 */
11
16
  export declare function runMeta(prepared: {
12
17
  jobId?: string;
13
- spec: TaskSpec;
18
+ acceptance: Pick<RunAcceptance, "objectiveAtSubmit">;
14
19
  }, source: string | null): {
15
20
  jobId: string | null;
16
21
  source: string | null;
17
- objectivePreview: string;
22
+ objectivePreview: string | null;
18
23
  };
24
+ /** 腿上有屏时 run 行预览的补写口(建行之后、起跑之前登记):交付的那一份 → 同一只读法 → 店的写一次口(列已有值则不动)。腿上没有屏 ⇒ 受理座永不调它。 */
25
+ export declare function fillRunPreviewOnDelivery(acceptance: Pick<RunAcceptance, "onDelivered">, runStore: Pick<RunStore, "fillObjectivePreview">, taskId: string): void;
19
26
  /** Build the cascade config from the operator ladder. Default gate = `status==="completed"` (escalate
20
27
  * on a cheap-rung failure); cost-ceiling from the task's budget bounds the cold-rerun tax. */
21
28
  export declare function cascadeConfig(ladder: readonly string[], maxCostUsd?: number): CascadeConfig;
@@ -1,12 +1,15 @@
1
1
  import { buildRunObjectivePreview } from "../user-authored-text.js";
2
2
  export function runMeta(prepared, source) {
3
- const obj = prepared.spec.objective ?? "";
3
+ const delivered = prepared.acceptance.objectiveAtSubmit;
4
4
  return {
5
5
  jobId: prepared.jobId ?? null,
6
6
  source,
7
- objectivePreview: buildRunObjectivePreview(obj),
7
+ objectivePreview: delivered === undefined ? null : buildRunObjectivePreview(delivered),
8
8
  };
9
9
  }
10
+ export function fillRunPreviewOnDelivery(acceptance, runStore, taskId) {
11
+ acceptance.onDelivered("run-preview", (objective) => runStore.fillObjectivePreview(taskId, buildRunObjectivePreview(objective)));
12
+ }
10
13
  export function cascadeConfig(ladder, maxCostUsd) {
11
14
  return {
12
15
  ladder: ladder.map((model) => ({ model })),
@@ -2,7 +2,7 @@ import http from "node:http";
2
2
  import type { MemoryComplianceFaces, MemoryOriginFaces, SessionMemoryStatusFace } from "../memory-operator-faces.js";
3
3
  import type { SideQueryAccounting } from "../budget.js";
4
4
  import type { IncomingMessage } from "node:http";
5
- import { type Runner, type TaskSpec, type TaskResult, type WorkflowRunStore, type MemoryEntry, type MemoryExportBundle, type MemoryImportReport, type ConsolidationRunReceipt, type ConsolidationDriverRunRow, type SideQuerySpec, type SideQueryResult, type Brain, type EngineNotice } from "@sema-agent/core";
5
+ import { type Runner, type RunnerDeps, type TaskSpec, type TaskResult, type WorkflowRunStore, type MemoryEntry, type MemoryExportBundle, type MemoryImportReport, type SideQuerySpec, type SideQueryResult, type Brain, type EngineNotice } from "@sema-agent/core";
6
6
  import type { TaskRequestBody } from "./wire-types.js";
7
7
  import type { ServiceConfig } from "../config-types.js";
8
8
  import { type SessionMirrorRuling } from "../web-admin-client/facade.js";
@@ -31,7 +31,7 @@ import type { Metrics } from "../observability/metrics.js";
31
31
  import { type RateGate } from "../observability/rate-limit.js";
32
32
  import type { QuotaTracker } from "../observability/cost-quota.js";
33
33
  import type { ModelUsageTracker, PromptManifestTracker } from "../budget.js";
34
- import type { ConsolidationSeatLabel, ConsolidationValve } from "../boot/memory-consolidation.js";
34
+ import type { ConsolidationValve, MemoryConsolidationFaces } from "../boot/memory-consolidation.js";
35
35
  import { streamApprovals, checkQuestionAnswer } from "./routes/approvals-assistant.js";
36
36
  export { streamApprovals, checkQuestionAnswer };
37
37
  import { cascadeConfig } from "./run-meta.js";
@@ -65,7 +65,7 @@ export interface RequestAuth {
65
65
  * 平铺形的直接后果 = main.ts 一个上千行的装配字面量,键与键之间没有任何「属于同一件事」的编译期表达,
66
66
  * 键落错家/从未接线只能靠源码钉逐个盯(1.300/1.301/1.304/1.307/1.310 五起真缺陷都是这个病族)。
67
67
  *
68
- * 药:七个**窄**接口 + 顶层必填八件。装配点按组构造(每组一个带类型标注的中间 const ⇒ 受 TS 多余
68
+ * 药:七个**窄**接口 + 顶层必填件(今天九件)。装配点按组构造(每组一个带类型标注的中间 const ⇒ 受 TS 多余
69
69
  * 属性检查保护),键放错组当场编译红。
70
70
  *
71
71
  * 兼容形(本批**不动消费点**):`ServiceDeps` = 平铺视图 ∩ 组槽。旧的平铺构造(96 个测试文件 +
@@ -73,8 +73,8 @@ export interface RequestAuth {
73
73
  * 入口摊平回平铺视图,server.ts 内的 ~700 处 `deps.x` 消费点原样保留。消费点迁移是后批的事——
74
74
  * 一步到位需要同批改 ~700 处 src + ~800 处 test 键位,与「先立形、后搬家」相比没有额外收益。
75
75
  */
76
- /** createHttpServer 的**必填八件**——引擎、部署配置、提交的身份与根会话(authorizer)、请求→TaskSpec 的映射、选路席、side-query 执行席、读口场景门、对端座的 desk。
77
- * 八者恒在场,故留在顶层(不进任何可选组):没有它们 server 根本起不来,包一层可选组只会把编译期
76
+ /** createHttpServer 的**必填九件**——引擎、部署配置、提交的身份与根会话(authorizer)、请求→TaskSpec 的映射、选路席、side-query 执行席、读口场景门、对端座的 desk、部署座的 mod 注册。
77
+ * 九者恒在场,故留在顶层(不进任何可选组):没有它们 server 根本起不来,包一层可选组只会把编译期
78
78
  * 错误推迟到运行期(本仓 E-HIGH-1 病族的成因就是「可选键从未接线、编译期无声」)。 */
79
79
  export interface ServiceCoreDeps {
80
80
  runner: Runner;
@@ -172,6 +172,14 @@ export interface ServiceCoreDeps {
172
172
  * 🔴 **必填**:没有 MCP / A2A 凭据的嵌入也给一只 `createPeerDesk()`(零 IO)—— 一个部署只有一张登记表,缺席不是「没有座」的另一种读法。
173
173
  */
174
174
  peerDesk: PeerDesk;
175
+ /**
176
+ * 部署座的 mod 注册(`RunnerDeps.mods`:这个部署每只 Runner 都带的同一份;起服第五段装配口 `assembleDeploymentMods` 交回,装配点把同一个值交给 Runner 与这里)。
177
+ * 本层只问它一件事:这条腿上有没有 prompt 屏(`prompt-screen.ts` 的 `promptScreenDeclared`,读注册**声明**的事件,连同任务座 `TaskSpec.mods`)——
178
+ * 有屏 ⇒ 受理座把目标落盘(run 行预览 · 续跑体 · 会话标题 · fleet 行名)推迟到引擎原样交付之后(读法全文 `run-acceptance.ts`「交付的是哪一份」)。
179
+ * 🔴 **必填**:缺席若读成「没有屏」,装了屏的部署会把屏前文本落进上面四处,而且谁也看不见 —— 机密轴上没有静默的那一侧。没有 mod 的嵌入显式传 `[]`;
180
+ * 与交给 Runner 的必须是同一个值(两处不同 = 嵌入方的装配错,本层读不出来)。
181
+ */
182
+ deploymentMods: NonNullable<RunnerDeps["mods"]>;
175
183
  }
176
184
  /** **durable 持久面**:每个键 present ⇔ 对应路由/能力位在场,absent ⇒ 501(诚实缺席),
177
185
  * 绝不静默降级。装配点 = main.ts 的 store backend 开箱段。 */
@@ -573,15 +581,9 @@ export interface ServiceSeamDeps {
573
581
  * **启动期**就被拒了(`boot/memory-consolidation.ts`),那些机器根本起不来。⇒ 缺席形是 **404 族**
574
582
  * (整域不挂),刻意**不是** 501:501 说「换部署形态」,这里的动作是「把旋钮打开」。全文见
575
583
  * `routes/memory-consolidation.ts` 头注。
576
- * 🔴 `seat` 的词表单一真源 = `boot/memory-consolidation.ts` 的 {@link ConsolidationSeatLabel}
577
- * (type-only import,tsc 擦除、非装载边)——在这里再写一遍字面量联合就是第二份会漂的词表。 */
578
- memoryConsolidation?: {
579
- readonly seat: ConsolidationSeatLabel;
580
- readonly model: string;
581
- readonly scopes: readonly string[];
582
- run(scope: string): Promise<ConsolidationRunReceipt>;
583
- lastRun(scope: string): ConsolidationDriverRunRow | undefined;
584
- };
584
+ * 🔴 形的单一真源 = `boot/memory-consolidation.ts` 的 {@link MemoryConsolidationFaces}(type-only import,tsc 擦除、非装载边)——
585
+ * 在这里再写一遍成员表就是第二份会漂的形(core 7.63.0 把 `lastRun` 改成异步时这里的手抄形正好漂了一次)。 */
586
+ memoryConsolidation?: MemoryConsolidationFaces;
585
587
  /** [ref] §3.1 / [ref] §1.3([ref] S-2):per-principal memory-capture opt-out 授权表的管理面
586
588
  * (`/v1/admin/memory-optout` 四口,operator-only)—— `backend.memoryOptOutGrant()` 的同一只(与
587
589
  * `RuntimeCaps.allowMemoryOptOut` 的 resolver 腿读同一张表,零缓存直读 ⇒ 写后下一次 prepare 即生效)。
@@ -721,7 +723,10 @@ export interface ServiceDeploymentDeps {
721
723
  * orchestrator/shell retries against the replacement instance; read paths + in-flight verbs (steer/cancel/
722
724
  * respond/subagent) stay open so the runs we are waiting on can still finish/interact.
723
725
  * • `inflight` is ASSIGNED BY createServer (a live union of this replica's in-flight legs: durable bg/resume
724
- * `inflightRuns` + live sync/resume streams `steerableRuns`) — main.ts polls it to know when drain is done.
726
+ * `inflightRuns` + live sync/resume streams `steerableRuns`, the leader leg, and the session-resident background
727
+ * shells/monitors — `inflightBreakdown` is the same read split into its three terms) — an observable fact (`/health.inflight`).
728
+ * `blockingInflight` (also ASSIGNED BY createServer) is the same breakdown minus the session-resident term: "is there still work that
729
+ * must be waited out" — the drain wait main.ts polls after SIGTERM, the orphan/lease windows and the SIGHUP idle window read THAT.
725
730
  * • /health carries `draining:true` + `version` (build self-description); draining is a readiness reason
726
731
  * (`http/readiness.ts`) ⇒ `/readyz` 503 + `/health` `ready:false` (the k8s readiness 摘流信号).
727
732
  * • [ref]:`attachedStreams` 同样 ASSIGNED BY createServer —— 本副本当前打开着的 SSE 流条数
@@ -732,6 +737,12 @@ export interface ServiceDeploymentDeps {
732
737
  * 硬停起点各调一次。
733
738
  */
734
739
  drainState?: import("../fleet/drain-entry.js").DrainState;
740
+ /**
741
+ * 在飞数第四类的读口:本进程**会话驻留的后台 shell / monitor** 行数(`session-resident-background.ts` 的 `countSessionResidentBackground` 在 composition root 绑上围栏表;
742
+ * 法与边界见该文件头)。`createHttpServer` 把它作为 `inflightBreakdown.sessionResident` 并进总数 `drainState.inflight` 的**同一次读**;它**不**进阻断集
743
+ * `drainState.blockingInflight`(排空 / 孤儿窗与附着租约自退 / SIGHUP 空闲窗判停读阻断集)。缺席(嵌入方 / 夹具)= 这一项恒 0,`inflight` 逐字同改前。
744
+ */
745
+ sessionResidentBackground?: () => number;
735
746
  /** 编排器片 2 件 3:服务租约此刻的读数(`fleet/serving-lease.ts` 的客户端交出;引擎信号铸点与计费准入判同读)。缺席 = 没装租约客户端
736
747
  * (非 fleet 成员 / 没接中心 / 测试夹具)⇒ `none`(与没有租约的引擎逐字同)。 */
737
748
  servingLease?: () => import("../fleet/engine-signal.js").ServingLeaseView;
@@ -880,7 +891,7 @@ export interface ServiceDepGroups {
880
891
  deployment?: ServiceDeploymentDeps;
881
892
  knobs?: ServiceKnobDeps;
882
893
  }
883
- /** 平铺视图 = 必填八件 + 其余七组的全可选并集。**派生**自组接口,故与分组形永不漂。 */
894
+ /** 平铺视图 = 必填九件 + 其余七组的全可选并集。**派生**自组接口,故与分组形永不漂。 */
884
895
  export type FlatServiceDeps = ServiceCoreDeps & Partial<ServiceStoreDeps & ServiceCoordinatorDeps & ServiceSeamDeps & ServiceObservabilityDeps & ServiceGovernanceDeps & ServiceDeploymentDeps & ServiceKnobDeps>;
885
896
  /** createHttpServer 的入参:两形皆可(平铺=存量,分组=推荐)。同名键两形都给时,**顶层显式键赢**
886
897
  * (含显式 `undefined`)——一处装配点只该用一形,混用时以肉眼最近的那个为准。 */
@@ -9,6 +9,7 @@ import {} from "../memory-sync.js";
9
9
  import {} from "../orchestration/workflow-agent-steer.js";
10
10
  import {} from "../orchestration/workflow-completion-inbox.js";
11
11
  import {} from "../fleet/fleet-bus.js";
12
+ import { blockingInflightOf, inflightTotal } from "../fleet/drain-entry.js";
12
13
  import {} from "../tool-approval.js";
13
14
  import {} from "../observability/rate-limit.js";
14
15
  import { IdempotencyCache } from "./idempotency.js";
@@ -20,6 +21,7 @@ import { coarseStatusForState, errorCodeForExit } from "./routes/images.js";
20
21
  export { coarseStatusForState, errorCodeForExit };
21
22
  import { sendError, sendHttpError, sendOpenFaceFault, logRequestError } from "./send.js";
22
23
  import { createRequestGuards } from "./request-guards.js";
24
+ import { createRunConcurrencyGate, liveRunIdsOf } from "./run-concurrency-cap.js";
23
25
  import { sqlEngineSeatIssue } from "../sql-engine-posture.js";
24
26
  import { parkDecisionStoreRefusal } from "../orchestration/workflow-park-decision.js";
25
27
  import { CHECKPOINT_CTX_SEALING_REQUIRED } from "./checkpoint-ctx.js";
@@ -66,6 +68,9 @@ export function createHttpServer(rawDeps) {
66
68
  if (typeof deps.authorize !== "function") {
67
69
  throw new Error("createHttpServer: the authorize seat is required — it is the only resolver of a submission's root session; pass `createAuthorizer(config, runner.sessions)` for a single-user embedding; refusing to start rather than run submissions on a root nobody attested");
68
70
  }
71
+ if (!Array.isArray(deps.deploymentMods)) {
72
+ throw new Error("createHttpServer: the deploymentMods seat is required — it is how the server learns whether a prompt screen runs on its legs (the same registrations the Runners carry as `mods`); pass `[]` for an embedding without mods; refusing to start rather than store objective text before the screen decides");
73
+ }
69
74
  {
70
75
  const retired = retiredServiceDepsRefusal(deps);
71
76
  if (retired !== undefined)
@@ -101,16 +106,28 @@ export function createHttpServer(rawDeps) {
101
106
  const steerableRuns = new Map();
102
107
  const wakeParkMints = new Map();
103
108
  const counters = { uncountedBillableInflight: 0, admittedInflight: 0 };
104
- if (deps.drainState)
105
- deps.drainState.inflight = () => new Set([...inflightRuns.keys(), ...steerableRuns.keys()]).size + counters.uncountedBillableInflight + counters.admittedInflight + (deps.leaderEndpoint?.inflight?.() ?? 0);
109
+ const blockingParts = () => ({
110
+ http: liveRunIdsOf(inflightRuns, steerableRuns).size + counters.uncountedBillableInflight + counters.admittedInflight,
111
+ leader: deps.leaderEndpoint?.inflight?.() ?? 0,
112
+ });
113
+ const inflightBreakdown = () => ({ ...blockingParts(), sessionResident: deps.sessionResidentBackground?.() ?? 0 });
114
+ if (deps.drainState) {
115
+ deps.drainState.inflightBreakdown = inflightBreakdown;
116
+ deps.drainState.inflight = () => inflightTotal(inflightBreakdown());
117
+ deps.drainState.blockingInflight = () => blockingInflightOf(blockingParts());
118
+ }
106
119
  let lastBillableActivityAt = performance.now();
120
+ let firstBillableActivityAt = null;
107
121
  const noteActivity = (url) => {
108
122
  if (isProbePath(url))
109
123
  return;
110
124
  lastBillableActivityAt = performance.now();
125
+ firstBillableActivityAt ??= lastBillableActivityAt;
111
126
  };
112
127
  if (deps.drainState)
113
128
  deps.drainState.lastActivityAt = () => lastBillableActivityAt;
129
+ if (deps.drainState)
130
+ deps.drainState.firstActivityAt = () => firstBillableActivityAt;
114
131
  if (deps.drainState)
115
132
  deps.drainState.yieldInflight = () => preemptableRuns.yieldAll();
116
133
  if (deps.hookWakeBus) {
@@ -152,7 +169,8 @@ export function createHttpServer(rawDeps) {
152
169
  }
153
170
  const { quotaExceeded, ownerCostQuotaDenied, ownerFleetLeaseDenied, ownerLeaseDenied, ownerQuotaDenied, leaseDenied, usageWindowDenialFor, usageWindowDenied, rateLimited, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk, readJson, readRawBody } = createRequestGuards({ deps });
154
171
  const { prepareSpec } = createAdmission({ deps, readJson, runnerFor });
155
- const { routeCtxBase, resumeCheckpoint, driveResumeIntoRunLog, driveResumeLeg, mayDiscloseRowError, resumePreempted, startFreshHostTurn, resumeWake, sessionNotifyDoor } = createResumeLegs({ deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor });
172
+ const { admitRun } = createRunConcurrencyGate({ deps, inflightRuns, steerableRuns });
173
+ const { routeCtxBase, resumeCheckpoint, driveResumeIntoRunLog, driveResumeLeg, mayDiscloseRowError, resumePreempted, startFreshHostTurn, resumeWake, sessionNotifyDoor } = createResumeLegs({ deps, runnerFor, idemCache, inflightRuns, preemptableRuns, steerableRuns, wakeParkMints, counters, readJson, readRawBody, rateLimited, quotaExceeded, ownerQuotaDenied, leaseDenied, ownerLeaseDenied, usageWindowDenied, admitRun, safeDecode, runOwnerOk, sessionOwnerOk, runSessionAcceptOk, prepareSpec, finalizeTaskResult, ownerCostQuotaDenied, ownerFleetLeaseDenied, usageWindowDenialFor });
156
174
  const { denyExpiredApprovals, redeemParkedAsk } = createApprovalSweeps({ deps, resumeCheckpoint, mayDiscloseRowError });
157
175
  const { handle } = createDispatch({ deps, noteActivity, routeCtxBase, counters });
158
176
  const server = http.createServer((req, res) => {
@@ -1,19 +1,20 @@
1
1
  /**
2
- * 「这条停驻行的持久化体在本版上能否重放」—— **唯一**判据(四问一只;五个问点同调:续跑驱动段 `resume-legs.ts` `resolveResumeSpecOrBlocked`、
2
+ * 「这条停驻行的持久化体在本版上能否重放」—— **唯一**判据(四问一只,外加 `settings.env` 凭据一臂;五个问点同调:续跑驱动段 `resume-legs.ts` `resolveResumeSpecOrBlocked`、
3
3
  * plan_review 的 prepare 段、SLA 拒扫腿的跳过臂 `approval-sweeps.ts`、终态 run 的 steer 铸唤醒卡之前 `routes/runs.ts`;`resolveSpec` 本身留作提交腿的拒点)。
4
4
  * 四问的规则、记号、拒句与删除条件的属主 = `http/checkpoint-ctx.ts` 顶注;答复 = 那里的 `storedBodyReplayAnswer`。
5
5
  *
6
- * 为什么判据住在这里而不是 `checkpoint-ctx.ts`:第三问要读体的**生效模式词**(`task-settings.ts` `storedBodyPermissionMode`,请求设置的解析层)与本部署此刻的
6
+ * 为什么判据住在这里而不是 `checkpoint-ctx.ts`:模式词问要读体的**生效模式词**(`task-settings.ts` `storedBodyPermissionMode`,请求设置的解析层)与本部署此刻的
7
7
  * **入口地板**(`runtime-governance.ts` `autonomyModeFloor`)—— 这两只属主带着配置层的包外依赖;`checkpoint-ctx.ts` 被随包的本地排空脚本经编译产物 import,
8
- * 它的闭包只许 node 内建 / 引擎包根(`test/drain-check-local.test.ts` 的闭包门)。判据只有这一只,前两问的谓词照旧住在各自属主里。
8
+ * 它的闭包只许 node 内建 / 引擎包根(`test/drain-check-local.test.ts` 的闭包门)。判据只有这一只,allow 读法问、工具名清单问与退役请求键问的谓词照旧住在各自属主里。
9
9
  */
10
10
  import type { PermissionMode } from "@sema-agent/core";
11
11
  import { type Autonomy } from "../runtime-governance.js";
12
12
  import { type ExecutionConfigRootDeployment } from "../execution-config-root.js";
13
+ import { type ModelApiKeyEnvTable } from "../server-secret-env.js";
13
14
  import { type StoredBodyReplayRefusal } from "./checkpoint-ctx.js";
14
15
  import type { CheckpointCtx } from "./route-ctx.js";
15
16
  /**
16
- * 第三问:本行体里的模式词能否按当前读法重放。不能 ⇔ 本部署此刻有入口地板 ∧ 行上没有当前的模式词读法记号 ∧ 体的生效词进这块地板会变
17
+ * 模式词问:本行体里的模式词能否按当前读法重放。不能 ⇔ 本部署此刻有入口地板 ∧ 行上没有当前的模式词读法记号 ∧ 体的生效词进这块地板会变
17
18
  * (`runtime-governance.ts` `entryPermissionMode`,与提交入口的盖章同一只函数;判的是「词变不变」而不是 `clamped` —— 体无词的旧行进地板取地板本身,
18
19
  * 那也是变)⇒ 返回拒因的两个事实(体的词、地板);能 ⇒ `undefined`。
19
20
  */
@@ -22,13 +23,15 @@ export declare function permissionModeReadingUnstated(ctx: CheckpointCtx, floor:
22
23
  floor: PermissionMode;
23
24
  } | undefined;
24
25
  /**
25
- * 预判读的部署配置:`autonomy`(第三问的模式词地板)、执行车道的 home 两位(`~/` 形规则那一问)与配置根的几位(user 设置层 `/` 规则那一问)。`ServiceConfig` 结构上满足,四个问点传 `deps.config` 整份。
26
+ * 预判读的部署配置:`autonomy`(模式词问的模式词地板)、执行车道的 home 两位(`~/` 形规则那一问)与配置根的几位(user 设置层 `/` 规则那一问)。`ServiceConfig` 结构上满足,四个问点传 `deps.config` 整份。
26
27
  */
27
28
  export type StoredReplayDeployment = ExecutionConfigRootDeployment & {
28
29
  readonly autonomy?: Autonomy | undefined;
30
+ /** 生效配置模型条目 name → `apiKeyEnv`(`settings.env` 凭据判据的名单一臂;缺席 = 没有模型条目,与 `serverCredentialValueSources` 的缺省同读)。 */
31
+ readonly modelApiKeyEnv?: ModelApiKeyEnvTable | undefined;
29
32
  };
30
33
  /**
31
- * 四问一只判据:不能重放 ⇒ 返回拒的那条规则;能 ⇒ `undefined`。顺序:allow 读法记号 → 请求设置里的条目表(**与拒点同序**:桶门〔形 / 空串 / 超帽〕→ 工具名清单读成空〔**只问限制方向**:放行方向的存量条目读成空,续跑照旧、只留一行运维 warn〕→ deny / ask 的名字层 + 规则文法)→ 模式词读法记号 → 体里的退役请求键(几问同中时答先中的那一问 ——
34
+ * 四问一只判据:不能重放 ⇒ 返回拒的那条规则;能 ⇒ `undefined`。顺序:allow 读法(体里带非空 `allow` 而写者版本证不出 ≥ 7.117.0;常设,按写者版本)→ 请求设置里的条目表(**与拒点同序**:桶门〔形 / 空串 / 超帽〕→ 工具名清单读成空〔**只问限制方向**:放行方向的存量条目读成空,续跑照旧、只留一行运维 warn〕→ deny / ask 的名字层 + 规则文法)→ 模式词读法记号 → 体里的退役请求键(几问同中时答先中的那一问 ——
32
35
  * 任一问中都不重放,句子只需一句)。`deployment` = 本部署**此刻**的 `config`(活 config:热改即生效):`autonomy` 的地板由它的唯一属主算,`~/` 形规则的 home 由 `executionLaneHomeDir` 算,问点都不自己算。
33
36
  */
34
37
  export declare function storedBodyReplayRefusal(ctx: CheckpointCtx, deployment: StoredReplayDeployment): StoredBodyReplayRefusal | undefined;
@@ -1,4 +1,4 @@
1
- import { REQUEST_SETTINGS_REFUSAL_PREFIX, SETTINGS_PERMISSION_BUCKETS, SETTINGS_RULE_REFUSAL_CODE, neverAutoNamesRefusal, rulePolicyBucketsRefusal, settingsPermissionBucketIssue, storedBodyPermissionMode } from "../task-settings.js";
1
+ import { REQUEST_SETTINGS_REFUSAL_PREFIX, SETTINGS_PERMISSION_BUCKETS, SETTINGS_RULE_REFUSAL_CODE, neverAutoNamesRefusal, readSettingsEnv, rulePolicyBucketsRefusal, settingsEnvCredentialIssue, settingsPermissionBucketIssue, storedBodyPermissionMode } from "../task-settings.js";
2
2
  import { readTightenOnlySeats, tightenOnlySeatField, tightenOnlySeatIssue } from "../tighten-only-seats.js";
3
3
  import { entryProblemsFacts } from "../entry-problems.js";
4
4
  import { autonomyModeFloor, entryPermissionMode } from "../runtime-governance.js";
@@ -6,7 +6,9 @@ import { inertToolListEntryOf, replayInertEntry } from "../rule-entry-list.js";
6
6
  import { executionLaneHomeDir } from "../execution-lane-caps.js";
7
7
  import { executionConfigRootOf } from "../execution-config-root.js";
8
8
  import { migrationWindowEnd } from "../plugins/one-time-migrations.js";
9
- import { allowListReadingEraOf, allowListReadingUnstated, permissionModeReadingStated, retiredRequestKeyOf } from "./checkpoint-ctx.js";
9
+ import { storedEnvCredentialsDropM38 } from "../stored-request-env-credentials-m38.js";
10
+ import { serverCredentialValueSources } from "../server-secret-env.js";
11
+ import { allowListWriterUnproven, permissionModeReadingStated, retiredRequestKeyOf } from "./checkpoint-ctx.js";
10
12
  export function permissionModeReadingUnstated(ctx, floor) {
11
13
  if (floor === undefined || permissionModeReadingStated(ctx))
12
14
  return undefined;
@@ -57,11 +59,23 @@ function storedRulePolicyRefusal(body, deployment) {
57
59
  const neverAutoRefused = neverAutoNamesRefusal(neverAuto);
58
60
  return neverAutoRefused === undefined ? undefined : replayInertEntry(tightenOnlySeatField("neverAuto"), "deny", SETTINGS_RULE_REFUSAL_CODE, `${REQUEST_SETTINGS_REFUSAL_PREFIX}${neverAutoRefused.message}`, { ...entryProblemsFacts(neverAutoRefused.problems) });
59
61
  }
62
+ function storedEnvCredentialRefusal(body, deployment) {
63
+ const issue = settingsEnvCredentialIssue(readSettingsEnv(body?.settings).reaching, serverCredentialValueSources(deployment.modelApiKeyEnv));
64
+ if (issue === null || storedEnvCredentialsDropM38(new Date().toISOString().slice(0, 10)))
65
+ return undefined;
66
+ return { errorCode: "request.env_credential_refused", message: issue.message, facts: { ...issue.fields } };
67
+ }
60
68
  export function storedBodyReplayRefusal(ctx, deployment) {
61
69
  const writer = ctx.writerVersion !== undefined ? { writerVersion: ctx.writerVersion } : {};
62
- if (allowListReadingUnstated(ctx))
63
- return { kind: "allow_list_reading", era: allowListReadingEraOf(ctx), ...writer };
64
- const entry = storedPermissionBucketRefusal(ctx.body) ?? storedTightenOnlySeatRefusal(ctx.body) ?? inertToolListEntryOf(ctx.body, ["deny"]) ?? storedRulePolicyRefusal(ctx.body, deployment);
70
+ if (allowListWriterUnproven(ctx))
71
+ return { kind: "allow_list_reading", ...writer };
72
+ const early = storedPermissionBucketRefusal(ctx.body) ?? storedTightenOnlySeatRefusal(ctx.body);
73
+ if (early !== undefined)
74
+ return { kind: "inert_tool_list", entry: early };
75
+ const envCredential = storedEnvCredentialRefusal(ctx.body, deployment);
76
+ if (envCredential !== undefined)
77
+ return { kind: "settings_env_credential", refusal: envCredential };
78
+ const entry = inertToolListEntryOf(ctx.body, ["deny"]) ?? storedRulePolicyRefusal(ctx.body, deployment);
65
79
  if (entry !== undefined)
66
80
  return { kind: "inert_tool_list", entry };
67
81
  const mode = permissionModeReadingUnstated(ctx, autonomyModeFloor(deployment.autonomy));
@@ -3,6 +3,9 @@ import type { Logger } from "../observability/logger.js";
3
3
  import type { Metrics } from "../observability/metrics.js";
4
4
  import { buildCheckpointCtx, type CheckpointCtxSealer, type SealedCheckpointCtx } from "./checkpoint-ctx.js";
5
5
  import { type RefusalBody } from "./refusal-body.js";
6
+ import type { CheckpointCtx, PreparedTaskSubmission } from "./route-ctx.js";
7
+ /** 这份续跑输入里体的目标是不是**不可展示**的那一份(`CheckpointCtx.promptScreened` 在场)。读续跑体目标的展示面只经这一只问;落盘的 JSON 逐位判形,不信型面。 */
8
+ export declare function objectiveWithheldIn(ctx: Pick<CheckpointCtx, "promptScreened">): boolean;
6
9
  /** 写不进的那一种可答复的结局(各腿把它交回自己的出口:同步 / durable 腿直接回,活流腿编码成终帧)。 */
7
10
  export interface SubmitCtxRefusal {
8
11
  readonly status: 413 | 409;
@@ -23,4 +26,13 @@ export declare function persistSubmitCtx(store: {
23
26
  readonly runStore: RunStore;
24
27
  readonly taskId: string;
25
28
  } | undefined, sessionId: string, fields: Parameters<typeof buildCheckpointCtx>[0]): Promise<SubmitCtxRefusal | undefined>;
29
+ /**
30
+ * 提交腿写续跑输入的入口(三个调用点同调这一只):体与标记按受理座提交时交出的那一份定(见文件头「体里的目标按哪一份写」),只写这一笔。
31
+ * 写不进的处置与答复 = {@link persistSubmitCtx}(本函数只定体、不另判)。
32
+ */
33
+ export declare function persistSubmissionCtx(store: {
34
+ putCtx(sessionId: string, ctx: SealedCheckpointCtx): Promise<void>;
35
+ }, deps: Parameters<typeof persistSubmitCtx>[1], claim: Parameters<typeof persistSubmitCtx>[2], prepared: Pick<PreparedTaskSubmission, "body" | "auth" | "verify" | "acceptance" | "bodyFromScreenedCtx"> & {
36
+ readonly spec: Pick<PreparedTaskSubmission["spec"], "sessionId" | "model">;
37
+ }): Promise<SubmitCtxRefusal | undefined>;
26
38
  //# sourceMappingURL=submit-ctx.d.ts.map
@@ -3,6 +3,17 @@ import { releaseClaimedRun } from "./claimed-run.js";
3
3
  import { buildCheckpointCtx, CheckpointCtxUnsealableError } from "./checkpoint-ctx.js";
4
4
  import { refusalBody } from "./refusal-body.js";
5
5
  import { limitFactsUnmeasured } from "../limit-refusal.js";
6
+ import { serviceVersion } from "../service-version.js";
7
+ import { recordFailOpen } from "../observability/fail-open.js";
8
+ import { DEFAULT_MODEL_REF } from "../model-select.js";
9
+ const WITHHELD_OBJECTIVE = "";
10
+ export function objectiveWithheldIn(ctx) {
11
+ return ctx.promptScreened === true;
12
+ }
13
+ function withheldBody(body, picked) {
14
+ const pin = typeof body.model !== "string" && typeof picked === "string" && picked !== DEFAULT_MODEL_REF ? { model: picked } : {};
15
+ return { ...body, objective: WITHHELD_OBJECTIVE, ...pin };
16
+ }
6
17
  function unsealableRefusal(e) {
7
18
  return {
8
19
  status: 409,
@@ -53,4 +64,16 @@ export async function persistSubmitCtx(store, deps, claim, sessionId, fields) {
53
64
  return rowTooLargeRefusal(tooLarge);
54
65
  }
55
66
  }
67
+ export async function persistSubmissionCtx(store, deps, claim, prepared) {
68
+ const sessionId = prepared.spec.sessionId;
69
+ const screened = prepared.acceptance.objectiveAtSubmit === undefined;
70
+ if (screened && prepared.verify !== undefined)
71
+ recordFailOpen("server.objective-delivery.verify-replay-copy", `session=${sessionId}`);
72
+ return persistSubmitCtx(store, deps, claim, sessionId, {
73
+ body: screened && prepared.verify === undefined ? withheldBody(prepared.body, prepared.spec.model) : prepared.body,
74
+ memoryScope: prepared.auth?.memoryScope,
75
+ ...(screened || prepared.bodyFromScreenedCtx === true ? { promptScreened: true } : {}),
76
+ writerVersion: serviceVersion(),
77
+ });
78
+ }
56
79
  //# sourceMappingURL=submit-ctx.js.map
@@ -172,9 +172,14 @@ export interface TaskRequestBody {
172
172
  * `outputSchema` (core ignores it there). */
173
173
  outputRetries?: number;
174
174
  /** Within-task compaction tuning (core [ref] `TaskSpec.compaction`). ⚠️ ONLY `clampTolerance` is a caller
175
- * knob — the rest of core's compaction object is an OPERATOR axis and is deliberately not on the wire, so this
176
- * type is narrower than core's field on purpose. `boot/resolve-spec.ts` accepts a finite number in [0,1] and
177
- * omits the whole `compaction` key otherwise. Pairs with `compactionModel` (which gear compacts). */
175
+ * knob HERE — the rest of core's compaction object is an OPERATOR axis and is deliberately not on the wire, so this
176
+ * type is narrower than core's field on purpose. `spec-fields.ts` `compactionSpecFragment` accepts a finite number
177
+ * in [0,1] and omits the whole `compaction` key otherwise. Pairs with `compactionModel` (which gear compacts).
178
+ * The other caller-owned member of core's `compaction`, the automatic-compaction seat `autoCompact`, rides the CC
179
+ * settings key `settings.autoCompactEnabled` (boolean; 7.146.0, capability bit `taskSettings.autoCompactEnabled`) —
180
+ * ONE seat per fact: it is deliberately NOT repeated here (`compaction.autoCompact` in the body is not read). Both
181
+ * members are minted into the one `TaskSpec.compaction` object by the same mapper. The manual seat `manualCompact`
182
+ * (the pass `POST /v1/runs/:id/compact` asks for) is not caller-writable: it takes the deployment's / engine's default. */
178
183
  compaction?: {
179
184
  clampTolerance?: number;
180
185
  };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 一切 JSON 请求体的字节上限 —— `readJson` 边读边计,越过即 413 `request.payload_too_large`(一条一直都在的门,`http/request-guards.ts`)。
3
+ *
4
+ * 住叶子(零依赖)而不是守门族里:依它定帽的面要读同一个数 —— 收编批量导入的「每个会话信封最多多少字节」只有落在它之下才可达
5
+ * (一个越过它的信封根本进不了门,把旋钮设到那里就是一只永远够不着的帽),配置的上帽从这里算(`adoption/import-limits.ts`)。
6
+ */
7
+ export declare const MAX_JSON_BODY_BYTES: number;
8
+ //# sourceMappingURL=json-body-limit.d.ts.map
@@ -0,0 +1,2 @@
1
+ export const MAX_JSON_BODY_BYTES = 8 * 1024 * 1024;
2
+ //# sourceMappingURL=json-body-limit.js.map
@@ -45,7 +45,7 @@ export type PullDiffResult = {
45
45
  * 而它**本就已经** `import { safeRev } from "./diffout.js"` —— 依赖边在,共享零成本。两份手抄的后果不是
46
46
  * 「多写一行」,是**单边改值后上行/pull 两腿对同一个 diff 的过/拒判定分家**:同一份补丁在一条腿上过、
47
47
  * 另一条腿上被截断,而两处都「按自己的常量」正确工作,没有任何一处会报错。
48
- * (同族先例:`checkpoint-store-sql.ts` 的 `export const TERMINAL_GRACE_MS`。)
48
+ * (同族先例:`checkpoint-store-face.ts` 的 `export const TERMINAL_GRACE_MS`。)
49
49
  */
50
50
  export declare const DEFAULT_MAX_DIFF_BYTES: number;
51
51
  export declare function safeRev(rev: string): boolean;
@@ -7,8 +7,8 @@
7
7
  * `scripts/drain-check-local.mjs`(经 `dist/local-data-root.js` 取同一只函数,缺省数据根不再自抄一份链:抄本只对了一半,
8
8
  * 设了 `AGENT_DATA_DIR` 的部署不带参数跑脚本会读到另一个目录并报「干净」)。纯叶、零反向依赖 ⇒ 脚本引它不会把整个服务装载起来。
9
9
  *
10
- * ⚠️ 与**配置根**(`configLocalRoot`:`CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`,不吃 `LOCAL_DATA_ROOT`)不是同一条链 —— 两根分家的那一形由
11
- * boot 期的 `local_roots_split` 告警说出来,本函数不改那条解析。
10
+ * ⚠️ 与**配置根**(`configLocalRoot`:`CONFIG_LOCAL_DIR` → `AGENT_DATA_DIR` → `~/.ai-agent`,不吃 `LOCAL_DATA_ROOT`)不是同一条链 —— 两根一声不响分家的那一形
11
+ * (没给 `CONFIG_LOCAL_DIR`)由 boot 期的 `local_roots_split` 告警说出来,本函数不改那条解析。
12
12
  */
13
13
  export declare function resolveLocalDataRoot(env: Readonly<Record<string, string | undefined>>, home: string): string;
14
14
  //# sourceMappingURL=local-data-root.d.ts.map
@@ -58,7 +58,7 @@
58
58
  * core 自己响亮拒(`memory.erasure_evidence_unavailable`),那是**运行期**的诚实拒,与「换部署形态才行」
59
59
  * 不是一回事(附录 A 两条分列)。
60
60
  */
61
- import { type EntryProvenanceAccount, type MemoryBackend, type MemoryEntryOrigin, type MemoryErasureAttestation, type OriginClearanceRow, type DeploymentSessionMemoryStatus, type SessionCaptureRecordStore } from "@sema-agent/core";
61
+ import { type EntryProvenanceAccount, type MemoryBackend, type MemoryEntryOrigin, type MemoryErasureAttestation, type OriginClearanceRow, type DeploymentSessionMemoryStatus, type SessionCaptureRecordStore, type SessionMemoryStatus } from "@sema-agent/core";
62
62
  import { type MemoryScopeStores } from "./memory-scope-stores.js";
63
63
  import type { SessionCaptureRecordSource } from "./plugins/store-backend.js";
64
64
  /**
@@ -249,11 +249,16 @@ export interface SessionMemoryStatusFace {
249
249
  /** 一份会话记忆状态的读法(见 {@link SessionMemoryStatusFace.readingAt})。 */
250
250
  export interface SessionMemoryStatusReading {
251
251
  readonly key: string;
252
- /** 引擎的答案,原样(引擎不抛:读不出的源 = 缺席的键)。路由只投 wire 的五键。 */
252
+ /** 引擎的答案,原样(引擎不抛:读不出的源 = 缺席的键)。路由只投 wire 的键。 */
253
253
  status: (sessionId: string) => Promise<SessionMemoryView>;
254
254
  }
255
- /** 状态面上 wire 的五键所在的那一份:部署对象答案去掉逐店明细 `planes`(逐店明细不上 wire,见 `core-keyset-guard.ts`)。 */
256
- export type SessionMemoryView = Omit<DeploymentSessionMemoryStatus, "planes">;
255
+ /**
256
+ * 状态面上 wire 的键所在的那一份,**引擎的答案原样**:带层座 ⇒ 那只店的引擎答案(`SessionMemoryStatus`:五键 + `writeHome`,`writeHome` 是店绑定的键);
257
+ * 不带层座 ⇒ 部署对象的答案(`DeploymentSessionMemoryStatus`:引擎折好的五键 + 引擎择好的会话级 `writeHome` + 逐店明细 `planes`,每行是一只店原样的 `SessionMemoryStatus`)。
258
+ * 会话级 `writeHome` 由引擎在店之间择一(点名家的店里较新的那只的家,原样;任一店读不出 / 同刻 / 无店点名 ⇒ 缺席);本服务不折、不择一、不去重,
259
+ * 路由把顶层 `writeHome` 与每行的 `plane` / `writeHome` 原样投出(见 `core-keyset-guard.ts` ⑩e)。
260
+ */
261
+ export type SessionMemoryView = SessionMemoryStatus & Partial<Pick<DeploymentSessionMemoryStatus, "planes">>;
257
262
  /**
258
263
  * [ref] seam①([ref] §S-7,core 7.0.2 [ref] 件1)—— `GET /v1/sessions/:id/memory-status` 的引擎面。
259
264
  * 命名照 CLAUDE.md 工厂律:返回带行为的活对象 ⇒ `create*`。
@@ -2,10 +2,11 @@
2
2
  * 记忆作用域店 —— 本部署的记忆在本宿主的**唯一持有者**:「一层住在哪只店」与「本部署有哪些店」都只在这里答。
3
3
  *
4
4
  * ## 规则(一条)
5
- * 一层(scope)的平面只有引擎的纯分类函数答(`scopePlanesOf`,契约 = 本服务铸 spec 时盖在会话上的同一只判据 {@link memoryScopeContractOf});
5
+ * 一层(scope)的平面只有引擎答(`memoryScopePlaneOf`,契约 = 本服务铸 spec 时盖在会话上的同一只判据 {@link memoryScopeContractOf});
6
6
  * 一层的店 = 它那只平面的店(项目平面 = 部署的店;目录钉住的部署上个人平面是个人挂载上的另一只店,引擎的 `resolveMemoryScopeStore` 开它)。
7
- * **本部署有哪些店**只有引擎的部署对象答(`createMemoryDeployment(…).stores()`):项目店,再加上个人挂载**在盘上**的那只个人店 ——
8
- * 盘的集合,不是配置点名的层推出来的集合(配置只决定会话往哪写;换了 `MEMORY_SCOPE` 之后个人挂载上留下的那只店照样在集合里)。
7
+ * **本部署有哪些店**只有引擎的部署对象答(`createMemoryDeployment(…).stores()`,契约 `memory.deployment.store_set`):项目店,再加上个人挂载上
8
+ * **存有记录**的那只个人店 —— 盘的集合,不是配置点名的层推出来的集合(配置只决定会话往哪写;换了 `MEMORY_SCOPE` 之后个人挂载上写过的那只店照样在集合里),
9
+ * 也不是「开过」的集合(打开 / 读 / 被拒的请求只留下引擎具名的工件,不让店进集;每次从盘上读,每只部署对象答同一个集合)。
9
10
  * 本服务在会话之外读写某一层的每一条腿(同步执行器、项目层折叠、中心侧导出 / 同步、bundle 导出导入、合规抹除 / 出处、外源标记、会话记忆状态、整理)
10
11
  * 一律问**本对象**。本对象就是起服装配出来的那只记忆引擎对象(`boot/stores.ts` 的 `memoryEngine`、`run-local.ts` 的同名量):它的 `backend` / `root`
11
12
  * 正是交给 Runner 的两件事实(`RunnerDeps.memoryBackend` / `RunnerDeps.memoryEngineDir`)—— 宿主腿与会话对同一层答同一只店。每进程一只,所有腿共用。
@@ -18,7 +19,7 @@
18
19
  *
19
20
  * ## 请求的店({@link memoryStoreForRequest})
20
21
  * 点名层的动词按点名的层取店;点名的几层不在同一只店 ⇒ 响亮拒(一次请求只落一只店)。不点名层的条目 id / 店级账本动词答在**本部署唯一的那只店**上;
21
- * 部署有两只店(盘上有个人店)⇒ 不猜,响亮拒并指路「在请求里点名层」。没有部署对象的部署(SQL 记忆孪生,不钉目录 ⇒ 两个平面同一只店)恒是一只店。
22
+ * 部署有两只店(个人挂载上的店存有记录)⇒ 不猜,响亮拒并指路「在请求里点名层」。没有部署对象的部署(SQL 记忆孪生,不钉目录 ⇒ 两个平面同一只店)恒是一只店。
22
23
  */
23
24
  import { type MemoryBackend, type MemoryDeployment } from "@sema-agent/core";
24
25
  import type { SessionCaptureRecordSource } from "./plugins/store-backend.js";
@@ -29,7 +30,7 @@ export interface MemoryScopeStores {
29
30
  readonly root: string;
30
31
  /** capture opt-out 记录源(起服取一次的那一只;缺席 ⇒ 引擎各自的文件缺省)。本部署每一只记忆引擎 —— 部署对象、各操作面按店构造的引擎 —— 都只读这一格。 */
31
32
  readonly captureRecords: SessionCaptureRecordSource | undefined;
32
- /** 层 `layer` 的平面(引擎的规范化 + 纯分类函数,与引擎的店解析同一读法;零建店、零读盘)。引擎读不出的层键 ⇒ 抛引擎的 `config.memory_scope_key`,原样。 */
33
+ /** 层 `layer` 的平面(引擎的 `memoryScopePlaneOf`,与引擎的店解析同一只;零建店、零读盘)。引擎读不出的层键 ⇒ 抛引擎的 `config.memory_scope_key`,原样。 */
33
34
  planeOf(layer: string): "project" | "personal";
34
35
  /** 层 `layer` 住的那只店(先判平面、每个平面一只实例)。读不出的层键 ⇒ 同 {@link MemoryScopeStores.planeOf}。 */
35
36
  storeOf(layer: string): MemoryBackend;
@@ -37,7 +38,8 @@ export interface MemoryScopeStores {
37
38
  deployment(): MemoryDeployment | undefined;
38
39
  }
39
40
  /**
40
- * 控制面归属判据 —— 记忆操作面(合规 / 外源标记 / 会话记忆状态 / consolidation 阀门的 D1 拒启门)与部署对象共用的**同一只**谓词:
41
+ * 控制面归属判据 —— 记忆操作面(合规 / 外源标记 / 会话记忆状态)与部署对象共用的**同一只**谓词(这些部署面问「后端发不发布控制面」;
42
+ * 整理阀门问的是另一件事 —— 整理状态随不随后端走 —— 只问引擎的 `memoryGovernanceDurability`,见 `boot/memory-consolidation.ts`):
41
43
  * backend 的面上带**非空** `controlPlaneRoot` 才算归属在案。空串与缺席同判「不归属」:core 对空串会 truthy-回退到本副本盘上派生控制面
42
44
  * (engine 构造里的 `backendControl ? … : derive(…)`),对无状态多副本部署这正是 D1 要拒的那台机器(折叠账随 pod 一起死)。
43
45
  */
@@ -63,7 +65,7 @@ export declare class MemoryLayerRefusal extends Error {
63
65
  /**
64
66
  * 一次请求的记忆店。`layers` = 请求点名的层(空 = 没点名):
65
67
  * · 点名 ⇒ 它们共住的那一只店;分住两只 ⇒ 拒(一次请求只落一只店,按店拆成几次请求)。
66
- * · 没点名 ⇒ 本部署唯一的那只店(= 部署的店);部署有两只店(盘上有个人店,{@link MemoryScopeStores.deployment})⇒ 拒,指路在请求里点名层。
68
+ * · 没点名 ⇒ 本部署唯一的那只店(= 部署的店);部署有两只店(个人挂载上的店存有记录,{@link MemoryScopeStores.deployment})⇒ 拒,指路在请求里点名层。
67
69
  * `seat` = 这个动词点名层的那一处(拒句里指路用,如 `` `scope` ``;缺省 = 层座 `scope`)。
68
70
  * 拒一律 {@link MemoryLayerRefusal}(读不出的层键同码,引擎原句随句)。
69
71
  */
@@ -1,4 +1,4 @@
1
- import { createMemoryDeployment, normalizeMemorySpec, resolveMemoryScopeStore, scopePlanesOf } from "@sema-agent/core";
1
+ import { createMemoryDeployment, memoryScopePlaneOf, resolveMemoryScopeStore } from "@sema-agent/core";
2
2
  import { memoryScopeContractOf } from "./memory-scope.js";
3
3
  export function backendOwnsControlPlane(backend) {
4
4
  return controlPlaneRootOf(backend) !== undefined;
@@ -7,6 +7,7 @@ export function controlPlaneRootOf(backend) {
7
7
  const root = backend?.controlPlaneRoot;
8
8
  return typeof root === "string" && root !== "" ? root : undefined;
9
9
  }
10
+ const PLANE_FACE = "memoryScopeStores.planeOf";
10
11
  export function createMemoryScopeStores(input) {
11
12
  let personal;
12
13
  const resolve = (layer) => resolveMemoryScopeStore({
@@ -16,19 +17,7 @@ export function createMemoryScopeStores(input) {
16
17
  memoryEngineDir: input.root,
17
18
  ...(input.onIncident !== undefined ? { onIncident: input.onIncident } : {}),
18
19
  });
19
- const planeOf = (layer) => {
20
- const contract = memoryScopeContractOf([layer]);
21
- let spec;
22
- try {
23
- spec = normalizeMemorySpec({ scopes: [layer], writeScope: layer, ...(contract !== undefined ? { scopeContract: contract } : {}) });
24
- }
25
- catch {
26
- spec = undefined;
27
- }
28
- if (spec === undefined || spec.writeScope === null)
29
- return resolve(layer).plane;
30
- return scopePlanesOf(spec.scopes, spec.writeScope, spec.scopeContract)?.writePlane ?? "project";
31
- };
20
+ const planeOf = (layer) => memoryScopePlaneOf(PLANE_FACE, layer, memoryScopeContractOf([layer]));
32
21
  let deployment;
33
22
  return {
34
23
  backend: input.backend,
@@ -173,6 +173,42 @@ export declare function memoryEngineRootFor(config: Pick<ServiceConfig, "memoryE
173
173
  * 所以它的根必须走这条链而不是 `memoryEngineRootFor` 的分派(codex 修复验证轮 [medium]:分派会把 run-local 的
174
174
  * 记忆搬到 `<localDataRoot>/memory-work`,存量记忆与控制面升级后隐形)。 */
175
175
  export declare function fileMemoryEngineRoot(config: Pick<ServiceConfig, "memoryEngineDir">, fallbackRoot?: string): string;
176
+ /**
177
+ * 一条路径的**规范形**(给 {@link judgeMemoryRootOutsideDataRoot} 的 `canonical` 座用的生产实现):
178
+ * · 既存的绝对路径 ⇒ **原样**交给 `realpath`(`.native`:大小写不敏感的文件系统上返回盘上的拼写;符号链接、macOS 的 `/var` → `/private/var` 在这一步展平)。
179
+ * 不先在字串上折 `..`:操作系统在展开符号链接**之后**才折 `..`,`/link/..` 的真身是链接目标的父目录,字串折叠会把它读成 `/`;
180
+ * · 其余(尚不存在 / 相对)⇒ 绝对化并折叠 → 取**最深既存祖先**的 `realpath` → 接回尚不存在的尾巴(尾巴只做字串规范化)。
181
+ *
182
+ * 为什么按「最深既存祖先」而不是直接 `realpath(p)`:boot 期记忆根常常还没建出来(新数据根),`realpath` 对不存在的路径抛 ENOENT,
183
+ * 而数据根那一侧已经存在、会被展平 —— 两侧拼写空间不一样,同一个目录会被读成「不在其下」。祖先用 `existsSync` 找(它对 ENOENT / ENOTDIR / EACCES / ELOOP
184
+ * 一律答 `false`,不抛),所以走到的那一级一定 stat 得动;`realpath` 仍可能在「探完到取值之间目录被挪走 / 搜索权限被收」的竞态里抛 ——
185
+ * 不在本函数里吞(吞了就是一条无声的降级臂),由 boot 调用点接住并响亮报一行(`memory_engine_dir_check_unavailable`),起服照常。
186
+ *
187
+ * 取舍:尾巴(不存在的那几段)只比规范化字串,**不折大小写**,`..` 也按字串折:两个只差大小写、且都还不存在的拼写在大小写不敏感的盘上会被读成两处 ——
188
+ * 方向是多报一条 warn(这条判据的失败方向),不会漏报。
189
+ */
190
+ export declare function canonicalPathOf(p: string): string;
191
+ /** {@link judgeMemoryRootOutsideDataRoot} 的读数:记忆引擎的根落在本机数据根**之外**。三键即 boot 日志行的字段(`hint` 是一句带两条真路径的人话)。 */
192
+ export type MemoryRootOutsideDataRoot = {
193
+ readonly root: string;
194
+ readonly dataRoot: string;
195
+ readonly hint: string;
196
+ };
197
+ /**
198
+ * 【判官】file 形记忆引擎的**已解析根**是不是落在本机数据根(`config.localDataRoot`)**之外** —— 是 ⇒ 回两条真路径(boot 打一条 warn),不是 ⇒ `undefined`。
199
+ *
200
+ * 判据只有这一条**路径包含**:`path.relative(数据根, 记忆根)` 不以 `..` 段开头、也不是绝对路径(跨盘符)⇒ 在其下(相等也算在其下)。**与 env 是否显式无关**:
201
+ * `MEMORY_ENGINE_DIR` 缺席不是判据(此前判据是「`LOCAL_DATA_ROOT` 在 ∧ `MEMORY_ENGINE_DIR` 缺席」,于是 `AGENT_DATA_DIR` 已把两者放在同一处时,
202
+ * 「正在共享全机默认库」一句不真,而显式 `MEMORY_ENGINE_DIR` 指到数据根之外的真分家反而不报)。数据根缺席 ⇒ 无从比较,不报。
203
+ *
204
+ * 纯函数:不读 env、不读盘 —— 规范化走 `canonical` 座(缺省 `path.resolve`,字串规范形;boot 递 {@link canonicalPathOf} 取符号链接 / 大小写展平后的形)。
205
+ * 读数里的两条路径是**调用方给的原样拼写**(运维认得出来的那两条),不是规范形。
206
+ */
207
+ export declare function judgeMemoryRootOutsideDataRoot(a: {
208
+ readonly memoryRoot: string;
209
+ readonly dataRoot: string | undefined;
210
+ readonly canonical?: (p: string) => string;
211
+ }): MemoryRootOutsideDataRoot | undefined;
176
212
  /** 本部署 file 记忆引擎的**平面三坐标**(根 / 记忆目录 / 控制面目录)—— {@link fileMemoryPlaneFor} 一次算齐。 */
177
213
  export interface FileMemoryPlane {
178
214
  readonly root: string;