@sema-agent/server 7.43.0 → 7.44.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 (345) hide show
  1. package/USAGE.md +18 -0
  2. package/dist/adoption/plan.js +31 -192
  3. package/dist/adoption/quiesce.js +3 -86
  4. package/dist/adoption/runner.js +3 -140
  5. package/dist/adoption/sql.js +0 -74
  6. package/dist/adoption/wire.js +0 -80
  7. package/dist/approval-ask-machine.js +0 -75
  8. package/dist/approval-card.js +0 -323
  9. package/dist/approval-deny-reasons.js +0 -42
  10. package/dist/approval-hmac.js +0 -35
  11. package/dist/approval-reconciler.js +8 -166
  12. package/dist/approval.js +4 -67
  13. package/dist/audit.js +1 -44
  14. package/dist/auth-bridge.js +4 -47
  15. package/dist/auth-keys.js +0 -23
  16. package/dist/bake-runner/main.js +4 -65
  17. package/dist/bake-runner/protocol.js +3 -58
  18. package/dist/bake-runner/runner.js +5 -91
  19. package/dist/bench/l8/artifact.js +2 -61
  20. package/dist/bench/l8/escape.js +0 -25
  21. package/dist/bench/l8/index.js +0 -14
  22. package/dist/bench/l8/probes.js +0 -60
  23. package/dist/bench/l8/run-probes.js +2 -45
  24. package/dist/bench/s1/arms.js +14 -189
  25. package/dist/bench/s1/live-deps.js +11 -253
  26. package/dist/bench/s1/oracle.js +1 -36
  27. package/dist/bench/s1/repair-oracle-adapter.js +0 -30
  28. package/dist/bench/s1/reviewer.js +0 -34
  29. package/dist/bench/s1/row.js +0 -62
  30. package/dist/bench/s1/run-firm.js +4 -70
  31. package/dist/bench/s1/runner-ctx.js +0 -40
  32. package/dist/bench/s1/tasks.js +0 -115
  33. package/dist/boot/adoption.js +0 -20
  34. package/dist/boot/budget-tracing.js +3 -55
  35. package/dist/boot/config-center.js +43 -551
  36. package/dist/boot/coordinators.js +1 -69
  37. package/dist/boot/crash-last.js +0 -18
  38. package/dist/boot/deferred-sandbox-path-env.js +3 -119
  39. package/dist/boot/execution-env.js +5 -171
  40. package/dist/boot/governance-seams.js +0 -92
  41. package/dist/boot/leader.js +0 -69
  42. package/dist/boot/limit-sync.js +0 -2
  43. package/dist/boot/memory-boundary.js +3 -91
  44. package/dist/boot/org-memory.js +1 -25
  45. package/dist/boot/parked-revive-gate.js +0 -150
  46. package/dist/boot/permission-rules-audit.js +1 -56
  47. package/dist/boot/reapers.js +15 -273
  48. package/dist/boot/resolve-spec.js +9 -768
  49. package/dist/boot/retention-lane.js +4 -87
  50. package/dist/boot/runner-deps.js +3 -297
  51. package/dist/boot/runtime-caps.js +0 -40
  52. package/dist/boot/session-faces.js +2 -133
  53. package/dist/boot/shutdown.js +9 -83
  54. package/dist/boot/side-query-lane.js +2 -137
  55. package/dist/boot/stores.js +15 -215
  56. package/dist/boot/task-list-lane.js +0 -18
  57. package/dist/boot/webfetch-summarize-lane.js +0 -47
  58. package/dist/boot/workflow-orchestration.js +3 -99
  59. package/dist/boot-reclaim.js +0 -20
  60. package/dist/bounded-session-map.js +0 -19
  61. package/dist/brain.js +2 -139
  62. package/dist/budget.js +9 -244
  63. package/dist/capabilities/builtin-tools.js +0 -3
  64. package/dist/capabilities/center-plugins.js +6 -51
  65. package/dist/capabilities/center-prompts.js +6 -71
  66. package/dist/capabilities/code-review-council.js +7 -40
  67. package/dist/capabilities/collab-workflows.js +1 -44
  68. package/dist/capabilities/hands-lane.js +0 -65
  69. package/dist/capabilities/memory-notice.js +0 -70
  70. package/dist/capabilities/prompt.js +0 -14
  71. package/dist/capabilities/prompts/code-review.js +0 -14
  72. package/dist/capabilities/prompts/identity.js +0 -6
  73. package/dist/capabilities/prompts/team.js +0 -4
  74. package/dist/capabilities/repo-tools.js +0 -34
  75. package/dist/capabilities/sandbox-file-send.js +3 -92
  76. package/dist/capabilities/scenarios.d.ts +0 -1
  77. package/dist/capabilities/scenarios.js +18 -279
  78. package/dist/capabilities/select-environment-tool.js +0 -32
  79. package/dist/capabilities/send-user-file-tool.js +2 -66
  80. package/dist/capabilities/skills.d.ts +1 -2
  81. package/dist/capabilities/skills.js +3 -21
  82. package/dist/capabilities/team.d.ts +3 -11
  83. package/dist/capabilities/team.js +3 -51
  84. package/dist/capabilities/tool-defer.js +0 -3
  85. package/dist/config-center/apply-effective.js +15 -330
  86. package/dist/config-center/apply-ledger.js +5 -38
  87. package/dist/config-center/facade.js +0 -41
  88. package/dist/config-center/hot-keys-registry.js +0 -20
  89. package/dist/config-center/http-client.js +2 -125
  90. package/dist/config-center/mcp-revocation.js +2 -34
  91. package/dist/config-center/read-face.js +0 -59
  92. package/dist/config-center/restart-signal.js +1 -85
  93. package/dist/config-center/skills-mcp.d.ts +1 -1
  94. package/dist/config-center/skills-mcp.js +9 -76
  95. package/dist/config-center/stage-limits.js +8 -35
  96. package/dist/config-invariants.js +0 -16
  97. package/dist/config-lkg.js +0 -42
  98. package/dist/config-provider.js +3 -186
  99. package/dist/config-types.js +0 -5
  100. package/dist/config.js +107 -1145
  101. package/dist/degenerate-instrument.js +3 -67
  102. package/dist/deployment-governance.js +0 -124
  103. package/dist/digest-form.js +0 -12
  104. package/dist/elicitation.js +3 -86
  105. package/dist/env-facts.js +7 -75
  106. package/dist/fleet/fleet-bus.js +34 -507
  107. package/dist/fleet/fleet-reconciler.js +9 -149
  108. package/dist/fleet/fleet-terminal-window.js +10 -178
  109. package/dist/fleet/subagent-tail-bus.js +3 -72
  110. package/dist/fleet-client.js +10 -70
  111. package/dist/fleet-lease.js +5 -79
  112. package/dist/git-api-kind.js +0 -3
  113. package/dist/governance-ask-marks.js +2 -78
  114. package/dist/hooks/branch-transcript.js +0 -74
  115. package/dist/hooks/cc-agent-hook-prompt.js +0 -29
  116. package/dist/hooks/cc-stop-prompt.js +1 -46
  117. package/dist/hooks/hook-llm.js +1 -53
  118. package/dist/hooks/hook-runner.js +20 -414
  119. package/dist/http/active-run-conflict.js +4 -129
  120. package/dist/http/cursor-fingerprint.d.ts +5 -0
  121. package/dist/http/cursor-fingerprint.js +5 -0
  122. package/dist/http/idempotency.js +0 -37
  123. package/dist/http/principal-gate.js +3 -40
  124. package/dist/http/route-ctx.js +0 -9
  125. package/dist/http/routes/a2a-serve.js +7 -319
  126. package/dist/http/routes/admin-config-refresh.js +0 -4
  127. package/dist/http/routes/admin-drain.js +0 -10
  128. package/dist/http/routes/adoption.js +1 -29
  129. package/dist/http/routes/agents-roster.js +1 -47
  130. package/dist/http/routes/approvals-assistant.js +32 -395
  131. package/dist/http/routes/attachments.js +4 -22
  132. package/dist/http/routes/capabilities.js +3 -420
  133. package/dist/http/routes/diagnostics.js +1 -81
  134. package/dist/http/routes/fleet.js +9 -185
  135. package/dist/http/routes/images.js +14 -239
  136. package/dist/http/routes/leader.js +0 -13
  137. package/dist/http/routes/memory-bundle.js +1 -61
  138. package/dist/http/routes/memory-policy.js +9 -99
  139. package/dist/http/routes/notify-wake.js +3 -37
  140. package/dist/http/routes/observability.js +2 -19
  141. package/dist/http/routes/retention-ops.js +2 -34
  142. package/dist/http/routes/rules.js +1 -93
  143. package/dist/http/routes/runs.js +64 -873
  144. package/dist/http/routes/session-sync.js +19 -258
  145. package/dist/http/routes/sessions-list.js +17 -43
  146. package/dist/http/routes/sessions.js +34 -226
  147. package/dist/http/routes/shared-memory.js +5 -36
  148. package/dist/http/routes/side-query.js +1 -87
  149. package/dist/http/routes/tasks.js +52 -722
  150. package/dist/http/routes/trace-usage.js +32 -195
  151. package/dist/http/routes/workflows.js +19 -195
  152. package/dist/http/run-meta.js +0 -6
  153. package/dist/http/send.js +0 -32
  154. package/dist/http/server.js +92 -1597
  155. package/dist/http/sse-lifecycle.js +2 -13
  156. package/dist/http/sse-log.js +3 -48
  157. package/dist/http/tar.js +5 -21
  158. package/dist/http/verify-rounds.js +0 -5
  159. package/dist/http/wire-gate.js +0 -9
  160. package/dist/http/workspace-content.js +0 -10
  161. package/dist/images/bake-validate.js +1 -70
  162. package/dist/images/manifest.js +1 -6
  163. package/dist/index.js +0 -21
  164. package/dist/key-resolver.js +2 -17
  165. package/dist/leader/diffout.js +1 -20
  166. package/dist/leader/diffup.js +0 -47
  167. package/dist/leader/endpoint.js +2 -57
  168. package/dist/leader/fanout.js +3 -45
  169. package/dist/leader/grader-env-factory.js +3 -72
  170. package/dist/leader/leader.js +5 -156
  171. package/dist/leader/merge.js +8 -115
  172. package/dist/leader/planner.js +3 -54
  173. package/dist/leader/repair-oracle.js +1 -60
  174. package/dist/leader/repair-wire.js +2 -79
  175. package/dist/leader/wire.js +8 -307
  176. package/dist/lsp/e2b-bridge.js +4 -64
  177. package/dist/lsp/e2b-manager.js +6 -94
  178. package/dist/lsp/lsp-frames.js +0 -12
  179. package/dist/lsp/manager.js +4 -96
  180. package/dist/lsp/ws-transport.js +5 -55
  181. package/dist/lsp-evict.js +1 -15
  182. package/dist/main.js +57 -765
  183. package/dist/memory-bundle-engine.js +0 -55
  184. package/dist/memory-export.js +0 -4
  185. package/dist/memory-posture.js +1 -15
  186. package/dist/memory-scope.js +11 -148
  187. package/dist/memory-sync-client.js +2 -44
  188. package/dist/memory-sync.js +1 -80
  189. package/dist/model-select.js +3 -80
  190. package/dist/observability/cost-quota.js +1 -17
  191. package/dist/observability/cost-taxonomy.js +0 -34
  192. package/dist/observability/fail-open.js +7 -86
  193. package/dist/observability/logger.js +0 -6
  194. package/dist/observability/metrics.js +0 -94
  195. package/dist/observability/otel-exporter.js +3 -13
  196. package/dist/observability/principal-context.js +0 -9
  197. package/dist/observability/prompt-manifest.js +1 -37
  198. package/dist/observability/rate-limit.js +0 -4
  199. package/dist/observability/secret-env-scrub.js +2 -56
  200. package/dist/observability/tool-trace.js +1 -70
  201. package/dist/orchestration/hardened-vm-runner.js +4 -118
  202. package/dist/orchestration/hardened-vm-worker-runner.js +1 -26
  203. package/dist/orchestration/hardened-vm-worker.js +0 -27
  204. package/dist/orchestration/subagent-steer.js +1 -45
  205. package/dist/orchestration/workflow-agent-steer.js +1 -80
  206. package/dist/orchestration/workflow-completion-inbox.js +32 -285
  207. package/dist/orchestration/workflow-notify-journal.js +16 -259
  208. package/dist/org-memory-admission.js +3 -47
  209. package/dist/parent-watch.js +2 -48
  210. package/dist/parked-decide.js +1 -109
  211. package/dist/per-task-image.js +0 -57
  212. package/dist/plan-cache-probe.js +3 -27
  213. package/dist/plugins/adoption-log-sql.js +2 -119
  214. package/dist/plugins/approval-ask-store-memory.js +3 -38
  215. package/dist/plugins/approval-ask-store-sql.js +6 -188
  216. package/dist/plugins/approval-exemption-store.js +2 -28
  217. package/dist/plugins/background-agent-store-sql.js +4 -105
  218. package/dist/plugins/background-shell-support.js +14 -122
  219. package/dist/plugins/blob-backend.js +6 -169
  220. package/dist/plugins/breaker-state-sql.js +8 -46
  221. package/dist/plugins/caching-session-store.js +4 -106
  222. package/dist/plugins/checkpoint-store-sql.js +18 -505
  223. package/dist/plugins/e2b-orphan-reclaim.js +0 -45
  224. package/dist/plugins/file-outcome-sink.js +0 -9
  225. package/dist/plugins/file-resume-anchor-store.js +4 -43
  226. package/dist/plugins/file-run-store.js +26 -364
  227. package/dist/plugins/file-snapshot-store-sql.js +10 -181
  228. package/dist/plugins/fork-routing-session-store.js +8 -111
  229. package/dist/plugins/host-platform.js +2 -91
  230. package/dist/plugins/image-bake-store-sql.js +7 -250
  231. package/dist/plugins/image-index-sql.js +4 -123
  232. package/dist/plugins/k8s-bg-scripts.js +4 -88
  233. package/dist/plugins/k8s-exec-protocol.js +0 -41
  234. package/dist/plugins/leader-run-store-sql.js +0 -101
  235. package/dist/plugins/local-checkpoint-store.js +8 -128
  236. package/dist/plugins/local-session-store.js +34 -296
  237. package/dist/plugins/local-task-attachment-store.js +2 -16
  238. package/dist/plugins/mailbox-store-sql.js +8 -65
  239. package/dist/plugins/memory-embedder-fingerprint.js +5 -166
  240. package/dist/plugins/memory-embedder.js +4 -70
  241. package/dist/plugins/memory-engine-pg.js +6 -170
  242. package/dist/plugins/memory-engine-tidb.js +7 -155
  243. package/dist/plugins/memory-engine-vector-util.js +0 -10
  244. package/dist/plugins/memory-key-guards.js +0 -34
  245. package/dist/plugins/memory-origin-law.js +0 -187
  246. package/dist/plugins/memory-resume-anchor-store.js +0 -17
  247. package/dist/plugins/memory-run-store.js +12 -89
  248. package/dist/plugins/memory-session-policy-store.js +0 -17
  249. package/dist/plugins/memory-sync-store-pg.js +4 -49
  250. package/dist/plugins/memory-sync-store-tidb.js +3 -35
  251. package/dist/plugins/outcome-ledger-sql.js +3 -97
  252. package/dist/plugins/permission-rule-store-file.js +5 -133
  253. package/dist/plugins/permission-rule-store-sql.d.ts +3 -0
  254. package/dist/plugins/permission-rule-store-sql.js +50 -307
  255. package/dist/plugins/pg-cost-quota.js +0 -7
  256. package/dist/plugins/pg-pool.js +0 -92
  257. package/dist/plugins/pg-rate-limiter.js +2 -13
  258. package/dist/plugins/pg-safe-json.js +4 -40
  259. package/dist/plugins/pg-session-storage.js +25 -189
  260. package/dist/plugins/posix-shell-fs.js +1 -31
  261. package/dist/plugins/remote-env-adb.js +12 -101
  262. package/dist/plugins/remote-env-e2b.js +40 -371
  263. package/dist/plugins/remote-env-file-error.js +0 -37
  264. package/dist/plugins/remote-env-host.js +63 -483
  265. package/dist/plugins/remote-env-k8s.js +32 -326
  266. package/dist/plugins/remote-env-local-docker.js +24 -157
  267. package/dist/plugins/remote-env-ssh.js +20 -128
  268. package/dist/plugins/remote-scratchpad.js +2 -32
  269. package/dist/plugins/remote-shell.js +1 -32
  270. package/dist/plugins/resume-anchor-store-sql.js +0 -11
  271. package/dist/plugins/retention-lane-store-sql.js +0 -108
  272. package/dist/plugins/retention-store-sql.js +4 -383
  273. package/dist/plugins/roster-store-sql.js +0 -55
  274. package/dist/plugins/run-store-sql.js +14 -251
  275. package/dist/plugins/s3-presign.js +2 -49
  276. package/dist/plugins/scheduler-support.js +3 -80
  277. package/dist/plugins/send-file-ledger.js +4 -53
  278. package/dist/plugins/send-user-file.js +4 -94
  279. package/dist/plugins/session-placement.js +1 -89
  280. package/dist/plugins/session-policy-store-sql.js +3 -81
  281. package/dist/plugins/session-store.js +0 -57
  282. package/dist/plugins/shared-memory-store-sql.js +4 -167
  283. package/dist/plugins/sql-driver.js +0 -17
  284. package/dist/plugins/sql-errors.js +0 -7
  285. package/dist/plugins/sql-escape.js +0 -8
  286. package/dist/plugins/sql-row-helpers.js +0 -25
  287. package/dist/plugins/store-backend.js +42 -222
  288. package/dist/plugins/store-contracts.js +2 -46
  289. package/dist/plugins/task-attachment-store.js +3 -49
  290. package/dist/plugins/task-list-store-sql.js +0 -79
  291. package/dist/plugins/tidb-cost-quota.js +1 -4
  292. package/dist/plugins/tidb-pool.js +1 -207
  293. package/dist/plugins/tidb-rate-limiter.js +3 -9
  294. package/dist/plugins/tidb-session-storage.js +4 -70
  295. package/dist/plugins/tidb-session-store.js +19 -341
  296. package/dist/plugins/tool-result-store-sql.js +4 -179
  297. package/dist/plugins/usage-window-store-sql.js +0 -8
  298. package/dist/plugins/web-search.js +10 -132
  299. package/dist/plugins/workflow-journal-store-sql.js +2 -58
  300. package/dist/plugins/workflow-run-store-sql.js +6 -91
  301. package/dist/plugins/worktree-isolation.js +6 -126
  302. package/dist/plugins/write-behind-counter.js +16 -75
  303. package/dist/principal-jwt.js +5 -60
  304. package/dist/project-memory.js +15 -146
  305. package/dist/prompts-domain-validate.js +1 -51
  306. package/dist/question.js +2 -111
  307. package/dist/resource-suspend.js +0 -18
  308. package/dist/router/route-orchestration.js +0 -77
  309. package/dist/rules-consent.d.ts +44 -5
  310. package/dist/rules-consent.js +75 -215
  311. package/dist/run-local.js +14 -381
  312. package/dist/runs.js +29 -617
  313. package/dist/runtime-caps-resolver.js +6 -132
  314. package/dist/runtime-governance.js +1 -232
  315. package/dist/sandbox-pkg-source.js +0 -37
  316. package/dist/sealed-key.js +3 -68
  317. package/dist/security.js +6 -291
  318. package/dist/session-leaf-bus.js +0 -32
  319. package/dist/session-sync-content.js +1 -69
  320. package/dist/session-sync-kernel.js +3 -60
  321. package/dist/session-sync.js +3 -66
  322. package/dist/session-titler.js +7 -41
  323. package/dist/session-watch.js +9 -73
  324. package/dist/shared-memory-scope-authorizer.js +0 -18
  325. package/dist/sighup-idle.js +1 -12
  326. package/dist/spec-fields.js +4 -143
  327. package/dist/store-live-probe.js +3 -49
  328. package/dist/task-a2a.js +1 -126
  329. package/dist/task-cwd.js +1 -103
  330. package/dist/task-mcp.js +1 -89
  331. package/dist/task-settings.js +10 -300
  332. package/dist/task-workflow.js +8 -75
  333. package/dist/tool-approval.d.ts +28 -1
  334. package/dist/tool-approval.js +70 -1430
  335. package/dist/trace/artifacts.js +5 -20
  336. package/dist/trace/engine-notice-wire.js +6 -138
  337. package/dist/trace/ledger-sink.js +7 -96
  338. package/dist/trace/project.js +4 -448
  339. package/dist/trace/redact.js +9 -77
  340. package/dist/turn-activity.js +1 -27
  341. package/dist/usage-analytics.js +4 -34
  342. package/dist/wall-clock-jump-guard.js +1 -68
  343. package/package.json +3 -3
  344. package/dist/capabilities/scenario-alias.d.ts +0 -27
  345. package/dist/capabilities/scenario-alias.js +0 -61
