@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
@@ -8,113 +8,31 @@ import { createDiscussionTools, discussionCoordinatorPrompt, getTeam, teamNames
8
8
  import { createWebSearchBackend, webSearchConfigFromSettings } from "../plugins/web-search.js";
9
9
  import { skillsForScenario } from "./skills.js";
10
10
  import { pickHandsRunner } from "./hands-lane.js";
11
- import { SCENARIO_ALIASES, canonicalScenarioName } from "./scenario-alias.js";
12
- // #273 / C-R14:alias 表与两只归一口的**属主**是 `scenario-alias.ts`(零依赖模块,避免
13
- // scenarios ↔ skills/skills-mcp/center-prompts 的循环 import)。此处再导出一次,保持既有 import 面。
14
- export { SCENARIO_ALIASES, canonicalScenarioName, scenarioConfigKeys } from "./scenario-alias.js";
15
- /**
16
- * Sema product identity, prepended to the core default base for the `default` scenario when
17
- * {@link ScenarioDeps.brandIdentity} is on. Honest about the substrate (the engine runs whichever model
18
- * the operator configured — not Claude/Anthropic), so the agent never inherits the model's training-time
19
- * "I am Claude" hallucination.
20
- *
21
- * Live-caught incident (a deployment served by one model family described itself as running on a DIFFERENT
22
- * vendor's model, because that vendor's name was baked into this text):
23
- * NOTHING substrate-specific may be hardcoded here — onboard is multi-provider (any OpenAI-/Anthropic-
24
- * compatible endpoint, self-hosted vLLM included), so a baked maker name makes every deployment that is not
25
- * that maker's misstate its own substrate. Same for
26
- * the gateway protocol (the brain stack serves Anthropic-compatible routes too — routing brain +
27
- * createAnthropicBrain — so "OpenAI-compatible gateway" would be the same class of lie). Neutral wording
28
- * (zero core-seam need; interpolating the model id would need a `StablePromptContext.model`
29
- * seam and reads poorly for ugly preset ids). The anti-"I am Claude" design is kept — only the baked
30
- * substrate facts are removed.
31
- */
32
- export { SEMA_IDENTITY } from "./prompts/identity.js"; // copy moved to the bundle-data layer
11
+ export { SEMA_IDENTITY } from "./prompts/identity.js";
33
12
  import { SEMA_IDENTITY } from "./prompts/identity.js";
34
- /**
35
- * Prompt provider for the branded `default` scenario: SEMA_IDENTITY on top of the ROLE base — the
36
- * task's own `body.systemPrompt`, or core's {@link DEFAULT_SYSTEM_PROMPT} when none. Core 1.243
37
- * (提示词主权批) appends the constitution structurally AFTER this role layer (harness
38
- * safety blocks + modes + memory guidance + the prefix-cache-safe `<user_memory>` tail), so identity
39
- * cannot displace the safety blocks. The pre-1.243 form (identity + `defaultPromptProvider.stableSystem`,
40
- * i.e. an ALREADY-assembled prompt) would now trip the migration guard (anchor-detect → pass-through +
41
- * `onError(phase:"prompt-constitution")`) — this role-only form is the guard-free contract.
42
- */
43
13
  export function semaIdentityProvider() {
44
- // Lens-4 endgame (core 1.310, [1006]②/[1012]): the typed stableBlocks branch now carries core's OWN
45
- // pass-through guard (M9-family anchor detection on identity declarations → provider-assembled,
46
- // un-doubled) — the 0c3352f server-side fallback routing for the already-assembled input face is
47
- // retired; the real-composer parity matrix keeps that face pinned byte-equal against the pre-M14 form.
48
14
  return {
49
- // M14 (core 1.303, 板 [968]): the branded head is a TYPED block — the stable id "sema/identity" gives
50
- // it a manifest section identity + digest (the G-1 observability gap; IDENTITY-DIGEST-MIGRATION.md
51
- // route B, this hook is its carrier). identity-slot semantics: the block REPLACES core/role.base at
52
- // the head position, so it must carry the FULL role layer (identity + role base) — byte-identical to
53
- // the previous stableSystem string. Finer identity/role digest separation is the catalog-section
54
- // endgame, not this hook's job.
55
15
  stableBlocks: (ctx) => [
56
16
  { id: "sema/identity", slot: "identity", text: `${SEMA_IDENTITY}\n\n${ctx.userSystemPrompt ?? DEFAULT_SYSTEM_PROMPT}` },
57
17
  ],
58
18
  };
59
19
  }
60
- /**
61
- * `code` 场景的提示词提供者([849] 造,[891] clay 裁定改名 autonomous→code):role 基底 = core
62
- * {@link CODE_SYSTEM_PROMPT}(core 1.298 [891] 改名批正名,旧名 FULL_BODY_SYSTEM_PROMPT=恒等
63
- * 别名过渡窗;CODE_AGENT persona + AUTONOMY_SELF_AUDIT + ANTI_VERBOSITY +
64
- * TOOL_PARAM_JSON;[891] 血缘定论 = CC 生产编码提示词的蒸馏去牌版,底本随 CC 版本对表 cc-anchors,
65
- * 非自研)——与 bench 的请求组装完全同源,引用 core 常量随版本走,绝不复制字符串(tb bench 实测:
66
- * 产品默认请求与同引擎 bench 态的差距根因就在这三件 persona/自检/终验)。品牌层与
67
- * {@link semaIdentityProvider} 同构:brandIdentity 开时 SEMA_IDENTITY 叠在 role 基底之上;任务自带
68
- * systemPrompt 按同一优先序赢过基底(role 合成规则与 default 场景一致)。宪法由 core 1.243 在 role
69
- * 层之后结构化追加——此处只产 role-only 形态(guard-free 契约,同 semaIdentityProvider 注释)。
70
- * 无 systemPrompt 且无品牌时原样返回 core 常量(测试以引用比对锁 bench 同源,不做字符串快照)。
71
- */
72
20
  export function codePromptProvider(brand) {
73
- // [1158]/[1161]/[1162] b 案(clay 拍):provider 供给的字节 core 永不改写(provenance 卫,1.329),
74
- // 所以 known-constant narrowing 够不着 provider 手里的 CODE_SYSTEM_PROMPT——simple 形的「裸底」
75
- // 由 server 自己按 ctx.promptProfile 选 core 常量(CODE_AGENT_PROMPT=core 1.329 narrowing 的同一
76
- // 目标常量,引用不复制,[891] 同源铁律)。classic 全链字节等价旧形;行为九段随 pack 走(平形
77
- // stableSystem/stableBlocks 都不排段;server 从未用 replaceAll)。
78
21
  const roleBase = (ctx) => ctx.userSystemPrompt ?? (ctx.promptProfile === "classic" ? CODE_SYSTEM_PROMPT : CODE_AGENT_PROMPT);
79
- // Unbranded: plain role layer via the string hook — core keeps its own core/role.base section identity,
80
- // and the reference-equality bench-parity tests keep comparing the exact core constant.
81
22
  if (!brand) {
82
23
  return { stableSystem: (ctx) => roleBase(ctx) };
83
24
  }
84
- // Branded: same M14 typed form as semaIdentityProvider (identity slot = role-layer replacement with the
85
- // brand line on top; the base under the brand follows the SAME profile fork — the brand overlay is the
86
- // legal divergence ([1134] 宪法二类), the base stays core-owned bytes).
87
25
  return {
88
26
  stableBlocks: (ctx) => [
89
27
  { id: "sema/identity", slot: "identity", text: `${SEMA_IDENTITY}\n\n${roleBase(ctx)}` },
90
28
  ],
91
29
  };
92
30
  }
93
- // [2400] CAPS-OPS-11:autonomousPromptProvider 别名导出已随场景退役删除(真身=codePromptProvider)。
94
- // 其 `@deprecated` JSDoc 曾残留于此,被 tsc 误挂到下方 buildScenarios(三处调用点全告 deprecated
95
- // ——TODO-3 立案的「迁移」实为孤儿注释,随删除清账;buildScenarios 本身非退役面)。
96
- /** #196 唯一取用口:场景按自己的判别位取子任务 Runner。场景体内一律不直接摸 `deps.subRunner` /
97
- * `deps.handslessSubRunner` —— 两处各挑一次就是漂移的成因,判别位必须是唯一开关。 */
98
31
  function subRunnerFor(deps, hands) {
99
32
  return pickHandsRunner(hands, { full: deps.subRunner, handsless: deps.handslessSubRunner });
100
33
  }
101
34
  export function buildScenarios(deps) {
102
- // The default scenario filters skills by "default" — NOT by req.scenario (which may be an unknown name
103
- // that fell back here): the bundle must be a coherent "default" set, matching how main.ts resolves an
104
- // unknown scenario to "default" for tools/prompt/mcp too (council finding — else default-tagged drop).
105
- // 抽成具名工厂:`code` 直接复用它取 bundle(工具/技能面与 default 恒等由构造保证,不靠两处
106
- // 各写一份 assembleCodeTools 参数再祈祷不漂移)。
107
35
  const defaultScenario = (req, _principal, ctx) => {
108
- // gap-audit rank-1 BLOCKER: the default agent is the CC agent loop — it MUST have the
109
- // full-body roster, not just nowTool. core's `assembleCodeTools` (1.161) ships TodoWrite (planning) +
110
- // the subagent/Task delegation tool (runs on the sub-runner, child ⊆ parent — no escalation) + WebFetch.
111
- // 🔒 WebFetch is GATED OUT on a multi-tenant deployment (REQUIRE_PRINCIPAL): allowHosts is unset = all PUBLIC,
112
- // and core says a multi-tenant deployment MUST allowlist (an injected tenant model could exfil via an
113
- // arbitrary public URL — the SSRF floor only blocks INTERNAL). Single-user TOC keeps it (the user's own box).
114
- // WebSearch backend resolution: a per-request `body.settings.webSearch` (the user's shell settings) WINS, but
115
- // ONLY on the single-user host lane (a per-request endpoint/key is a capability config — on multi-tenant a tenant
116
- // could SSRF via a `searxng` URL pointed at an internal host, so multi-tenant uses ONLY the deployment-env
117
- // `deps.webSearch`; design/107 axis, mirrors settings.env). Absent both ⇒ WebSearch not assembled (core ships none).
118
36
  const reqWebSearch = deps.requirePrincipal !== true
119
37
  ? webSearchConfigFromSettings(req?.settings?.webSearch)
120
38
  : undefined;
@@ -123,43 +41,13 @@ export function buildScenarios(deps) {
123
41
  tools: [
124
42
  nowTool(),
125
43
  ...assembleCodeTools({
126
- // [1900]/[1904]:WebFetch 摘要缝——键在场才铸(负控=修前字节形)。
127
44
  ...(deps.webFetchSummarize ? { webFetch: { summarize: deps.webFetchSummarize } } : {}),
128
- // CC 201 parity: CC 201 makes the two planning families MUTUALLY
129
- // EXCLUSIVE — with TodoV2 (the Task trio) enabled, TodoWriteTool.isEnabled returns false and the
130
- // interactive model sees ONE family (TodoWrite survives only as an SDK/non-interactive compat slot).
131
- // Both-on was the CC 198 posture (live-caught cost: the model wastes a turn comparing the two tools
132
- // and juggles duplicate state). taskList:true below ⇒ TodoWrite OFF.
133
45
  todoWrite: false,
134
- // core 1.215 (design/119): the structured task-list family (TaskCreate/TaskGet/TaskUpdate/TaskList —
135
- // stable ids, status transitions, dependencies); core defaults it OFF, the full-body deployment opts
136
- // in. Under CC 201 this family is the ONLY planning leg (see todoWrite above).
137
46
  taskList: true,
138
- // #318([4659] F2 → [4692] 定谳):**会话级**清单店。缺这一键时 core 落
139
- // `store ?? createMemoryTaskListStore()` 的私有 per-run 臂,而本工厂是**每请求**调用的
140
- // (resolve-spec 的 selectScenario;resume 同链重解析)⇒ 同一会话每 turn 一只新店:
141
- // TaskList 中途报空、TaskCreate 从 #1 重铸号,已发给用户的任务回执从此指向别的任务。
142
- // 分区键 = 会话(同 session 跨 turn/跨 resume 连续,跨 session 互不可见);sessionId 只从
143
- // 鉴权解析的 {@link ScenarioContext} 取,绝不从 req(= 调用方自报的 body)取。
144
- // 无 sessionId 的调用形(parkedReviveTool 的裸 Agent 取用、能力详情探针)整键省略 ⇒
145
- // per-call 私店 = 修前字节形(那两条腿都不消费 task-list 家族)。
146
47
  ...(ctx?.sessionId !== undefined && deps.taskListStoreFor
147
48
  ? { taskListStore: deps.taskListStoreFor(ctx.sessionId) }
148
49
  : {}),
149
- // core 1.206 (design/115 P3): `background` exposes CC's `run_in_background` on the Agent tool — the
150
- // child runs async under an `a*` task id (TaskOutput/TaskStop; ONE "later" completion notification;
151
- // parent teardown aborts orphans). Same trust envelope as the sync path (child ⊆ parent roster, same
152
- // rights) — only the scheduling is async, so no posture gate. owner/scope: core 1.365 BREAKING 升必填,
153
- // 但 core 仍 ctx-first(subagent.js `ctx.taskId ?? bg.owner` / `ctx.principal ?? bg.scope`)——引擎链
154
- // 每 spawn 恒带 ctx.taskId,这里的值是类型要求的兜底:scope="default" 与引擎链 `principal ?? "default"`
155
- // 同词([1509] 单租户拼法);owner 哨兵在 ctx.taskId 缺席的病态窗把行锚到无人可匹配的键(fail-closed
156
- // 方向,default-deny 谓词下不可读,绝不放大)。
157
- // core 1.364 agentStore = durable 写半场,与 RunnerDeps.backgroundAgentStore 同实例(deps 注入)。
158
- // core 1.382 checkpointStore/ensureChildSessionDurable = design/153 parked 状态机的「同车必接」
159
- // 一对([1561] 提货单①②,ScenarioDeps 字段注释详述);两者任一缺席 = 净是 pre-153 行为。
160
50
  subagent: {
161
- // #196:default/code 的委派子代**要**手(CC parity;main.ts subRunner 注里那条 "subagent
162
- // couldn't run pwd" 缺陷就是这条腿缺手),故按本场景判别位取 full 支。
163
51
  runner: subRunnerFor(deps, "full"),
164
52
  background: {
165
53
  registry: defaultTaskRegistry,
@@ -169,57 +57,26 @@ export function buildScenarios(deps) {
169
57
  ...(deps.checkpointStore ? { checkpointStore: deps.checkpointStore } : {}),
170
58
  ...(deps.ensureChildSessionDurable ? { ensureChildSessionDurable: deps.ensureChildSessionDurable } : {}),
171
59
  },
172
- // core 1.287 extraTools seam ([795]/[802]③): per-spawn dynamic tool injection (SendUserFile
173
- // reaches the delegated-child roster; product still goes through resolveToolSubset).
174
60
  ...(deps.subagentExtraTools ? { extraTools: deps.subagentExtraTools } : {}),
175
- // 历史复审轴B #3(1.254):extraTools 工厂 throw 的错误 sink——core 契约「absent ⇒ dropped after
176
- // the generic disclosure」= 部署日志零痕迹,只剩模型面固定 note。接 warn 让故障可观测。
177
61
  ...(deps.subagentExtraTools && deps.logger
178
62
  ? { onExtraToolsError: (err, info) => deps.logger?.warn?.("subagent_extra_tools_error", { ...info, err: err instanceof Error ? err.message : String(err) }) }
179
63
  : {}),
180
64
  },
181
65
  ...(webSearch ? { webSearch } : {}),
182
- // A-033.1(#256):工具名取 core 的 `WEB_FETCH_TOOL_NAME` 而不是裸串 —— 这是**多租户闸**
183
- // (allowHosts 未设 = 全公网可达 ⇒ 一个租户能把另一个租户的内网可达面当出口)。core 改名该工具时,
184
- // 裸串比对恒不相等,闸门整条不生效且零症状:名单里照样挂着一只全公网 WebFetch。
185
66
  }).filter((t) => t.name !== WEB_FETCH_TOOL_NAME || deps.requirePrincipal !== true),
186
67
  ],
187
- // default/code 是手带并集的**故意消费者**(上方 rank-1 BLOCKER 注:CC agent loop MUST have the
188
- // full-body roster)——#196 判别位在这里表态 full,收窄只落在声明了 none 的场景上。
189
68
  hands: "full",
190
69
  skills: skillsForScenario(deps.skills, "default"),
191
- // Local/TOC deployment brands the neutral default scenario as Sema (so the local engine answers "who
192
- // are you" as Sema, not the model's hallucinated identity). Cloud leaves it neutral unless opted in.
193
70
  ...(deps.brandIdentity ? { promptProvider: semaIdentityProvider() } : {}),
194
71
  };
195
72
  };
196
73
  return {
197
74
  default: defaultScenario,
198
- // [849]→[891] code(原名 autonomous,clay 裁定改名+persona×终验解耦):与 default 完全同 roster
199
- // (full-body 工具面 + default 技能面原样复用,连 WebFetch 多租户闸/WebSearch 装配都跟着 default
200
- // 工厂走),只换一轴——role 基底 = core CODE_SYSTEM_PROMPT(CC 编码 persona 蒸馏版,见
201
- // codePromptProvider)。**不再定死 finalVerification**([891] 解耦:终验是无人值守运行模式旋钮,
202
- // caller 显式声明,交互态不多烧终验轮)。这是 [891] 起的出厂缺省场景(config DEFAULT_SCENARIO
203
- // 缺省 "code")——编码产品出厂态对标 CC 出厂态;通用域中性 persona 的 default 保留为显式可选。
204
- // 刻意走内建、不走 center overlay:overlay 已无 prompt 通道([1053] ScenarioEntry.prompt 直删,场景级提示词=center prompts binding)。
205
- // #318:`ctx` 必须原样转给 default 工厂 —— 漏转 = 出厂缺省场景(DEFAULT_SCENARIO 缺省即 code)
206
- // 拿不到会话级清单店,F2 在最常用的那条腿上原地复活([4659] 症状③ 钉的就是这一格)。
207
75
  code: (req, principal, ctx) => ({
208
76
  ...defaultScenario(req, principal, ctx),
209
77
  promptProvider: codePromptProvider(deps.brandIdentity === true),
210
78
  }),
211
- // [891]→[2400] CAPS-OPS-11(clay 裁 2026-08-03,不留任何过渡):`autonomous` 过渡 alias 已退役。
212
- // body 显式传它 ⇒ 5.1.0 起的 scenario_unknown 400 响亮拒(附现役词表);旧语义迁移形=
213
- // scenario:"code" + body.finalVerification:true(终验自此=纯 caller 显式旋钮,场景层无定死)。
214
79
  "code-review": (req) => codeReview(deps, req),
215
- // Neutral repo-readonly scenario (OA ask): the SAME clone-free read-only repo tools as code-review
216
- // (repo_tree / repo_read_file / repo_pull_diff over GIT_API_TOKEN) with NO promptProvider — the
217
- // objective and the center-distributed skills own the output contract. The reviewer framing was
218
- // never a security control (the boundary is the token's scope + the read-only tool set); it only
219
- // fought skill-driven outputs (live: oa-repo-scan loaded but the lead still wrote a review).
220
- // Deliberately NOT the alternative "let a scenario-targeted skill override promptProvider": that
221
- // would let runtime-distributed content rewrite the deployment-pinned system prompt and break the
222
- // stablePrompt STABLE→VARIABLE prefix-cache discipline (design/12/17).
223
80
  scan: (req) => {
224
81
  const repoClient = requireRepoClient(deps, "scan");
225
82
  if (typeof req.repo !== "string" || !req.repo.trim()) {
@@ -227,28 +84,18 @@ export function buildScenarios(deps) {
227
84
  }
228
85
  return {
229
86
  tools: [...repoToolsFor(repoClient, parseRepo(req.repo)), nowTool()],
230
- // #196:头注的「工具集即边界」自此在**最终 roster** 上成立(修前 core 把手带 band 并集进来,
231
- // clone-free 承诺只存在于这份声明里)。
232
87
  hands: "none",
233
88
  skills: skillsForScenario(deps.skills, "scan"),
234
89
  };
235
90
  },
236
- // Multi-agent DISCUSSION (#273 / C-R14 改名,旧名 `team` = 入口 alias 一版,见 {@link SCENARIO_ALIASES}):
237
- // pick a template, the consumer (OA) injects business context, we run it. Templates resolve from the
238
- // live registry (`getTeam`) = BUILTIN_TEAMS overridden/extended by the sema registry at runtime
239
- // (applyEffective → registerTeams), so the roster is config-driven; built-ins are the fallback when
240
- // the center supplies none. ⚠️ 模板注册面(`req.team` / center `teams` 键 / registry TEAMS.md)刻意
241
- // **不**改名:那是 registry-core 的配置词汇(跨仓契约),改它要另立件+center 侧同款 alias;C-R14 的
242
- // server 射程 = 场景名/工具名/模型可见文案三件([4127] 认领形)。
243
91
  discuss: (req) => {
244
92
  const name = typeof req.team === "string" ? req.team : "default";
245
- const template = getTeam(name); // built-ins, overridden/extended by the sema registry at runtime
93
+ const template = getTeam(name);
246
94
  if (!template) {
247
95
  throw new HttpError(404, `unknown discussion roster "${name}" (available: ${teamNames().join(", ")})`);
248
96
  }
249
97
  const objective = typeof req.objective === "string" ? req.objective : "Discuss the task.";
250
98
  const businessContext = typeof req.businessContext === "string" ? req.businessContext : undefined;
251
- // #196:成员/synthesizer 是纯讨论人格(模板声明零工具),跑在无手 subRunner 上。
252
99
  const hands = "none";
253
100
  return {
254
101
  tools: createDiscussionTools(subRunnerFor(deps, hands), template, { objective, businessContext, ...(deps.logger ? { logger: deps.logger } : {}) }),
@@ -259,39 +106,26 @@ export function buildScenarios(deps) {
259
106
  },
260
107
  };
261
108
  }
262
- /** Resolve the scenario for a request (falls back to `default`). */
263
109
  export function selectScenario(scenarios, name) {
264
- // #273 alias:两条产线(HTTP resolve-spec / run-local)在算 `scenarioName` 时已经归一过,所以这里
265
- // 对它们是**幂等**的;真正需要这一手的是**库面**调用方(index.ts 导出 selectScenario,嵌入式装配
266
- // 自己传名字)—— 不归一的话旧名会静默落 `default`,那是比「名字过时」更坏的结果(整套工具面换人)。
267
- const resolved = name === undefined ? undefined : canonicalScenarioName(name, Object.keys(scenarios));
268
- // #193 P2-13:own-property 守卫——`Record` 字面量的裸索引对原型链键("constructor" 形状)会把
269
- // Object.prototype 上的函数当 Scenario 返回。上游 resolve-spec 按 Object.keys 校验 ⇒ 当前不可利用,
270
- // 本守卫是纵深防御(本函数是导出面,不得依赖单一调用方的入口校验)。
110
+ const resolved = name;
271
111
  return (resolved && Object.hasOwn(scenarios, resolved) && scenarios[resolved]) || scenarios.default;
272
112
  }
273
- /** 把属主的判别式摊平成 wire 上的两个键(可用臂整键省略原因,契约=「缺席即没有理由」)。 */
274
113
  function availabilityFields(deps, toolset) {
275
114
  const verdict = scenarioAvailability(deps, toolset);
276
115
  return verdict.available ? { available: true } : { available: false, unavailableReason: verdict.reason };
277
116
  }
278
- /** 内建五场景详情。default/code/discuss 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan 是
279
- * fail-loud 语义(缺 GIT_API 配置/principal 即 throw)→ 探针失败落静态表兜底(表↔工厂一致性由测试锁:
280
- * 测试喂 fake deps 真调工厂对账工具名)。enabled 对内建恒 true(约定②)。 */
281
117
  export function builtinScenarioDetails(scenarios, deps) {
282
118
  const probe = (name, req, principal, fallback) => {
283
119
  try {
284
- return ((Object.hasOwn(scenarios, name) ? scenarios[name] : undefined)?.(req, principal)?.tools ?? []).map((t) => t.name); // P2-13 同族守卫
120
+ return ((Object.hasOwn(scenarios, name) ? scenarios[name] : undefined)?.(req, principal)?.tools ?? []).map((t) => t.name);
285
121
  }
286
122
  catch {
287
- return fallback; // 工厂 fail-loud(部署未配该场景的后端)→ 静态名单(web 仍能展示工具面选项)
123
+ return fallback;
288
124
  }
289
125
  };
290
126
  const REPO_TOOLS = ["repo_tree", "repo_read_file", "repo_pull_diff", "Now"];
291
127
  const mk = (name, summary, toolset, tools, promptSummary) => ({
292
128
  name, source: "builtin", builtin: true, summary, toolset, tools, promptSummary, enabled: true,
293
- // [C132]:可用性与上面的静态回退表分家 —— 回退表答的是「工具面长什么样」(缺配置时也照画),
294
- // 这两个键答的是「现在点下去会不会 501」。同一份属主判据,内建与 center 两条产线共用。
295
129
  ...availabilityFields(deps, toolset),
296
130
  });
297
131
  return {
@@ -301,13 +135,9 @@ export function builtinScenarioDetails(scenarios, deps) {
301
135
  default: mk("default", "通用/助理场景(域中性 persona,CC agent loop 全量工具面;[891] 起非出厂缺省,显式可选)", "full-body", probe("default", {}, undefined, ["Now"]), deps.brandIdentity ? "Sema 产品身份 + core 默认宪法(结构层由引擎合成)" : "core 默认宪法(中性,无场景附加 persona)"),
302
136
  "code-review": mk("code-review", "代码评审(clone-free 只读仓库工具;council=true 升多镜头评审团)", "repo-readonly", probe("code-review", { repo: "probe/probe" }, undefined, REPO_TOOLS), "评审员/评审团协调提示词(镜头分工+产出契约)"),
303
137
  scan: mk("scan", "中性只读仓库扫描(输出契约由 objective+skills 决定)", "repo-readonly", probe("scan", { repo: "probe/probe" }, undefined, REPO_TOOLS), "无场景附加提示词(objective+center skills 拥有输出契约)"),
304
- // #273 / C-R14:旧名 `team` 不再是一条**被声明**的场景(它只是 SCENARIO_ALIASES 里的入口别名)——
305
- // 列举面只列现役主名,免得消费方把别名当成第二条能力画上去。
306
- discuss: mk("discuss", "多智能体讨论圆桌(模板成员+synthesizer,模板可由 registry 配置)", "discuss", probe("discuss", {}, undefined, ["run_discussion", "run_team"]), "讨论协调者提示词(圆桌纪律+合成流程)"),
138
+ discuss: mk("discuss", "多智能体讨论圆桌(模板成员+synthesizer,模板可由 registry 配置)", "discuss", probe("discuss", {}, undefined, ["run_discussion"]), "讨论协调者提示词(圆桌纪律+合成流程)"),
307
139
  };
308
140
  }
309
- /** center 条目详情(有效性判定与 centerScenarios 完全同款:无效条目既不进 overlay 也不进详情——
310
- * 保证约定①「详情显示的来源=运行实际用的定义」永不错位)。 */
311
141
  export function centerScenarioDetails(specs, builtinNames, deps) {
312
142
  const out = {};
313
143
  for (const spec of specs ?? []) {
@@ -315,23 +145,17 @@ export function centerScenarioDetails(specs, builtinNames, deps) {
315
145
  continue;
316
146
  if (typeof spec.name !== "string" || !SCENARIO_NAME_RE.test(spec.name))
317
147
  continue;
318
- if (Object.hasOwn(SCENARIO_ALIASES, spec.name))
319
- continue; // #273:保留名与 centerScenarios 同判(约定①)
320
148
  if (!Object.hasOwn(TOOLSETS, spec.toolset) || !TOOLSETS[spec.toolset])
321
- continue; // P2-13 同族守卫
149
+ continue;
322
150
  out[spec.name] = {
323
151
  name: spec.name,
324
152
  source: "center",
325
153
  builtin: builtinNames.includes(spec.name),
326
- summary: "", // center spec 无 notes 字段(回落空串;registry 侧加 notes 后在此透传)
154
+ summary: "",
327
155
  toolset: spec.toolset,
328
156
  tools: spec.toolset === "repo-readonly" ? ["repo_tree", "repo_read_file", "repo_pull_diff", "Now"] : ["Now"],
329
- // [1053] ScenarioEntry.prompt 已删(scenario 级提示词=center prompts binding 单入口)——center 条目
330
- // 恒空串;字段本身保留(builtin 条目仍有真值,wire 形不变)。
331
157
  promptSummary: "",
332
158
  enabled: true,
333
- // [C132]:center 声明的 repo 型场景与内建的 scan/code-review 受**同一份**判据管辖
334
- // ——overlay 不是判据的第二属主。
335
159
  ...availabilityFields(deps, spec.toolset),
336
160
  };
337
161
  }
@@ -352,47 +176,28 @@ const TOOLSETS = {
352
176
  },
353
177
  };
354
178
  const SCENARIO_AVAILABLE = { available: true };
355
- /**
356
- * 🔴 场景可用性的**唯一属主**。列举面(`ScenarioDetail.available` / `unavailableReason`)与请求面
357
- * ({@link requireRepoClient} 的 501 拒绝臂,三处调用点)都只从这里取值——两处各写一份就是本仓反复
358
- * 吃过的「同一语义两个属主」病:判据一漂,列举面开始说谎而没人先红。一致性由 capabilities.test 的
359
- * **对表格**逐名钉住(`available:false` ⟺ 良性请求真吃 501),而不是靠这段注释。
360
- *
361
- * 判据键 = **toolset**:内建详情与 center 条目都带这个字段,故两条产线天然共用同一份判据。
362
- * `requiresRepo` 的 toolset 需要部署配好 git 后端(`GIT_API_BASEURL` ⇒ `deps.repoClient`)。
363
- * 词表外的 toolset(内建的 `full-body`/`discuss`)不依赖后端 ⇒ 恒可用。
364
- */
365
179
  export function scenarioAvailability(deps, toolset) {
366
- if (Object.hasOwn(TOOLSETS, toolset) && TOOLSETS[toolset]?.requiresRepo === true && deps.repoClient === undefined) { // P2-13 同族守卫
180
+ if (Object.hasOwn(TOOLSETS, toolset) && TOOLSETS[toolset]?.requiresRepo === true && deps.repoClient === undefined) {
367
181
  return { available: false, reason: "git_client_unconfigured" };
368
182
  }
369
183
  return SCENARIO_AVAILABLE;
370
184
  }
371
- /** 拒绝文案的唯一属主:闭集穷举 switch(新增原因词不在这里表态即编译红)。 */
372
185
  export function scenarioUnavailableMessage(reason, scenarioLabel) {
373
186
  switch (reason) {
374
187
  case "git_client_unconfigured":
375
188
  return `${scenarioLabel} scenario needs GIT_API_BASEURL configured`;
376
189
  }
377
190
  }
378
- /** repo 型场景取 git 客户端的唯一口(三处场景工厂同源)。不可用 ⇒ 按属主给的原因与文案 501 响亮拒。 */
379
191
  function requireRepoClient(deps, scenarioLabel) {
380
192
  const client = deps.repoClient;
381
193
  if (client !== undefined)
382
194
  return client;
383
195
  const verdict = scenarioAvailability(deps, "repo-readonly");
384
- // 判成 available 却没有客户端 = 属主与消费面脱钩(对表格会先红)。此处仍 fail-loud,不静默放行。
385
196
  throw new HttpError(501, verdict.available
386
197
  ? `${scenarioLabel} scenario has no repo client although the availability owner reports it available (wiring defect)`
387
198
  : scenarioUnavailableMessage(verdict.reason, scenarioLabel));
388
199
  }
389
200
  export const SCENARIO_NAME_RE = /^[a-z][a-z0-9-]{1,31}$/;
390
- // ([1053] registry-core 0.10.15:SCENARIO_PROMPT_MAX 随 ScenarioEntry.prompt 一并退役)
391
- /**
392
- * Build the center-declared scenario overlay. Invalid specs are SKIPPED with a warning (never fail
393
- * boot — same posture as skills/teams); valid ones become scenarios keyed by name, ready to be merged
394
- * OVER the built-ins (center wins). Returns the overlay plus which built-in names it shadows.
395
- */
396
201
  export function centerScenarios(deps, specs, builtinNames, logger) {
397
202
  const overlay = {};
398
203
  const shadows = [];
@@ -403,30 +208,15 @@ export function centerScenarios(deps, specs, builtinNames, logger) {
403
208
  logger?.warn("sema_registry_scenario_invalid", { name: String(spec?.name), reason: "name must match " + String(SCENARIO_NAME_RE) });
404
209
  continue;
405
210
  }
406
- // #273 alias 窗:**旧名是保留名**。窗内 `team` 只能是 `discuss` 的入口别名 —— 让 center 用它另立
407
- // 一条场景会立刻造出两个属主:请求面归它、而按场景键的运维配置(skills/mcp/a2a/prompts 的旧名
408
- // 标签)在 alias 窗里同时也算 `discuss` 的,于是那条影子场景的 MCP/A2A(工具面+egress 面)会静默
409
- // 漏进 discuss 的 roster(codex 对抗复审 [high],验真后采纳)。二义在源头掐掉,并且**响亮**跳过
410
- // ——静默跳过等于让运维以为声明生效了。窗撤销时随 SCENARIO_ALIASES 一起消失。
411
- if (Object.hasOwn(SCENARIO_ALIASES, spec.name)) {
412
- logger?.warn("sema_registry_scenario_invalid", {
413
- name: spec.name,
414
- reason: `"${spec.name}" is a RESERVED deprecated scenario alias during the rename window — it resolves to "${SCENARIO_ALIASES[spec.name]}". Declare this scenario under a different name.`,
415
- });
416
- continue;
417
- }
418
- const toolset = Object.hasOwn(TOOLSETS, spec.toolset) ? TOOLSETS[spec.toolset] : undefined; // P2-13 同族守卫
211
+ const toolset = Object.hasOwn(TOOLSETS, spec.toolset) ? TOOLSETS[spec.toolset] : undefined;
419
212
  if (!toolset) {
420
213
  logger?.warn("sema_registry_scenario_invalid", { name: spec.name, reason: `unknown toolset "${String(spec.toolset)}" (have: ${Object.keys(TOOLSETS).join(", ")})` });
421
214
  continue;
422
215
  }
423
- // [1053] registry-core 0.10.15:ScenarioEntry.prompt 直删(clay 亲裁)——center 场景的提示词通道收敛为
424
- // center prompts binding(scenario 维,[1050]①a/[1051]③a);overlay 只组装工具面+技能面,提示词由投影腿
425
- // (effective 声明数组→core stableBlocks,[1054]③ 已认领)在 binding 落地后统一承载。
426
216
  const name = spec.name;
427
217
  overlay[name] = (req) => ({
428
218
  tools: toolset.build(deps, req),
429
- hands: toolset.hands, // #196:表态随 toolset 走(center 声明面不可自选 hands —— 能力仍是代码定死的)
219
+ hands: toolset.hands,
430
220
  skills: skillsForScenario(deps.skills, name),
431
221
  });
432
222
  if (builtinNames.includes(name))
@@ -443,34 +233,19 @@ function codeReview(deps, req) {
443
233
  const repoTools = repoToolsFor(repoClient, coords);
444
234
  const skills = skillsForScenario(deps.skills, "code-review");
445
235
  const objective = typeof req.objective === "string" ? req.objective : "Review this repository.";
446
- // Tiering: `council: true` runs the multi-lens council (L1 parallel lenses + L3 arbiter) — for
447
- // high-value/whole-repo review (~15× tokens). Default = the lead reviews the in-scope files DIRECTLY
448
- // (cheap, reliable, finishes fast). A live run showed the model self-orchestrates unreliably, so for
449
- // review (a KNOWN decomposition) the subagent fan-out is CODE-driven here, not left to the model
450
- // (deterministic + cheaper + cacheable; see design/35). For OPEN-ENDED tasks where the decomposition
451
- // isn't known, core's `createSubagentTool` (model-driven self-delegation, with maxDepth / isolated
452
- // context / compressed-report guardrails) is the right tool — wire it in a dedicated scenario then.
453
- // #196:两条腿(直评 / council)都声明 none —— 直评腿的 lead 只读仓库工具,council 腿的 lead 只调
454
- // run_council;lens/arbiter 子任务同样只读仓库,故 council 工具拿无手 subRunner。
455
236
  const hands = "none";
456
- // [C143]:lenses 非 number 形响亮拒——此前 string[]/任意形静默滑过(typeof 门恒 false 整键落空),
457
- // client-core 0.22.0 曾按想象契约钉 string[],用户发出的键被静默吞([C142] web-client wire 实证)。
458
- // 与 (a) 案(client-core 改 number)对齐后客户端不会再发该形;本 400 是防第三方直调的纵深。
459
237
  if (req.lenses !== undefined && typeof req.lenses !== "number") {
460
238
  throw new HttpError(400, "lenses must be a number (council lens COUNT, 1-6) — a named lens subset is not a request-level knob", { code: "request.field_invalid" });
461
239
  }
462
240
  if (req.council === true) {
463
- const rounds = clampRounds(req.rounds); // finite-guarded (NaN/±Inf → undefined → council default 1)
241
+ const rounds = clampRounds(req.rounds);
464
242
  return {
465
243
  tools: [
466
244
  createCouncilTool(subRunnerFor(deps, hands), repoTools, objective, {
467
245
  metrics: deps.metrics,
468
246
  logger: deps.logger,
469
- debate: req.debate === true, // L1+L2+L3 (peer debate) vs L1+L3
470
- // OA cost/coverage dial: an optional structured lens COUNT (clamped to [1,6] + NaN-guarded in pickLenses;
471
- // absent = all 6 → zero regression). Per-request, no restart — orthogonal to the lens MODEL (startup env).
247
+ debate: req.debate === true,
472
248
  ...(typeof req.lenses === "number" ? { lensCount: req.lenses } : {}),
473
- // Caller-settable debate rounds (finite, clamped [1,3]; absent/NaN = default 1). Only when debate=true.
474
249
  ...(rounds !== undefined ? { rounds } : {}),
475
250
  }),
476
251
  ],
@@ -479,31 +254,16 @@ function codeReview(deps, req) {
479
254
  promptProvider: coordinatorPrompt,
480
255
  };
481
256
  }
482
- // [C144]([3296]⑤ web-client 实证):直评腿补 nowTool 与 scan 对齐——此前真装配无 Now 而 capabilities
483
- // 声明 fallback 表 REPO_TOOLS 含 Now(repoClient 缺席时 probe 落 fallback 多报一枚=声明/装配错位;
484
- // scan 当年照本场景抄还多加了 Now)。对齐后 fallback 表自动变准,评审报告带时间戳同样合理。
485
257
  return { tools: [...repoTools, nowTool()], hands, skills, promptProvider: directReviewerPrompt };
486
258
  }
487
259
  const reviewBody = "Produce ONE prioritized review: a short summary, then findings grouped by severity " +
488
260
  "(blocker / major / minor), each with `path:line`, the problem, and a concrete fix. Cite real code — never invent files or symbols.";
489
- // Each provider supplies ONLY its stable base; `stablePrompt` enforces STABLE→VARIABLE ordering
490
- // (base, then userSystemPrompt, then per-user memoryBlock) so the prefix-cache discipline can't be
491
- // got wrong by hand (design/12, design/17).
492
- /** Default: the lead reads the in-scope files and reviews them directly. */
493
261
  const directReviewerPrompt = stablePrompt("You are a senior code reviewer. Call repo_tree to map the repository, then repo_read_file to read " +
494
262
  "the files in scope, then review them yourself. " +
495
263
  reviewBody);
496
- /** Council mode: the lead just runs the (code-driven) council tool and returns its result verbatim. */
497
264
  const coordinatorPrompt = stablePrompt("You coordinate a code-review council. Call `run_council` exactly once (no arguments). When it " +
498
265
  "returns the arbitrated review, output that review as your final answer VERBATIM — do not " +
499
266
  "summarize, shorten, or rewrite it.");
500
- /**
501
- * Personalization seam: merge the caller's own skills AFTER the scenario's, with
502
- * SCENARIO-WINS on a name clash — the security baseline (scenario/center skills) can never be shadowed
503
- * by user content. Duplicate names within the user list keep the first. Clashes are logged for audit
504
- * (silent drops would make "my skill didn't fire" undebuggable). Input is already shape/size-validated
505
- * by the HTTP layer (validateUserSkills); this stays pure merge policy.
506
- */
507
267
  export function mergeUserSkills(scenarioSkills, userSkills, logger) {
508
268
  if (!userSkills || userSkills.length === 0)
509
269
  return scenarioSkills;
@@ -515,52 +275,31 @@ export function mergeUserSkills(scenarioSkills, userSkills, logger) {
515
275
  dropped.push(s.name);
516
276
  continue;
517
277
  }
518
- reserved.add(s.name); // also dedupes within the user list itself (first wins)
278
+ reserved.add(s.name);
519
279
  out.push(s);
520
280
  }
521
281
  if (dropped.length > 0)
522
282
  logger?.warn("user_skills_dropped", { reason: "name reserved by scenario (or duplicate)", names: dropped });
523
283
  return out;
524
284
  }
525
- /**
526
- * resolveSpec 的执法入口(指派为默认、body.scenario 可换但限 allowlist)。返回本请求的
527
- * 生效场景名(未做 unknown→default 回退——那是 resolveSpec 既有职责,保持单点)。
528
- *
529
- * - body.scenario 显式给出:allowlist 非空且集外 ⇒ typed 拒绝(400 `scenario_not_allowed`,附 allowlist
530
- * ——机器码归 wire、人话归壳/web);集内 ⇒ 用户主动切换赢。allowlist 为空(无 ruling / 该 principal
531
- * 三层皆无指派 / caps 面故障)⇒ 无治理约束,现状放行(与「null 落兜底链」同一语义:分配面未覆盖 =
532
- * 不收紧,恒不比没有治理时更宽;caps 故障不把用户锁死在 default——场景切换不是权限放大面,fail-open
533
- * 到现状,与 workflows 面的 fail-closed 是刻意的 posture 差异,见 runtime-caps-resolver)。
534
- * - body.scenario 未给:ruling 胜者 ⇒ 指派为默认;无胜者 ⇒ defaultScenario 现状链。
535
- */
536
285
  export function gateScenarioRequest(ruling, bodyScenario, defaultScenario, known, logger) {
537
286
  if (typeof bodyScenario === "string") {
538
- // #273 alias(HTTP lane 的**唯一**归一点,见 SCENARIO_ALIASES 头注):旧名在这里变主名,词表门/
539
- // allowlist 门/返回值三者此后只谈主名。allowlist 一并归一——运维写的是旧名、调用方用新名(或反过来)
540
- // 说的是**同一个能力**,窗内不许因为改名把人挡在门外。
541
- const requested = canonicalScenarioName(bodyScenario, known, logger);
542
- // [2395]D③(5.1.0):body 显式给出的**未知名**响亮 400——此前静默折 default(oa 退役后老调用方
543
- // 无声换了场景=更宽工具面,与零兼容纲领相反)。词表面先于治理面:名字都不存在时谈不上 allowlist。
544
- // 指派链(ruling/default)不在此验——悬空指派名仍走 resolveSpec 既有 unknown→default(runtime-caps
545
- // 面的误配另案,不该让一次中心误配把每个请求 400 死)。
287
+ const requested = bodyScenario;
546
288
  if (!known.includes(requested)) {
547
- // 文案点名调用方**发的**那个名字(归一后的名字对他没有意义:他没发过它)。
548
289
  throw new HttpError(400, `unknown scenario "${bodyScenario}" — this deployment's scenarios are: ${known.join(", ")}`, {
549
290
  code: "scenario_unknown",
550
291
  extra: { known: [...known] },
551
292
  });
552
293
  }
553
294
  const allowed = ruling?.allowlist ?? [];
554
- if (allowed.length > 0 && !allowed.map((n) => canonicalScenarioName(n, known)).includes(requested)) {
295
+ if (allowed.length > 0 && !allowed.includes(requested)) {
555
296
  throw new HttpError(400, `scenario "${bodyScenario}" is not in this principal's assigned allowlist`, {
556
297
  code: "scenario_not_allowed",
557
- extra: { allowlist: allowed }, // 运维自己的字面(不替他改写配置),判定用的是归一后的集合
298
+ extra: { allowlist: allowed },
558
299
  });
559
300
  }
560
301
  return requested;
561
302
  }
562
- // 指派面(ruling.scenario)/部署面(DEFAULT_SCENARIO)同样过归一:两者都是**配置里写的名字**,
563
- // 旧名不归一就会落到 resolveSpec 的 unknown→default 回退上——静默换场景,正是本窗要避免的。
564
- return canonicalScenarioName(ruling?.scenario ?? defaultScenario, known, logger);
303
+ return ruling?.scenario ?? defaultScenario;
565
304
  }
566
305
  //# sourceMappingURL=scenarios.js.map
@@ -1,35 +1,7 @@
1
- /**
2
- * RFC A2 (sema-internal server/docs/design/ENV-SELECT-AND-REGION-SOURCE.md) — the model-facing `SelectEnvironment` tool.
3
- * A 切片先行;挂载路线 = RFC 判定②(a):deployment 经 `spec.tools` 注入(同 lsp/
4
- * 自定义工具),core 收编等真用起来再谈。
5
- *
6
- * Backend = the EXISTING trusted admission chain, nothing new:
7
- * - discovery: `ImageIndex.list({latestOnly, status:'published', viewer})` — the SAME per-principal
8
- * visibility the `/v1/images/select` catalog applies (SQL-baked, never caller enumeration).
9
- * - selection: `resolveSandboxImageRef` (fail-closed profile→digest re-admission).
10
- * - binding: a session-keyed PROFILE (intent — never a digest) the next task's resolveSpec re-resolves
11
- * FAIL-CLOSED, exactly like a body-supplied `sandboxImageProfile`. Storing the profile (not the ref)
12
- * keeps the re-admit discipline intact: every task re-resolves LIVE, a stale digest can never ride a
13
- * session binding past admission.
14
- *
15
- * Boundary contract (verbatim in the tool's model-facing note): a binding change takes
16
- * effect ONLY at a task boundary; any child (including design/129 session-scoped background children)
17
- * inherits the binding at its spawn moment and never drifts mid-flight. This tool therefore only writes
18
- * the session-profile store — the RUNNING task's sandbox is untouched.
19
- *
20
- * 409-style self-correction: a failed selection returns `missing` + the viewer-visible catalog
21
- * (tenant-neutral profile names only — the list enters model context).
22
- */
23
1
  import { Type } from "typebox";
24
2
  import { BoundedSessionMap } from "../bounded-session-map.js";
25
3
  import { resolveSandboxImageRef } from "../per-task-image.js";
26
- /** Session-keyed selected PROFILE (intent). Mirrors PerTaskImageRegistry's bounded-eviction shape, but holds
27
- * the profile string so every subsequent task re-resolves fail-closed (re-admit discipline). In-memory —
28
- * honest v1 boundary: a worker restart drops the selection (the model can re-select; nothing fails closed
29
- * the wrong way, the session just falls back to the worker default image). */
30
4
  export class SessionEnvironmentSelection {
31
- // #97 R11: same BoundedSessionMap basis as PerTaskImageRegistry (composition, not inheritance) — this
32
- // class's own ctor/method surface is unchanged.
33
5
  bounded;
34
6
  constructor(maxEntries = 4096) {
35
7
  this.bounded = new BoundedSessionMap(maxEntries);
@@ -46,7 +18,6 @@ const capList = (caps) => Object.entries(caps ?? {})
46
18
  .map(([k]) => k);
47
19
  async function availableProfiles(catalog, viewer) {
48
20
  const { entries } = await catalog.list({ ...viewer, status: "published", latestOnly: true, limit: 100 });
49
- // tenant-neutral: profile name + boolean capability keys only (this list enters model context).
50
21
  return entries.map((e) => ({ profile: e.profile, capabilities: capList(e.capabilities) }));
51
22
  }
52
23
  export function selectEnvironmentTool(deps) {
@@ -58,7 +29,6 @@ export function selectEnvironmentTool(deps) {
58
29
  "The change takes effect for the NEXT task/subagent — the current sandbox is not swapped mid-run; " +
59
30
  "running work keeps the environment it started with, and artifacts hand over via the workspace/git. " +
60
31
  "Use this instead of hand-installing SDK-level toolchains (Android SDK, JVM, browsers, …).",
61
- // set-to-value on a session-keyed map: repeat-safe with the same args.
62
32
  effect: "idempotent",
63
33
  parameters: Type.Object({
64
34
  profile: Type.Optional(Type.String({ description: "Target image profile name (from the available list). Omit to discover." })),
@@ -83,7 +53,6 @@ export function selectEnvironmentTool(deps) {
83
53
  index: deps.catalog,
84
54
  });
85
55
  if (!resolved.ok) {
86
- // self-correction payload: the error + the viewer's own catalog. Model retries with a valid one.
87
56
  const available = await availableProfiles(deps.catalog, viewer);
88
57
  return {
89
58
  content: JSON.stringify({ error: resolved.message, ...(caps.length > 0 ? { missing: caps } : {}), available }),
@@ -91,7 +60,6 @@ export function selectEnvironmentTool(deps) {
91
60
  };
92
61
  }
93
62
  if (!sessionId) {
94
- // outside a Runner session there is nothing to bind to — honest failure, not a silent success.
95
63
  return { content: JSON.stringify({ error: "no session to bind the environment selection to" }), details: { mode: "select-failed" } };
96
64
  }
97
65
  deps.selection.set(sessionId, a.profile);