@@ -1,17 +1,3 @@
1
- /**
2
- * design/113 C4 — `RunnerDeps.loadProjectMemory` (core 1.182): the DEPLOYMENT half that makes sema "open like CC" —
3
- * inject the project's CLAUDE.md + recent git narrative as a read-only, fenced virtual memory layer at the tail of the
4
- * env block, so a fresh "你好" lands on "I'm the AI for the acme-widget project…" instead of a blank greeting.
5
- *
6
- * core's half (1.182/1.184): it calls this once per prepare, fences+sanitizes the return as UNTRUSTED data (CLAUDE.md
7
- * is data, not an instruction), caps it at MAX_MEMORY_BYTES, and (1.184) auto-prepends a TRUSTED framing line. We just
8
- * return the content.
9
- *
10
- * 🔴 HOST/FILE LANE ONLY. The ctx hands us only `cwd` (a string) — NO executionEnv instance and NO sessionId. So we can
11
- * read with the worker's own `fs`/`execFile` ONLY when cwd is a LOCAL path (the host lane). On a remote sandbox lane
12
- * (e2b/k8s/ssh/adb/local-docker) cwd lives in the container — the worker can't read it, and there's no env handle in
13
- * the ctx to exec through — so main.ts only wires this on the host lane; here we defensively re-check (stat the dir).
14
- */
15
1
  import { promises as fs, constants as fsConstants } from "node:fs";
16
2
  import { execFile } from "node:child_process";
17
3
  import { createHmac, randomBytes } from "node:crypto";
@@ -19,78 +5,43 @@ import { isAbsolute, join, sep } from "node:path";
19
5
  import { scrubSecretEnv } from "@sema-agent/core";
20
6
  import { recordSecretEnvScrub } from "./observability/secret-env-scrub.js";
21
7
  import { redactSecrets } from "./trace/redact.js";
22
- /** The instruction-file name chain (core 1.302 S-1): AGENTS.md wins, CLAUDE.md is the fallback, then the
23
- * `.claude/` nested form. The FIRST hit is the single declared instruction source. */
24
8
  const INSTRUCTION_FILE_CHAIN = ["AGENTS.md", "CLAUDE.md", ".claude/CLAUDE.md"];
25
- /** One byte budget shared by the loader AND the probe — the fingerprint is sha256 over the first
26
- * `INSTRUCTION_READ_CAP` raw bytes, so both sides MUST read the same bounded window or every probe
27
- * would report a phantom change. (Consequence: an edit past the cap is invisible to the probe —
28
- * honest limit of the bounded-read design, same reason the injected content is capped.) */
29
9
  const INSTRUCTION_READ_CAP = 24 * 1024;
30
- /** Cap a string to a byte budget on a UTF-8 character boundary (never split a multi-byte char). */
31
10
  function capUtf8(s, maxBytes) {
32
11
  const buf = Buffer.from(s, "utf8");
33
12
  if (buf.length <= maxBytes)
34
13
  return s;
35
14
  let end = maxBytes;
36
- // back off to a UTF-8 lead byte (0xxxxxxx or 11xxxxxx) so we don't slice mid-character
37
15
  while (end > 0 && (buf[end] & 0xc0) === 0x80)
38
16
  end -= 1;
39
17
  return buf.subarray(0, end).toString("utf8");
40
18
  }
41
- /** Bounded raw read of an ABSOLUTE path (lstat symlink gate → first `maxBytes` bytes). The shared primitive
42
- * under both the loader and `makeProbeInstructionSources` — one read posture, one fingerprint. */
43
19
  async function readRawCapped(target, maxBytes, truncatedFlag = { v: false }) {
44
20
  let fh;
45
21
  try {
46
- // Symlink containment: a `CLAUDE.md` symlink must NOT read an arbitrary file outside the repo (double-review #3).
47
- // 🔴 win32 真机(S6, 2026-07-04): `fsConstants.O_NOFOLLOW` is UNDEFINED on Windows Node, so `O_RDONLY | undefined`
48
- // silently collapses to `O_RDONLY` (0) — the flag is a no-op and the symlink IS followed (verified: read out the
49
- // outside file). O_NOFOLLOW only exists/atomically-enforces on POSIX. So gate portably with an explicit lstat
50
- // symlink-reject: authoritative on win32 (where the open-flag can't enforce it), belt-and-suspenders on POSIX.
51
22
  const lst = await fs.lstat(target);
52
23
  if (lst.isSymbolicLink())
53
- return null; // a symlinked CLAUDE.md → refuse (no arbitrary-file read), same as O_NOFOLLOW → ELOOP
24
+ return null;
54
25
  if (lst.size > maxBytes)
55
- truncatedFlag.v = true; // S23: cap fired — surfaced as claudeTruncated on the injection event
56
- // Keep O_NOFOLLOW in the flags too: on POSIX it closes the tiny lstat→open TOCTOU window atomically; on win32 it's
57
- // a harmless 0. BOUNDED read of the first maxBytes bytes — never buffer the whole file, so a 50MB/hostile CLAUDE.md
58
- // can't amplify memory (double-review #2: the old fs.readFile loaded the WHOLE file before capping).
26
+ truncatedFlag.v = true;
59
27
  fh = await fs.open(target, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0));
60
28
  const buf = Buffer.allocUnsafe(maxBytes);
61
29
  const { bytesRead } = await fh.read(buf, 0, maxBytes, 0);
62
30
  return buf.subarray(0, bytesRead);
63
31
  }
64
32
  catch {
65
- return null; // ENOENT / EISDIR / ELOOP (a symlink, rejected by O_NOFOLLOW) / EACCES — absent or unreadable → no content
33
+ return null;
66
34
  }
67
35
  finally {
68
36
  await fh?.close();
69
37
  }
70
38
  }
71
- /** Keyed fingerprint over the RAW bounded-read window (pre-capUtf8 bytes — the ①"cap 前原始字节" contract).
72
- * KEYED, not a plain digest (self-review, information-surface lens): core puts the short form of this
73
- * fingerprint into the MODEL-VISIBLE instructions-change notice and the steering trace — a plain sha256
74
- * of the raw bytes would let anyone holding a known template dictionary-compare low-entropy controlled
75
- * fields against the digest. A per-process random key makes the digest correspond to nothing outside
76
- * this process. Loader and probe share THIS one function (any split would fabricate phantom "changed"
77
- * signals); sources are re-declared at every prepare in the declaring process, so a restart (new key)
78
- * never gets compared against old fingerprints. */
79
39
  const INSTRUCTION_HASH_KEY = randomBytes(16);
80
40
  function instructionHash(raw) {
81
41
  return `hmac-sha256:${createHmac("sha256", INSTRUCTION_HASH_KEY).update(raw).digest("hex")}`;
82
42
  }
83
- /** The "declared absent, watched for appearance" fingerprint for higher-priority chain members —
84
- * core 1.310 ([1006]①) widened `instructionSources[].contentHash` to `string | null`, so this is a
85
- * plain typed null (an appearing file renders as `hash absent → <hash>` in the change notice). */
86
43
  const ABSENT_HASH = null;
87
- /** Recover (root, rel) for a path the LOADER declared — every declared path is `root + one chain member`,
88
- * so the chain suffix identifies the split unambiguously. The probe needs this to re-run the loader's
89
- * parent-directory symlink gate each round (self-review: the probe checked only the final component, so
90
- * swapping `.claude` for a symlink out of the repo MID-RUN made the probe fingerprint an outside file). */
91
44
  function chainCoordinates(absPath) {
92
- // LONGEST suffix wins: `…/.claude/CLAUDE.md` also ends with the bare `/CLAUDE.md` — matching in chain
93
- // order would split off `.claude` into the root and skip the parent gate entirely (caught by the test).
94
45
  let best = null;
95
46
  for (const rel of INSTRUCTION_FILE_CHAIN) {
96
47
  const suffix = sep + rel.split("/").join(sep);
@@ -100,11 +51,6 @@ function chainCoordinates(absPath) {
100
51
  }
101
52
  return best && { root: best.root, rel: best.rel };
102
53
  }
103
- /** Reject a symlink on ANY intermediate directory of `rel` under root (adversarial review F2): the final
104
- * component's lstat/O_NOFOLLOW can't see a `.claude` DIRECTORY that is itself a symlink out of the repo
105
- * (e.g. → ~/.claude) — the target file there lstat's as a plain file and would be read and injected.
106
- * Same TOCTOU honesty as the final-component gate: the lstat→open window on a swapped intermediate dir
107
- * is accepted (a local attacker with user rights can read the target directly anyway). */
108
54
  async function hasSymlinkedParent(root, rel) {
109
55
  const parts = rel.split("/").slice(0, -1);
110
56
  let cur = root;
@@ -116,65 +62,33 @@ async function hasSymlinkedParent(root, rel) {
116
62
  }
117
63
  return false;
118
64
  }
119
- /** Run a fixed git subcommand under root with execFile (argv array → NO shell → no injection), timeout + maxBuffer.
120
- * 🔴 The repo's own `.git/config` can otherwise STEER these read-only calls into code execution — double-review proved
121
- * `core.fsmonitor=<prog>` runs `<prog>` on `git status` (auto-fires on every prepare in a hostile working dir). We
122
- * neutralize the exec-config inlets: `-c core.fsmonitor=false -c core.hooksPath=/dev/null` override the repo's values,
123
- * and GIT_CONFIG_GLOBAL/SYSTEM=/dev/null drop the global/system config so a planted one can't inject either. */
124
65
  function git(root, args, maxBytes) {
125
66
  const safeArgs = ["-c", "core.fsmonitor=false", "-c", "core.hooksPath=/dev/null", ...args];
126
- // A-033.3(#256):带 `findings` 收集器 —— 见下方 env 行的注释,这条腿丢掉的是「仓内的钩子/凭证助手
127
- // 本来会看到的」那批 key,零留痕的话保护生效与否在遥测里读不出来。收集器不改变剥除行为。
128
67
  const scrubFindings = [];
129
68
  const scrubbedEnv = scrubSecretEnv(process.env, scrubFindings);
130
69
  recordSecretEnvScrub("server.project-memory.git-subprocess", scrubFindings);
131
70
  return new Promise((res) => {
132
- execFile("git", safeArgs,
133
- // scrubSecretEnv: a git hook / credential helper must not see the orchestrator's *_KEY/*_TOKEN/*_SECRET env.
134
- // GIT_CONFIG_GLOBAL/SYSTEM=/dev/null: ignore ~/.gitconfig + /etc/gitconfig so a planted exec-config can't steer us.
135
- { cwd: root, timeout: 5000, maxBuffer: maxBytes, env: { ...scrubbedEnv, GIT_CONFIG_GLOBAL: "/dev/null", GIT_CONFIG_SYSTEM: "/dev/null" } }, (err, stdout, stderr) => {
71
+ execFile("git", safeArgs, { cwd: root, timeout: 5000, maxBuffer: maxBytes, env: { ...scrubbedEnv, GIT_CONFIG_GLOBAL: "/dev/null", GIT_CONFIG_SYSTEM: "/dev/null" } }, (err, stdout, stderr) => {
136
72
  if (!err)
137
73
  return res({ out: stdout.trim() });
138
- // S23: the git narrative used to vanish with zero fingerprint — classify why (→ gitDropReason).
139
74
  const e = err;
140
75
  const fail = e.killed ? "timeout" : e.code === "ENOENT" ? "git-missing" : /not a git repository/i.test(String(stderr)) ? "non-repo" : /maxBuffer/i.test(String(e.message)) ? "maxbuffer" : "error";
141
76
  res({ out: "", fail });
142
77
  });
143
78
  });
144
79
  }
145
- /**
146
- * Build a `loadProjectMemory` for the host lane. Returns null (skip) unless hands are enabled AND cwd is a local
147
- * directory. Reads the instruction-file chain (AGENTS.md → CLAUDE.md → .claude/CLAUDE.md, S-1) + (non-subagent)
148
- * `git log -5` / `git status`, redacts secrets, and caps the total. A subagent gets a trimmed instruction file and
149
- * no git narrative (cost control, H7). When an instruction file is hit the return is a `ProjectMemoryLoad`
150
- * declaring it in `instructionSources` (path + sha256 over the raw bounded-read window) so core's
151
- * instructions-change lane can probe it mid-run.
152
- *
153
- * Session snapshot cache (S-2): keyed on ctx.sessionId — a session keeps ONE stable instruction snapshot
154
- * (mid-run file edits surface via the probe as tail attachments, never a silently re-rendered prefix).
155
- * `phase: "post-compact"` forces a re-read (core re-renders the prefix there anyway). No entry for
156
- * sessionless one-shot runs. (post-clear never arrives from core — RB-9 — so it is not a cache key here.)
157
- */
158
80
  export function makeLoadProjectMemory(opts) {
159
- const totalCap = opts.maxBytes ?? 32 * 1024; // service self-cap, tighter than core's 100KiB MAX_MEMORY_BYTES backstop
81
+ const totalCap = opts.maxBytes ?? 32 * 1024;
160
82
  const claudeCap = INSTRUCTION_READ_CAP;
161
83
  const subagentClaudeCap = 4 * 1024;
162
84
  const gitCap = 8 * 1024;
163
85
  const cache = new Map();
164
86
  const CACHE_MAX = opts.cacheMax ?? 256;
165
87
  return async (ctx) => {
166
- // All gates that decide WHETHER anything loads run BEFORE the cache (F3): a hands-less or bad-cwd call
167
- // must answer null itself, never serve (or seed) a session slot it wasn't allowed to read.
168
88
  if (!ctx.handsEnabled)
169
- return null; // hands-less chat: no real project (H4)
89
+ return null;
170
90
  if (!isAbsolute(ctx.cwd))
171
91
  return null;
172
- // Canonicalize through the FILESYSTEM, not lexically (self-review, information-surface lens): a
173
- // lexical resolve() keeps symlinks and case aliases distinct — a symlinked cwd re-pointed at another
174
- // project would keep hitting the OLD slot and serve project A's instruction snapshot inside project B,
175
- // and a case alias would silently split one project across two slots. realpath is both the cache key
176
- // AND the read root, so key and content can never disagree. Unresolvable cwd (gone/unreadable) → null,
177
- // the same answer doLoad's stat gate would give.
178
92
  let root;
179
93
  try {
180
94
  root = await fs.realpath(ctx.cwd);
@@ -182,26 +96,17 @@ export function makeLoadProjectMemory(opts) {
182
96
  catch {
183
97
  return null;
184
98
  }
185
- // Subagents share the parent's project but get a differently-capped view — never let the two shapes
186
- // poison one cache slot; the subagent leg just stays uncached (cheap, trimmed read).
187
99
  const cacheKey = ctx.sessionId && !ctx.isSubagent ? `${ctx.sessionId}\u0000${root}` : undefined;
188
100
  if (cacheKey) {
189
101
  const hit = cache.get(cacheKey);
190
102
  if (hit && ctx.phase !== "post-compact")
191
103
  return hit.load;
192
- // Post-compact single-flight (codex lens-1 remainder): two post-compact calls on one slot must share
193
- // ONE fresh read — each starting its own would leave the surviving snapshot dependent on launch
194
- // order. An in-flight entry that was ITSELF started post-compact is that generation's read (the
195
- // settle window is milliseconds; a next compaction landing inside it joins the same read — accepted).
196
104
  if (hit && !hit.settled && hit.phase === "post-compact")
197
105
  return hit.load;
198
106
  }
199
107
  const load = doLoad(ctx, root);
200
108
  if (cacheKey) {
201
109
  if (cache.size >= CACHE_MAX && !cache.has(cacheKey)) {
202
- // Evict the oldest SETTLED entry only (codex lens-1 remainder): deleting an in-flight promise
203
- // orphans its read (the key's next call starts a second one). No settled entry → allow a
204
- // temporary over-cap; loads settle in milliseconds, so the map stays bounded in practice.
205
110
  for (const [k, e] of cache) {
206
111
  if (e.settled) {
207
112
  cache.delete(k);
@@ -211,10 +116,6 @@ export function makeLoadProjectMemory(opts) {
211
116
  }
212
117
  const entry = { load, settled: false, phase: ctx.phase };
213
118
  cache.set(cacheKey, entry);
214
- // Release-review finding: a burst of >CACHE_MAX concurrent DISTINCT loads sails past the insert-time
215
- // eviction (nothing settled to evict yet) and, without this, the map would stay at the burst's
216
- // high-water mark forever (each later insert evicts exactly one settled entry, net zero). Trim back
217
- // down to the cap as loads settle — oldest settled first, in-flight entries always survive.
218
119
  const trimSettled = () => {
219
120
  if (cache.size <= CACHE_MAX)
220
121
  return;
@@ -231,9 +132,6 @@ export function makeLoadProjectMemory(opts) {
231
132
  trimSettled();
232
133
  }, () => {
233
134
  entry.settled = true;
234
- // A rejected load must not poison the slot (doLoad catches everything today; belt for future
235
- // edits). Guard the delete on identity (codex lens-1): an OLD generation's failure must not
236
- // evict the NEWER entry that a post-compact refresh has already installed in the same slot.
237
135
  if (cache.get(cacheKey) === entry)
238
136
  cache.delete(cacheKey);
239
137
  trimSettled();
@@ -244,12 +142,7 @@ export function makeLoadProjectMemory(opts) {
244
142
  async function doLoad(ctx, root) {
245
143
  const st = await fs.stat(root).catch(() => null);
246
144
  if (!st?.isDirectory())
247
- return null; // remote-lane cwd not present locally, or a bad path → skip
248
- // S-1 file-name chain: first hit wins and is the ONE declared instruction source.
249
- // Consistent-snapshot selection (codex lens-1 remainder): the chain walk is not atomic — a
250
- // higher-priority file created between "checked absent" and "picked the fallback" would silently
251
- // lose to the fallback until a post-compact reload. After a non-top pick, re-verify the members
252
- // above it; an appearance redoes the walk (bounded — after the retries the last pick stands).
145
+ return null;
253
146
  const claudeTrunc = { v: false };
254
147
  let claudeRaw = null;
255
148
  let hitFile;
@@ -264,7 +157,7 @@ export function makeLoadProjectMemory(opts) {
264
157
  for (let i = 0; i < INSTRUCTION_FILE_CHAIN.length; i++) {
265
158
  const rel = INSTRUCTION_FILE_CHAIN[i];
266
159
  if (rel.includes("/") && (await hasSymlinkedParent(root, rel)))
267
- continue; // F2: a symlinked .claude dir escapes the repo
160
+ continue;
268
161
  const raw = await readRawCapped(join(root, rel), claudeCap, claudeTrunc);
269
162
  if (raw !== null) {
270
163
  claudeRaw = capUtf8(raw.toString("utf8"), claudeCap);
@@ -275,11 +168,10 @@ export function makeLoadProjectMemory(opts) {
275
168
  }
276
169
  }
277
170
  if (hitIndex <= 0)
278
- break; // top pick or nothing found: no higher-priority member to re-verify
171
+ break;
279
172
  let appeared = false;
280
173
  for (const rel of INSTRUCTION_FILE_CHAIN.slice(0, hitIndex)) {
281
174
  const higher = await fs.lstat(join(root, rel)).catch(() => null);
282
- // A symlink would be REFUSED by the read gate — its appearance is not a better pick.
283
175
  if (higher && !higher.isSymbolicLink()) {
284
176
  appeared = true;
285
177
  break;
@@ -289,9 +181,9 @@ export function makeLoadProjectMemory(opts) {
289
181
  break;
290
182
  }
291
183
  const claude = ctx.isSubagent && claudeRaw ? capUtf8(claudeRaw, subagentClaudeCap) : claudeRaw;
292
- const claudeTruncated = claudeTrunc.v || Boolean(ctx.isSubagent && claudeRaw && Buffer.byteLength(claudeRaw, "utf8") > subagentClaudeCap); // S23
184
+ const claudeTruncated = claudeTrunc.v || Boolean(ctx.isSubagent && claudeRaw && Buffer.byteLength(claudeRaw, "utf8") > subagentClaudeCap);
293
185
  let gitNarrative = "";
294
- let gitOk = true; // S23: subagent (git not attempted) stays ok=absent-by-design
186
+ let gitOk = true;
295
187
  let gitDropReason;
296
188
  if (!ctx.isSubagent) {
297
189
  const [log, status] = await Promise.all([
@@ -304,33 +196,22 @@ export function makeLoadProjectMemory(opts) {
304
196
  if (log.out)
305
197
  parts.push(`## Recent commits\n${log.out}`);
306
198
  if (status.out)
307
- parts.push(`## Working tree\n${status.out.split("\n").slice(0, 40).join("\n")}`); // bound the dirty list
199
+ parts.push(`## Working tree\n${status.out.split("\n").slice(0, 40).join("\n")}`);
308
200
  gitNarrative = parts.join("\n\n");
309
201
  }
310
202
  const sections = [];
311
203
  if (claude && hitFile)
312
- sections.push(`# ${hitFile}\n${claude}`); // heading = the REAL file name (S-1)
204
+ sections.push(`# ${hitFile}\n${claude}`);
313
205
  if (gitNarrative)
314
206
  sections.push(gitNarrative);
315
207
  if (sections.length === 0)
316
208
  return null;
317
- // redact secrets BEFORE capping (a key must not survive into the injected context, where a tool could exfil it).
318
209
  const redacted = redactSecrets(sections.join("\n\n"));
319
210
  const capped = capUtf8(redacted, totalCap);
320
- // S23: gitOk/gitDropReason/claudeTruncated — a silently-emptied narrative now has a fingerprint here.
321
- // cacheSize: the snapshot cache's population at THIS load (bound observability — the settle-time trim
322
- // keeps it ≤ the cap once in-flight bursts settle; also what the deterministic trim test asserts).
323
211
  opts.logger?.debug?.("project_memory_loaded", { cwd: root, bytes: Buffer.byteLength(capped, "utf8"), isSubagent: ctx.isSubagent, gitOk, ...(gitDropReason ? { gitDropReason } : {}), claudeTruncated, ...(hitFile ? { instructionFile: hitFile } : {}), ...(ctx.phase ? { phase: ctx.phase } : {}), cacheSize: cache.size });
324
212
  if (!capped)
325
213
  return null;
326
- // Declare the instruction source (①): absolute path + fingerprint. The hash is over the PRE-redaction,
327
- // pre-cap raw window — the probe re-reads the file itself, so the fingerprint must be file-derived, not
328
- // derived from the redacted/assembled injection text.
329
214
  if (hitFile && hitHash) {
330
- // Watch the chain members ABOVE the pick too (codex lens-1 remainder): core's change lane only
331
- // probes DECLARED paths, so a higher-priority file appearing mid-run would otherwise never produce
332
- // a signal until a post-compact reload. Absent members are pre-declared with the runtime's
333
- // "absent" hash (ABSENT_HASH note) — appearance renders as `hash absent → <hash>` in the notice.
334
215
  return {
335
216
  content: capped,
336
217
  instructionSources: [
@@ -342,27 +223,15 @@ export function makeLoadProjectMemory(opts) {
342
223
  return capped;
343
224
  }
344
225
  }
345
- /**
346
- * ② The `RunnerDeps.probeInstructionSources` seam: re-fingerprint the declared instruction files mid-run so
347
- * core's instructions-change lane can append a tail attachment when one changes. Same read posture as the
348
- * loader (lstat symlink gate → bounded read of the first `INSTRUCTION_READ_CAP` bytes → sha256) — any drift
349
- * here would fabricate phantom "changed" signals every turn. Per-source failure → `contentHash: null`
350
- * (source gone/unreadable); any unexpected top-level failure → `null` (probe abstains; core also races this
351
- * against a 2s ceiling and treats a throw as null, but we stay honest on our side of the seam).
352
- */
353
226
  export function makeProbeInstructionSources() {
354
227
  return async (sources) => {
355
228
  try {
356
229
  return await Promise.all(sources.map(async ({ path }) => {
357
230
  if (!isAbsolute(path))
358
- return { path, contentHash: null }; // we only ever declare absolute paths
359
- // Re-run the loader's parent-directory symlink gate EVERY round (self-review: the probe checked
360
- // only the final component, so swapping `.claude` for a symlink out of the repo mid-run made the
361
- // probe fingerprint an outside file — and its short form reaches the model-visible notice).
362
- // Declared paths are always root+chain-member, so the coordinates are recoverable from the path.
231
+ return { path, contentHash: null };
363
232
  const coords = chainCoordinates(path);
364
233
  if (coords && coords.rel.includes("/") && (await hasSymlinkedParent(coords.root, coords.rel))) {
365
- return { path, contentHash: null }; // swapped-in symlinked parent → refuse, same as the loader
234
+ return { path, contentHash: null };
366
235
  }
367
236
  const raw = await readRawCapped(path, INSTRUCTION_READ_CAP);
368
237
  return { path, contentHash: raw === null ? null : instructionHash(raw) };
@@ -1,23 +1,10 @@
1
- /**
2
- * prompts 域候选门(判形)——从 capabilities/center-prompts.ts **下沉**的中立叶子(design/158 S6,
3
- * lens2 §G:`config-lkg.ts → capabilities/center-prompts.ts` 是全仓唯一一条显式分层反向边,config 层
4
- * 值 import capabilities 层)。本模块只依赖 node 内置 + `@sema-agent/core`,不依赖 capabilities/
5
- * config 任何东西,config-lkg(LKG 读侧复判)与 capabilities(候选门)同引即无反向边。
6
- * 消费者直引本模块([2354] 兼容面全清:center-prompts.ts 的原位兼容 re-export 已删);语义/文案零改动——逐字搬运。
7
- *
8
- * ─── 以下为原 center-prompts.ts 的判形半场,注释原样保留 ───
9
- *
10
- * 本模块仅有的一层「形校验」不是语义复检,是**防炸底线**:core 对坏形声明的守卫是 THROW(assemble 期),
11
- * 一个坏 pack 会让每个任务 prepare 炸掉——server 在采用前做结构判形,坏形=拒绝采用该候选的 prompts 面
12
- * (warn 点名,沿用上一个好的/内置),任务面永不因配置坏形而全灭(config 候选门同精神,只作用于本域)。
13
- */
14
1
  import { createHash } from "node:crypto";
15
2
  import { createRequire } from "node:module";
16
3
  import { readFileSync } from "node:fs";
17
4
  import { dirname, join } from "node:path";
18
5
  import { verifyPromptArtifact } from "@sema-agent/core";
19
6
  import { SHA256_DIGEST_RE } from "./digest-form.js";
20
- const ID_RE = /^[a-z0-9][a-z0-9/._-]*$/; // core assemble 同形(≤128、非 core/ 前缀在下面单查)
7
+ const ID_RE = /^[a-z0-9][a-z0-9/._-]*$/;
21
8
  const SLOTS = new Set(["identity", "scenario", "behavior"]);
22
9
  function declarationIssue(d, where) {
23
10
  if (d === null || typeof d !== "object")
@@ -31,10 +18,6 @@ function declarationIssue(d, where) {
31
18
  return `${where}: bad slot`;
32
19
  if (typeof o.text !== "string" || o.text.length === 0)
33
20
  return `${where}: text must be a non-empty string`;
34
- // 发版复审(1.224 批)F1:contentHash **必备且重算比对**——core 的 epoch 声明轴用 (id,slot,chars,
35
- // contentHash?),hash 缺席/被 core 丢弃(错值)后**同长度文本编辑不改 artifactDigest=不重 pin**,
36
- // 恰是撤 restart 臂所依赖的审计不变量被击穿。center 链路恒逐 section 铸([1065]),正常路径零影响;
37
- // 这里拒的是坏发布/被改载荷(整拒候选,LKG/内置继续服务)。
38
21
  if (typeof o.contentHash !== "string" || !SHA256_DIGEST_RE.test(o.contentHash))
39
22
  return `${where}: contentHash is required (sha256:<64hex> — the epoch declaration axis needs it for byte-level change detection)`;
40
23
  const expected = `sha256:${createHash("sha256").update(o.text).digest("hex")}`;
@@ -42,11 +25,6 @@ function declarationIssue(d, where) {
42
25
  return `${where}: contentHash does not match sha256(text) — stale or tampered declaration`;
43
26
  return undefined;
44
27
  }
45
- /**
46
- * 结构判形(防炸底线,非语义复检)。`ok:false` 时调用方拒绝采用该候选的 prompts 面并 warn——绝不让
47
- * 一个坏 pack 把每个任务的 prepare 炸掉。校验与 core assemble 的 THROW 条件同形(id 规范/长度/core/
48
- * 保留、slot 闭集)+ 下发信封两键形;`contentHash` 只查形(值的校验归 core 1.315,错值 core 丢弃+warn)。
49
- */
50
28
  export function validateCenterPrompts(raw) {
51
29
  if (raw === null || typeof raw !== "object")
52
30
  return { ok: false, error: "prompts is not an object" };
@@ -55,11 +33,6 @@ export function validateCenterPrompts(raw) {
55
33
  return { ok: false, error: "bad packId form (center:<digest12>)" };
56
34
  if (typeof p.contentDigest !== "string" || !SHA256_DIGEST_RE.test(p.contentDigest))
57
35
  return { ok: false, error: "bad contentDigest form" };
58
- // 发版复审 F2/F4 两条硬化:
59
- // - 数组内 id 必须唯一(core composer 对 duplicate section id 是 THROW——独立判形查不出对撞,必须
60
- // 整数组查重;基线与各 override 互不查——override 整包替换,不同数组不共存于一次装配);
61
- // - 空数组=坏形拒绝(F4:wire 契约是「整包替换终形」,空数组语义未定义——「显式清空场景」不是 v1
62
- // 用例,静默把空重解释为「回落内置」会跑出与 center 发布物不同的提示词;语义决定权推回 center)。
63
36
  const arrayIssue = (decls, where) => {
64
37
  if (!Array.isArray(decls))
65
38
  return `${where} is not an array`;
@@ -92,10 +65,6 @@ export function validateCenterPrompts(raw) {
92
65
  }
93
66
  return { ok: true, value: raw };
94
67
  }
95
- // ═══ 双轴门(a 实现班车,[985]a/[1242]/[1248] 过轮定稿;registry-core 0.10.20 同班车)═══
96
- /** core 引擎版本(verifyPromptArtifact 的 compatibleCore range 输入)——经 exports map 解析包根读
97
- * package.json(`require('@sema-agent/core/package.json')` 被 exports 封死,ERR_PACKAGE_PATH_NOT_EXPORTED
98
- * 第一手教训)。解析失败回 "0.0.0" = range-gated artifact 被诚实拒(fail-closed 响,绝不静默采用)。 */
99
68
  export const CORE_ENGINE_VERSION = (() => {
100
69
  try {
101
70
  const entry = createRequire(import.meta.url).resolve("@sema-agent/core");
@@ -105,21 +74,11 @@ export const CORE_ENGINE_VERSION = (() => {
105
74
  return "0.0.0";
106
75
  }
107
76
  })();
108
- /**
109
- * prompts 域候选门 v2(validateCenterPrompts 的双轴升级;整拒语义不变=任一在场轴坏形 ⇒ 整个 prompts
110
- * 面拒绝采用,LKG/内置继续服务)。唯一认可形=`{schemaVersion:1, declarations?, catalog?}`
111
- * (registry-core `EffectivePromptsWire`)至少一轴。[2373]B-9(5.0.0):legacy 平铺兼容窗到期删——
112
- * 0.13.0 起 EffectivePromptsWire 首字段是 `schemaVersion: z.ZodLiteral<1>`,与 server 共用 ^0.13.0 的
113
- * 合规 center 不可能再发平铺;非合规体=响亮拒绝(整拒落 LKG/内置),不再静默吸收。
114
- * catalog 轴的语义权威=core `verifyPromptArtifact`(形/digest 逐位复算/ARTIFACT_LIMITS/compatibleCore
115
- * range 全在 core 一处——[1242]③ a 解,上限单真源永不漂)。
116
- */
117
77
  export function validatePromptsDomain(raw) {
118
78
  if (raw === null || typeof raw !== "object")
119
79
  return { ok: false, error: "prompts is not an object" };
120
80
  const w = raw;
121
81
  if (w.schemaVersion === undefined) {
122
- // [2373]B-9:平铺形拒绝(兼容窗 5.0.0 到期)。文案带指引=下游冒烟立刻可自修,不是哑谜。
123
82
  return { ok: false, error: "prompts payload has no schemaVersion — the legacy flat shape was retired in 5.0.0; wrap as {schemaVersion:1, declarations:{...}} (registry-core EffectivePromptsWire)" };
124
83
  }
125
84
  if (w.schemaVersion !== 1)
@@ -138,21 +97,12 @@ export function validatePromptsDomain(raw) {
138
97
  const c = verifyPromptArtifact(w.catalog, { engineVersion: CORE_ENGINE_VERSION });
139
98
  if (!c.ok)
140
99
  return { ok: false, error: `catalog axis: ${c.code}: ${c.reason}` };
141
- // verify 返回的是重建后的 typed artifact;wire 上的 envelope 原样保留(sourceRevision 不进 digest,
142
- // e/b 接线时 core artifact 仓吃 envelope 全形)。
143
100
  value.catalog = w.catalog;
144
101
  value.axes.push("catalog");
145
102
  }
146
103
  value.identity = `${value.declarations?.contentDigest ?? "-"}+${value.catalog?.artifact.artifactDigest ?? "-"}`;
147
104
  return { ok: true, value };
148
105
  }
149
- /** #100(docs/R100-PROMPTS-LANE-FIX.md §V2-B)worker 腿 effective 上 `prompts` 键的**形-肯定**判别:
150
- * 消费形特征(v2 `schemaVersion` 键,或 legacy 三键 packId/contentDigest/sections)⇒ 原样返回交
151
- * {@link validatePromptsDomain}(收或拒,方向不变);其余=center 存储原文(global lane [1057]①e 下发
152
- * 存储形是合法输入)——catalog 在场则投影 `{schemaVersion:1, catalog}`(config-provider 本地腿先例),
153
- * 否则视为「center 未发」⇒ undefined。null=有意 cleared 语义(#100 前 null 走拒,方向为宽是设计变更,
154
- * 留痕于设计档)。垃圾标量原样返回(fail-loud 归 validate)。
155
- * 属主放本中立叶:config-lkg 与 boot/refresh 同吃,capabilities 层会重开 S6 刚消的反向依赖边。 */
156
106
  export function centerPromptsFromEffective(eff) {
157
107
  const prompts = eff?.prompts;
158
108
  if (prompts === undefined || prompts === null)