@sema-agent/server 7.125.1 → 7.126.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 (248) hide show
  1. package/MIGRATION.md +195 -2
  2. package/README.md +2 -2
  3. package/README.zh-CN.md +2 -2
  4. package/USAGE.md +263 -140
  5. package/deploy/sema-up/README.md +1 -1
  6. package/deploy/sema-up/chart/templates/networkpolicy-sandbox.yaml +47 -0
  7. package/deploy/sema-up/chart/templates/sandbox-rbac.yaml +5 -1
  8. package/deploy/sema-up/chart/templates/server-v4pro.yaml +2 -2
  9. package/deploy/sema-up/chart/templates/server.yaml +1 -1
  10. package/deploy/sema-up/chart/values.yaml +12 -0
  11. package/deploy/sema-up/cluster-up.sh +10 -10
  12. package/deploy/sema-up/kube-up.sh +11 -10
  13. package/deploy/sema-up/lib/dataroot.sh +1 -1
  14. package/deploy/sema-up/lib/envf.sh +5 -5
  15. package/deploy/sema-up/lib/flagshape.sh +5 -5
  16. package/deploy/sema-up/lib/lanes.py +1 -1
  17. package/deploy/sema-up/lib/parse-machines.py +2 -2
  18. package/deploy/sema-up/lib/shell-access.sh +1 -1
  19. package/deploy/sema-up/preflight.sh +4 -4
  20. package/deploy/sema-up/sema-up.sh +33 -33
  21. package/deploy/sema-up/smoke.sh +3 -3
  22. package/deploy/sema-up/stack/compose.yaml +1 -1
  23. package/deploy/sema-up/stack/sema-stack-cluster.yaml +6 -2
  24. package/dist/approval-card.d.ts +20 -4
  25. package/dist/approval-content-kind.d.ts +38 -22
  26. package/dist/approval-content-kind.js +20 -4
  27. package/dist/audit.js +2 -0
  28. package/dist/bench/s1/arms.d.ts +9 -37
  29. package/dist/bench/s1/arms.js +4 -61
  30. package/dist/bench/s1/live-deps.d.ts +4 -10
  31. package/dist/bench/s1/live-deps.js +1 -222
  32. package/dist/bench/s1/oracle.d.ts +1 -1
  33. package/dist/bench/s1/reviewer.d.ts +1 -1
  34. package/dist/bench/s1/row.d.ts +5 -7
  35. package/dist/bench/s1/row.js +0 -2
  36. package/dist/bench/s1/run-firm.d.ts +2 -2
  37. package/dist/bench/s1/run-firm.js +1 -4
  38. package/dist/bench/s1/runner-ctx.d.ts +17 -29
  39. package/dist/bench/s1/runner-ctx.js +3 -7
  40. package/dist/bench/s1/tasks.d.ts +9 -46
  41. package/dist/bench/s1/tasks.js +4 -63
  42. package/dist/boot/execution-env.d.ts +38 -26
  43. package/dist/boot/execution-env.js +71 -38
  44. package/dist/boot/memory-boundary.d.ts +1 -1
  45. package/dist/boot/parked-revive-gate.d.ts +0 -3
  46. package/dist/boot/parked-revive-gate.js +2 -2
  47. package/dist/boot/resolve-spec.d.ts +0 -2
  48. package/dist/boot/resolve-spec.js +12 -17
  49. package/dist/boot/retention-lane.d.ts +12 -1
  50. package/dist/boot/retention-lane.js +2 -0
  51. package/dist/boot/runner-deps.d.ts +5 -5
  52. package/dist/boot/runner-deps.js +5 -16
  53. package/dist/boot/session-faces.d.ts +5 -1
  54. package/dist/boot/session-faces.js +37 -1
  55. package/dist/boot/stage-03-store-backend.d.ts +1 -0
  56. package/dist/boot/stage-03-store-backend.js +2 -2
  57. package/dist/boot/stage-04-stores.d.ts +1 -0
  58. package/dist/boot/stage-05-execution-env.d.ts +2 -2
  59. package/dist/boot/stage-05-execution-env.js +7 -4
  60. package/dist/boot/stage-06-runners.d.ts +2 -3
  61. package/dist/boot/stage-06-runners.js +8 -7
  62. package/dist/boot/stage-07-capability-layer.d.ts +2 -3
  63. package/dist/boot/stage-07-capability-layer.js +3 -4
  64. package/dist/boot/stage-08-reapers.d.ts +2 -3
  65. package/dist/boot/stage-08-reapers.js +2 -2
  66. package/dist/boot/stage-09-leader.d.ts +2 -3
  67. package/dist/boot/stage-09-leader.js +2 -1
  68. package/dist/boot/stage-10-http-server.d.ts +2 -3
  69. package/dist/boot/stage-10-http-server.js +2 -2
  70. package/dist/boot/stores.d.ts +1 -0
  71. package/dist/boot/stores.js +4 -3
  72. package/dist/boot/web-admin.d.ts +5 -0
  73. package/dist/boot/web-admin.js +28 -14
  74. package/dist/brain.d.ts +2 -2
  75. package/dist/brain.js +1 -1
  76. package/dist/capabilities/builtin-tools.d.ts +2 -1
  77. package/dist/capabilities/composite-members.d.ts +1 -1
  78. package/dist/capabilities/composite-members.js +1 -1
  79. package/dist/capabilities/hands-lane.d.ts +9 -6
  80. package/dist/capabilities/hands-lane.js +2 -2
  81. package/dist/capabilities/memory-notice.d.ts +27 -22
  82. package/dist/capabilities/memory-notice.js +15 -17
  83. package/dist/capabilities/scenarios.d.ts +9 -14
  84. package/dist/capabilities/scenarios.js +5 -50
  85. package/dist/capability-facts.d.ts +22 -11
  86. package/dist/capability-facts.js +13 -6
  87. package/dist/config-catalog.js +11 -4
  88. package/dist/config-provider.d.ts +5 -1
  89. package/dist/config-provider.js +23 -4
  90. package/dist/config-types.d.ts +14 -1
  91. package/dist/config.js +39 -6
  92. package/dist/deployment-governance.d.ts +19 -21
  93. package/dist/deployment-governance.js +7 -8
  94. package/dist/env-name-allowlist-knobs.d.ts +5 -5
  95. package/dist/env-name-allowlist-knobs.js +1 -0
  96. package/dist/execution-lane-caps.js +2 -2
  97. package/dist/hooks/hook-runner.d.ts +84 -30
  98. package/dist/hooks/hook-runner.js +74 -41
  99. package/dist/http/admission.js +12 -9
  100. package/dist/http/durable-run-leg.d.ts +5 -4
  101. package/dist/http/durable-run-leg.js +1 -1
  102. package/dist/http/idempotency.d.ts +28 -6
  103. package/dist/http/idempotency.js +10 -2
  104. package/dist/http/principal-gate.d.ts +14 -0
  105. package/dist/http/principal-gate.js +7 -0
  106. package/dist/http/request-guards.d.ts +2 -1
  107. package/dist/http/request-guards.js +14 -14
  108. package/dist/http/resume-legs.d.ts +2 -2
  109. package/dist/http/resume-legs.js +5 -4
  110. package/dist/http/route-ctx.d.ts +8 -6
  111. package/dist/http/routes/a2a-serve.js +11 -7
  112. package/dist/http/routes/approvals-assistant.js +18 -3
  113. package/dist/http/routes/background.d.ts +1 -1
  114. package/dist/http/routes/background.js +1 -1
  115. package/dist/http/routes/capabilities.js +10 -2
  116. package/dist/http/routes/mcp-reconnect.js +1 -1
  117. package/dist/http/routes/runs.d.ts +4 -3
  118. package/dist/http/routes/runs.js +12 -13
  119. package/dist/http/routes/session-sync.js +14 -11
  120. package/dist/http/routes/sessions.js +9 -9
  121. package/dist/http/routes/side-query.js +6 -19
  122. package/dist/http/routes/tasks.js +12 -11
  123. package/dist/http/server.d.ts +4 -0
  124. package/dist/http/server.js +2 -2
  125. package/dist/http/submit-replay.d.ts +15 -8
  126. package/dist/http/submit-replay.js +6 -0
  127. package/dist/http/wire-types.d.ts +7 -9
  128. package/dist/index.d.ts +0 -1
  129. package/dist/index.js +0 -1
  130. package/dist/internal-run-env.d.ts +30 -0
  131. package/dist/internal-run-env.js +11 -0
  132. package/dist/leader/wire.js +3 -2
  133. package/dist/memory-scope.d.ts +19 -7
  134. package/dist/memory-scope.js +25 -8
  135. package/dist/observability/fail-open.d.ts +15 -3
  136. package/dist/observability/fail-open.js +15 -3
  137. package/dist/observability/metrics.d.ts +0 -1
  138. package/dist/observability/metrics.js +0 -3
  139. package/dist/observability/run-terminal.js +69 -4
  140. package/dist/permission-rule-vocab.d.ts +3 -2
  141. package/dist/permission-rule-vocab.js +1 -1
  142. package/dist/plan-cache-probe.d.ts +3 -2
  143. package/dist/plugins/approval-ask-store-sql.d.ts +1 -1
  144. package/dist/plugins/caching-session-store.d.ts +3 -0
  145. package/dist/plugins/caching-session-store.js +3 -0
  146. package/dist/plugins/checkpoint-store-sql.d.ts +42 -28
  147. package/dist/plugins/checkpoint-store-sql.js +57 -11
  148. package/dist/plugins/file-run-store.d.ts +25 -21
  149. package/dist/plugins/file-run-store.js +70 -34
  150. package/dist/plugins/local-checkpoint-store.d.ts +8 -5
  151. package/dist/plugins/local-checkpoint-store.js +5 -15
  152. package/dist/plugins/local-session-store.d.ts +66 -7
  153. package/dist/plugins/local-session-store.js +168 -101
  154. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +1 -1
  155. package/dist/plugins/memory-run-store.d.ts +19 -3
  156. package/dist/plugins/memory-run-store.js +38 -3
  157. package/dist/plugins/one-time-migrations.d.ts +141 -39
  158. package/dist/plugins/one-time-migrations.js +32 -143
  159. package/dist/plugins/pg-pool.js +11 -1
  160. package/dist/plugins/pg-session-storage.d.ts +2 -0
  161. package/dist/plugins/pg-session-storage.js +9 -5
  162. package/dist/plugins/remote-env-adb.d.ts +3 -0
  163. package/dist/plugins/remote-env-adb.js +1 -0
  164. package/dist/plugins/remote-env-device.d.ts +3 -0
  165. package/dist/plugins/remote-env-device.js +1 -0
  166. package/dist/plugins/remote-env-e2b.d.ts +4 -0
  167. package/dist/plugins/remote-env-e2b.js +2 -1
  168. package/dist/plugins/remote-env-host.d.ts +12 -0
  169. package/dist/plugins/remote-env-host.js +11 -1
  170. package/dist/plugins/remote-env-k8s.d.ts +227 -39
  171. package/dist/plugins/remote-env-k8s.js +450 -75
  172. package/dist/plugins/remote-env-local-docker.d.ts +4 -0
  173. package/dist/plugins/remote-env-local-docker.js +2 -1
  174. package/dist/plugins/remote-env-ssh.d.ts +3 -0
  175. package/dist/plugins/remote-env-ssh.js +1 -0
  176. package/dist/plugins/retention-store-sql.d.ts +4 -1
  177. package/dist/plugins/retention-store-sql.js +11 -9
  178. package/dist/plugins/run-store-sql.d.ts +44 -6
  179. package/dist/plugins/run-store-sql.js +92 -27
  180. package/dist/plugins/session-capture-record-store-sql.d.ts +2 -3
  181. package/dist/plugins/sql-driver.d.ts +2 -1
  182. package/dist/plugins/store-backend.js +1 -1
  183. package/dist/plugins/store-contracts.d.ts +17 -3
  184. package/dist/plugins/tidb-pool.js +15 -3
  185. package/dist/plugins/tidb-session-storage.js +3 -2
  186. package/dist/plugins/tidb-session-store.d.ts +2 -0
  187. package/dist/plugins/tidb-session-store.js +6 -2
  188. package/dist/request-key-closure.d.ts +12 -0
  189. package/dist/request-key-closure.js +3 -3
  190. package/dist/router/route-orchestration.d.ts +3 -11
  191. package/dist/router/route-orchestration.js +0 -4
  192. package/dist/rule-entry-list.js +0 -4
  193. package/dist/rules-consent.js +3 -0
  194. package/dist/run-local.d.ts +5 -4
  195. package/dist/run-local.js +23 -17
  196. package/dist/runs.js +1 -0
  197. package/dist/security.d.ts +7 -0
  198. package/dist/security.js +5 -0
  199. package/dist/server-secret-env.d.ts +10 -4
  200. package/dist/server-secret-env.js +4 -1
  201. package/dist/session-incarnation.d.ts +46 -2
  202. package/dist/session-incarnation.js +14 -3
  203. package/dist/session-sync-kernel.d.ts +10 -3
  204. package/dist/spec-fields.d.ts +24 -8
  205. package/dist/spec-fields.js +21 -2
  206. package/dist/task-workflow.d.ts +6 -4
  207. package/dist/task-workflow.js +3 -2
  208. package/dist/tool-approval.d.ts +78 -6
  209. package/dist/tool-approval.js +7 -3
  210. package/dist/trace/core-keyset-guard.d.ts +4 -4
  211. package/dist/trace/engine-notice-wire.d.ts +26 -1
  212. package/dist/trace/engine-notice-wire.js +7 -2
  213. package/dist/trace/project.d.ts +8 -3
  214. package/dist/trace/project.js +15 -1
  215. package/dist/trace/self-minted-wire.d.ts +104 -2
  216. package/dist/trace/self-minted-wire.js +35 -0
  217. package/dist/web-admin-client/apply-effective.d.ts +13 -7
  218. package/dist/web-admin-client/apply-effective.js +11 -26
  219. package/dist/web-admin-client/apply-ledger.d.ts +2 -2
  220. package/dist/web-admin-client/candidate-gate.d.ts +39 -4
  221. package/dist/web-admin-client/candidate-gate.js +9 -2
  222. package/dist/web-admin-client/facade.d.ts +2 -2
  223. package/dist/web-admin-client/http-client.d.ts +3 -1
  224. package/dist/web-admin-client/http-client.js +4 -3
  225. package/dist/web-admin-client/read-warnings.d.ts +33 -5
  226. package/dist/web-admin-client/read-warnings.js +41 -6
  227. package/dist/web-admin-client/types.d.ts +11 -5
  228. package/dist/workspace-cache.d.ts +45 -0
  229. package/dist/workspace-cache.js +30 -0
  230. package/dist/workspace-space-commit.d.ts +116 -0
  231. package/dist/workspace-space-commit.js +117 -0
  232. package/dist/workspace-space.d.ts +75 -9
  233. package/dist/workspace-space.js +52 -1
  234. package/package.json +4 -4
  235. package/dist/boot/session-incarnation-seat.d.ts +0 -41
  236. package/dist/boot/session-incarnation-seat.js +0 -69
  237. package/dist/capabilities/code-review-council.d.ts +0 -49
  238. package/dist/capabilities/code-review-council.js +0 -111
  239. package/dist/capabilities/collab-wire.d.ts +0 -36
  240. package/dist/capabilities/collab-wire.js +0 -2
  241. package/dist/capabilities/collab-workflows.d.ts +0 -47
  242. package/dist/capabilities/collab-workflows.js +0 -90
  243. package/dist/capabilities/prompts/code-review.d.ts +0 -25
  244. package/dist/capabilities/prompts/code-review.js +0 -39
  245. package/dist/capabilities/prompts/team.d.ts +0 -10
  246. package/dist/capabilities/prompts/team.js +0 -6
  247. package/dist/capabilities/team.d.ts +0 -113
  248. package/dist/capabilities/team.js +0 -70
package/dist/brain.d.ts CHANGED
@@ -108,8 +108,8 @@ export interface DegradeRouteWatch {
108
108
  *
109
109
  * 缺省下首字节段不铸键(core 自缺省当家,[ref])、`0` = 该段不设引擎钟;而切备只发生在一次调用**失败之后**。于是主网关接了连接
110
110
  * 却不回响应头时:缺省 ⇒ 要等 core 的首字节 lane 走完(缺省首窗 + 一次加宽重试;本仓不铸 `firstByteRetries`,S-753 车PA)才切备,
111
- * 时间预算短于那一段的调用在备网关被尝试之前就被取消 —— 以 core 7.33.x 缺省计 lane ≈ 15 min,code-review council 每个镜头的 180 s 与 team
112
- * discuss 的 600 s 都在其内(车PA codex r1:600 s 在主网关的加宽重试窗里到点,不是「只剩余量」);
111
+ * 时间预算短于那一段的调用在备网关被尝试之前就被取消 —— 以 core 7.33.x 缺省计 lane ≈ 15 min,一次带短时间预算的调用
112
+ * (分钟级的工具 / 子任务预算)在主网关的加宽重试窗里就到点,不是「只剩余量」;
113
113
  * `0` ⇒ 首字节 lane 永不切备。启动日志里同行的 `gatewayRetryCap:"fast-fail:…"` 只管共享重试梯子,读起来却像「会快切」—— 这条披露把它补齐。
114
114
  * 显式设了正数窗 ⇒ `undefined`(部署已表态);单路由 ⇒ `undefined`(没有备可切)。**不替运维铸窗**:出路是旋钮,不是本仓的数。
115
115
  */
package/dist/brain.js CHANGED
@@ -193,7 +193,7 @@ export function buildFailoverFirstByteNotice(config) {
193
193
  firstByteTimeoutMs: r.firstByteTimeoutMs ?? null,
194
194
  effect: zero
195
195
  ? "the first-byte lane never fails over a primary that accepts the connection but sends no response headers (0 = no engine clock on that segment); only a run-level limit ends that wait"
196
- : "a primary that accepts the connection but sends no response headers is failed over only after core's default first-byte lane runs out (the first window, then one widened retry); a call with a time budget shorter than that — the code-review council's 180 s lenses and team discuss's 600 s, with core 7.33.x defaults — is cancelled before the backup is ever tried",
196
+ : "a primary that accepts the connection but sends no response headers is failed over only after core's default first-byte lane runs out (the first window, then one widened retry); a call with a time budget shorter than that (minutes, with core 7.33.x defaults) is cancelled before the backup is ever tried",
197
197
  fix: "set MODEL_FIRST_BYTE_TIMEOUT_MS to the longest response-header wait a healthy primary needs (queue + prefill), so a hung primary is cut over after that window and its one widened retry",
198
198
  };
199
199
  }
@@ -27,7 +27,8 @@ export interface SubagentExtraToolOptions {
27
27
  *
28
28
  * core 7.46.0 #1432 —— **工厂与名册一个来源**:返回委派工具的两键,`extraTools`(每次 spawn 交出 `forSpawn` 选中的那几只)与
29
29
  * `extraToolNames`(全部可能交出的那几只的名字 / 别名 / 内容来源,从同一批 `ToolSpec` 读出)。名册在场 ⇒ 引擎重新能列出子代的工具池:
30
- * 定义级 / 工具级 / `TaskSpec.agents` 清单里的退役拼法与大小写孪生回到开跑前拒(`config.agent.unknown_tool`),记忆内容来源读者按声明读这几只。
30
+ * 定义级 / 工具级 / `TaskSpec.agents` 清单里没有任何工具答应的名字(core 7.49.0 #1623 起含无人挂的名;退役拼法与大小写孪生 7.46.0 起)开跑前拒
31
+ * (`config.agent.unknown_tool`),记忆内容来源读者按声明读这几只。
31
32
  * 修前(core 7.32.0–7.45.x)工厂恒在场 ⇒ 池不可静态知 ⇒ 那一族拼法只告不拒、委派结果一律按「外部」读。
32
33
  * 工厂**不抛**:各组的 `tools` 是装配期就建好的对象;部署 `forSpawn` 的抛由 core 收成「这一 spawn 什么都不继承 + 模型面披露句」(onExtraToolsError 记 warn)。
33
34
  * 一种拼法出现在两只工具上(部署工具与 `Now` 撞名 / 别名撞)⇒ core 在建委派工具处抛 `config.extra_tool_names`(`shape`),不静默取其一。
@@ -53,7 +53,7 @@ export declare const COMPOSITE_CAPABILITY_MEMBERS: {
53
53
  readonly retention: readonly ["mode", "maxAgeDays"];
54
54
  readonly workflowsGate: readonly ["engineCan", "denial"];
55
55
  readonly sealedKey: readonly ["alg", "publicKeyId", "publicKey"];
56
- readonly workspaceSync: readonly ["persist", "keep"];
56
+ readonly workspaceSync: readonly ["persist", "keep", "timeoutMs", "cache"];
57
57
  };
58
58
  type CompositeTable = typeof COMPOSITE_CAPABILITY_MEMBERS;
59
59
  /** 值的**具名成员对象臂**(标量 / 字面词 / `false` / `null` / `undefined` / 数组 / 函数臂剥掉)。 */
@@ -14,7 +14,7 @@ export const COMPOSITE_CAPABILITY_MEMBERS = {
14
14
  retention: ["mode", "maxAgeDays"],
15
15
  workflowsGate: ["engineCan", "denial"],
16
16
  sealedKey: ["alg", "publicKeyId", "publicKey"],
17
- workspaceSync: ["persist", "keep"],
17
+ workspaceSync: ["persist", "keep", "timeoutMs", "cache"],
18
18
  };
19
19
  export function gateCompositeMembers(body) {
20
20
  return body;
@@ -4,8 +4,8 @@
4
4
  * 根本问题(设计小票 §1,案源 [ref]/[ref]/[ref]):core 的手带工具面是 **per-Runner 表态**
5
5
  * (`prepare-task`:`handsEnabled = ownedEnv || deps.executionEnv`,ownedEnv 只要挂了
6
6
  * `executionEnvFactory` 就每任务必铸)。server 此前全场景共享一只 runner + 一只 subRunner,
7
- * 于是任何 REMOTE_EXEC 部署里,声明 clone-free 的 `scan`、只有 `run_council` 的评审团、
8
- * 零工具人格的 team 成员,最终 roster 都被并集进全量可写手带(Bash/Edit/Write/…)。
7
+ * 于是任何 REMOTE_EXEC 部署里,声明 clone-free 的 `scan` / `code-review`(当年还有评审团与讨论成员两种零工具人格),
8
+ * 最终 roster 都被并集进全量可写手带(Bash/Edit/Write/…)。
9
9
  *
10
10
  * 本模块提供三件:
11
11
  * ① {@link ScenarioHands} 判别位 + {@link pickHandsRunner} 的**穷举** switch —— 新场景不表态 = 编译红,
@@ -19,14 +19,17 @@ import { type Runner, type TaskSpec, type ToolPolicy } from "@sema-agent/core";
19
19
  /** 场景对手带工具面的表态(闭集)。`full` = core 手带 band 照常 mount;`none` = 不挂 executionEnvFactory 的 Runner。 */
20
20
  export type ScenarioHands = "full" | "none";
21
21
  /**
22
- * 无手孪生 Runner 的 deps 构造:把 `executionEnvFactory` **摘键**(不是置 undefined)。
22
+ * 无手孪生 Runner 的 deps 构造:把 `executionEnvFactory` **与 `lspManager`** 一起**摘键**(不是置 undefined)。
23
23
  * 摘键让「这只 Runner 没有手」在 deps 字面量上自证;置 undefined 留下一个值为 undefined 的键,
24
24
  * 与 core 现在的 `deps.executionEnvFactory ||` 判读结果相同,但把正确性寄托在真值判断上。
25
+ * **无手就无 LSP**:LSP 管理器是手的附属(宿主车道的 `NodeLspManager` 经 `defaultLspReadText` 直读本地盘,
26
+ * 没有读面围栏;沙箱车道推断降级为 unavailable)—— 只摘手不摘它,无手 Runner 会在 StubExecutionEnv 上挂 LSP。
25
27
  */
26
- export declare function withoutExecutionEnv<T extends {
28
+ export declare function withoutHands<T extends {
27
29
  executionEnvFactory?: unknown;
28
- }>(deps: T): Omit<T, "executionEnvFactory">;
29
- /** 一对 Runner:同 deps,唯一差别是 `handsless` 不挂 `executionEnvFactory`。 */
30
+ lspManager?: unknown;
31
+ }>(deps: T): Omit<T, "executionEnvFactory" | "lspManager">;
32
+ /** 一对 Runner:同 deps,唯一差别是 `handsless` 不挂 `executionEnvFactory` 与 `lspManager`。 */
30
33
  export interface HandsRunnerPair {
31
34
  full: Runner;
32
35
  handsless: Runner;
@@ -1,7 +1,7 @@
1
1
  import { handBandEffects, judgeRuleToolName, combinePolicies, createAllowDenyPolicy } from "@sema-agent/core";
2
2
  import { NO_DECLARED_TOOL_NAMES } from "../namespaced-rule-name.js";
3
- export function withoutExecutionEnv(deps) {
4
- const { executionEnvFactory: _handsDropped, ...rest } = deps;
3
+ export function withoutHands(deps) {
4
+ const { executionEnvFactory: _handsDropped, lspManager: _lspDropped, ...rest } = deps;
5
5
  return rest;
6
6
  }
7
7
  export function pickHandsRunner(hands, pair) {
@@ -13,10 +13,11 @@
13
13
  * ⚠️ 两条边界,越界即双重注入/假话:
14
14
  * · **engine 在场就不是我们的座位**。哪怕 `memoryWrite:false`(writeScope null)——那恰恰是引擎自己
15
15
  * 挂只读披露的状态,server 再叠一句口径不同的「你没有持久记忆」= 同一件事两种说法同框。
16
- * · **推断到「有落盘路」就闭嘴**。判别式与 core 的 `rosterCanPersist` 同口径(已知落盘路 = 挂载且未
17
- * 排除的文件写工具 / 可写 shell)。⚠️ 名单确实要读(codex 复审 [medium] 之后加的 `excludeTools` 一位:
18
- * roster 把落盘路**整批**卸掉时,手带在场也等于没有写通道),但负控仍然成立:必须**全部**已知落盘路
19
- * 都不在才算断链 —— `excludeTools:["Write"]` 不算(Bash/Edit 还在),请求面摘一个工具名改不动结论。
16
+ * · **推断到「有落盘路」就闭嘴**。判别式与 core 的 `rosterCanPersist` 同口径(已知落盘路 = 这一 run 名册上
17
+ * 真挂着的文件写工具 / 可写 shell)。「名册上还剩什么」**只读引擎的答案**:core 7.49.0(#1623)起一 run 的可用工具是
18
+ * 引擎一只选择器(`allowTools` ∩ … − `excludeTools`,别名 / `Tool(content)` / `mcp__srv…` 都由它读)在 prepare 算出的,
19
+ * 本模块不再读请求的任何一张工具名表 —— 名册臂在装配时刻读 provider 拿到的 `ctx.tools`(选择器之后的名册)。
20
+ * 负控仍然成立:必须**全部**已知落盘路都不在名册上才算断链(摘掉 `Write`,Bash / Edit 还在)。
20
21
  */
21
22
  import { type PromptProvider, type ToolCatalogEntry } from "@sema-agent/core";
22
23
  import type { ScenarioHands } from "./hands-lane.js";
@@ -25,8 +26,8 @@ import { type ExecutionLane } from "../execution-lane-caps.js";
25
26
  * 「已知落盘路」的判据,作用在 core **静态工具目录**的一行上(S-503 / [ref])—— 与 core 自己的
26
27
  * `rosterCanPersist` 推断逐条同形,取的是同一批目录属性,不是它结论的抄件:
27
28
  * · `mountedBy` 落在 core 的 `HANDS_BAND_TAGS` 里 —— 手带 band 之外的定义(记忆面工具、调度族)
28
- * 不在本判别式说得着的面上:本判别式的输入全是**手带事实**(`hands` / `handsReadOnly` /
29
- * `excludeTools`),把一只 band 外的写工具算进来,就是拿一条根本没挂的通道去撤回披露(fail-open);
29
+ * 不在本判别式说得着的面上:本判别式的输入全是**手带事实**(`hands` / `handsReadOnly` / 名册上的手带名),
30
+ * 把一只 band 外的写工具算进来,就是拿一条根本没挂的通道去撤回披露(fail-open);
30
31
  * · `face.effect !== "read"`(缺席读 `write`,与 core 同缺省)—— 只读 shell 壳在这里出局,它与全壳
31
32
  * 共用 `Bash` 这个 wire 名,靠的正是这一位分开;
32
33
  * · `face.pathTarget.access !== "read"`(文件写工具)**或** `face.family === "shell"`(可写 shell)。
@@ -44,8 +45,8 @@ export declare function persistencePathToolNamesOf(entries: readonly ToolCatalog
44
45
  * 「已知落盘路」的工具名闭集 —— **由 core 的静态工具目录派生**,本仓不再存第二份名表
45
46
  * (S-503 / [ref];改前是手抄的四名表 Write / Edit / NotebookEdit / Bash,没有任何差集门)。
46
47
  *
47
- * 为什么手抄在这一位上特别危险:core 改名/加一只写工具,手抄表静默失配成「没有一个写工具被排除」
48
- * (fail-open 方向)—— `excludeTools` 把真正的落盘路全排掉了,而我们对着一张旧名单答「还剩」。
48
+ * 为什么手抄在这一位上特别危险:core 改名/加一只写工具,手抄表静默失配(fail-open 方向)—— 名册上真正的
49
+ * 落盘路已经全不在了,而我们对着一张旧名单答「还剩」。
49
50
  * 判据的属主现在是 {@link persistencePathToolNamesOf},值的属主是 core 的目录行。
50
51
  */
51
52
  export declare const KNOWN_PERSISTENCE_TOOL_NAMES: readonly string[];
@@ -69,15 +70,6 @@ export interface MemoryFaceFacts {
69
70
  hands: ScenarioHands;
70
71
  /** 折完 governance+settings 后的最终 `handsReadOnly`(`AUTONOMY=read-only` 收窄;plan 模式不点亮它 —— plan 的手带照挂、调用时由引擎问)。手带只剩读腿。 */
71
72
  handsReadOnly: boolean;
72
- /** 折完之后的最终 `TaskSpec.excludeAllTools`(S-493;core 7.8.0 [ref])。`true` = **整只**工具面卸空
73
- * (core `prepareDeferClassify` 对它直接 `roster.unmountWhere(() => true)`)⇒ 手带在场也一条落盘路都不剩。
74
- * 🔴 它与下面那张名单是**同一个语义面**(「哪些工具不在 roster 上」)的两种声明,判据合成一只
75
- * {@link isUnmounted} —— 上游同版的姊妹谓词 `fullShellReachable` 也正是把 `spec.excludeAllTools !== true`
76
- * 当一个合取项(`prepare-inherited-gate.js` 亲读),本仓与它同口径。 */
77
- excludeAllTools: boolean;
78
- /** 折完之后的最终 `TaskSpec.excludeTools`(roster 真卸载,不是延迟披露)。**全部**已知落盘路都被排掉
79
- * ⇒ 手带在场也等于没有写通道 —— core 的 `rosterCanPersist` 也是按排除后的 roster 算的,这里跟上。 */
80
- excludeTools: readonly string[] | undefined;
81
73
  /**
82
74
  * 本部署的手带**挂没挂**(= core 拿没拿到执行环境;能力表 `coreRemoteExecutionEnv` 位,经
83
75
  * {@link buildNoticeLaneFacts} 取)。`false` = `in-process`:core 落 `StubExecutionEnv`,手带整族不 mount
@@ -103,18 +95,31 @@ export interface MemoryFaceFacts {
103
95
  * 不在调用点手写 `config.remoteExec !== undefined`(门 D R4)。
104
96
  */
105
97
  export declare function buildNoticeLaneFacts(lane: ExecutionLane): Pick<MemoryFaceFacts, "handsWired" | "filePlaneRemote">;
98
+ /**
99
+ * 披露的三种结局。`"roster"` = 部署事实答不完,剩下的问题(「名册上还剩不剩已知落盘路」)只有引擎在 prepare
100
+ * 之后知道 —— 由 {@link withNoPersistentMemoryNotice} 在装配时刻读 provider 拿到的名册作答,不在 resolveSpec 期猜。
101
+ */
102
+ export type NoPersistentMemoryNotice = "disclose" | "silent" | "roster";
106
103
  /**
107
104
  * 该不该把 `NO_PERSISTENT_MEMORY_NOTICE` 组进本次任务的 system prompt。
108
105
  *
109
106
  * 优先序(每一条都有独立理由,不是同一条判据的三种写法):
110
- * ① engine 在场 ⇒ **否**(引擎的座位,见文件头注);
111
- * ② 部署显式声明(或 host 腿装配层自动声明)⇒ 声明说了算(`true` 撤回 / `false` 强制)—— operator 的部署
107
+ * ① engine 在场 ⇒ `silent`(引擎的座位,见文件头注);
108
+ * ② 部署显式声明(或 host 腿装配层自动声明)⇒ 声明说了算(`true` ⇒ `silent` / `false` ⇒ `disclose`)—— operator 的部署
112
109
  * 事实压过我们的推断,与 core 对 `memoryPersistenceCapable` 的处置同向;
113
- * ③ 否则推断:有已知落盘路(手带挂上、场景不摘手、不是只读、文件平面在本机、且没被卸载决定卸光)⇒ 否;没有 ⇒ 是。
110
+ * ③ 否则推断:手带没挂 / 场景摘手 / 只读 / 文件平面不在本机 ⇒ `disclose`;都不是 ⇒ `roster`(名册上还剩不剩已知落盘路,
111
+ * 读引擎选择器之后的名册 —— 本模块不读请求的任何工具名表,core 7.49.0 #1623 起那是引擎一只运算的输入)。
114
112
  * 🔴 S-602 只改臂③ 里「车道」那一段(`handsWired` / `filePlaneRemote`),①② 的优先序一字不动 —— 「in-process ⇒
115
113
  * 披露」只在这两臂**之后**成立(挂了记忆面、或声明了 capable 的 in-process 部署照旧不披露)。
116
114
  */
117
- export declare function shouldDiscloseNoPersistentMemory(facts: MemoryFaceFacts): boolean;
115
+ export declare function noPersistentMemoryNotice(facts: MemoryFaceFacts): NoPersistentMemoryNotice;
116
+ /**
117
+ * 名册臂:引擎选择器之后的名册上还剩不剩已知落盘路(`KNOWN_PERSISTENCE_TOOL_NAMES` 任一名在册)。⚠️ 负控:摘掉**一个**
118
+ * `Write` 不算断链(Bash / Edit 还在)—— 必须**全部**已知落盘路都不在名册上。名册为空(`excludeAllTools`)⇒ 一条都不剩。
119
+ */
120
+ export declare function rosterLeavesPersistencePath(tools: readonly {
121
+ readonly name: string;
122
+ }[]): boolean;
118
123
  /**
119
124
  * 把披露段组进一个 prompt provider,**按 provider 自己的钩子形分腿**(换形会改段身份与 append 座位):
120
125
  * · typed(`stableBlocks`)⇒ {@link mountNotice}(普通形追加 behavior 声明;已组装 identity 形接在其文本后)。
@@ -129,5 +134,5 @@ export declare function shouldDiscloseNoPersistentMemory(facts: MemoryFaceFacts)
129
134
  * 走 typed 声明腿就没有这条耦合:role 层仍由 core 按 `userSystemPrompt ?? DEFAULT_SYSTEM_PROMPT` 自己铸,
130
135
  * 我方只贡献一条 behavior 段,`providerDropsAppend` 的判决包装前后逐格相同。
131
136
  */
132
- export declare function withNoPersistentMemoryNotice(provider: PromptProvider | undefined): PromptProvider;
137
+ export declare function withNoPersistentMemoryNotice(provider: PromptProvider | undefined, when: Exclude<NoPersistentMemoryNotice, "silent">): PromptProvider;
133
138
  //# sourceMappingURL=memory-notice.d.ts.map
@@ -26,24 +26,21 @@ export function buildNoticeLaneFacts(lane) {
26
26
  const caps = executionLaneCaps(lane);
27
27
  return { handsWired: caps.coreRemoteExecutionEnv === "supported", filePlaneRemote: caps.hostFilePlane !== "supported" };
28
28
  }
29
- function isUnmounted(facts, name) {
30
- return facts.excludeAllTools || (facts.excludeTools?.includes(name) ?? false);
31
- }
32
- function anyPersistenceToolSurvives(facts) {
33
- return KNOWN_PERSISTENCE_TOOL_NAMES.some((name) => !isUnmounted(facts, name));
34
- }
35
- export function shouldDiscloseNoPersistentMemory(facts) {
29
+ export function noPersistentMemoryNotice(facts) {
36
30
  if (facts.memoryFaceMounted)
37
- return false;
31
+ return "silent";
38
32
  if (facts.declaredCapable !== undefined)
39
- return !facts.declaredCapable;
33
+ return facts.declaredCapable ? "silent" : "disclose";
40
34
  if (facts.hands === "none" || facts.handsReadOnly)
41
- return true;
35
+ return "disclose";
42
36
  if (!facts.handsWired)
43
- return true;
37
+ return "disclose";
44
38
  if (facts.filePlaneRemote)
45
- return true;
46
- return !anyPersistenceToolSurvives(facts);
39
+ return "disclose";
40
+ return "roster";
41
+ }
42
+ export function rosterLeavesPersistencePath(tools) {
43
+ return tools.some((t) => KNOWN_PERSISTENCE_TOOL_NAMES.includes(t.name));
47
44
  }
48
45
  const NOTICE_BLOCK = { id: NO_PERSISTENT_MEMORY_SECTION_ID, slot: "behavior", text: NO_PERSISTENT_MEMORY_NOTICE };
49
46
  function mountNotice(decls) {
@@ -52,15 +49,16 @@ function mountNotice(decls) {
52
49
  return [...decls, NOTICE_BLOCK];
53
50
  return decls.map((d) => (d === assembledIdentity ? { id: d.id, slot: d.slot, text: `${d.text}\n\n${NO_PERSISTENT_MEMORY_NOTICE}` } : d));
54
51
  }
55
- export function withNoPersistentMemoryNotice(provider) {
52
+ export function withNoPersistentMemoryNotice(provider, when) {
53
+ const due = (ctx) => when === "disclose" || !rosterLeavesPersistencePath(ctx.tools);
56
54
  if (provider?.stableBlocks) {
57
55
  const blocks = provider.stableBlocks.bind(provider);
58
- return { ...provider, stableBlocks: (ctx) => mountNotice(blocks(ctx)) };
56
+ return { ...provider, stableBlocks: (ctx) => (due(ctx) ? mountNotice(blocks(ctx)) : blocks(ctx)) };
59
57
  }
60
58
  if (provider?.stableSystem) {
61
59
  const stable = provider.stableSystem.bind(provider);
62
- return { ...provider, stableSystem: (ctx) => `${stable(ctx)}\n\n${NO_PERSISTENT_MEMORY_NOTICE}` };
60
+ return { ...provider, stableSystem: (ctx) => (due(ctx) ? `${stable(ctx)}\n\n${NO_PERSISTENT_MEMORY_NOTICE}` : stable(ctx)) };
63
61
  }
64
- return { stableBlocks: () => [NOTICE_BLOCK] };
62
+ return { stableBlocks: (ctx) => (due(ctx) ? [NOTICE_BLOCK] : []) };
65
63
  }
66
64
  //# sourceMappingURL=memory-notice.js.map
@@ -1,10 +1,9 @@
1
1
  import { type WebFetchConfig, type BackgroundAgentStore, type CheckpointStore, type PromptProvider, type Runner, type SkillSpec, type TaskListStore, type ToolSpec, type WebSearchConfig } from "@sema-agent/core";
2
- import type { Metrics } from "../observability/metrics.js";
3
2
  import type { Logger } from "../observability/logger.js";
4
3
  import { type SubagentExtraToolSet } from "./builtin-tools.js";
5
4
  import { type RepoReadClient } from "./repo-tools.js";
6
5
  import { type LoadedSkill } from "./skills.js";
7
- import { type ScenarioHands } from "./hands-lane.js";
6
+ import type { ScenarioHands } from "./hands-lane.js";
8
7
  /**
9
8
  * Scenario routing — a deployment serves many scenarios from one image. Each request's `scenario`
10
9
  * selects a pre-wired capability bundle: tools (incl. subagent roster) + a whole-harness prompt +
@@ -53,15 +52,8 @@ export interface ScenarioContext {
53
52
  export type Scenario = (req: ScenarioRequest, principal?: string, ctx?: ScenarioContext) => ScenarioBundle;
54
53
  export interface ScenarioDeps {
55
54
  runner: Runner;
56
- /** In-memory runner for ephemeral sub-tasks (council lenses/arbiter) — keeps them out of TiDB. */
55
+ /** The runner a delegated (Agent-tool) child runs on — same deps as `runner`, hands included (CC parity). */
57
56
  subRunner: Runner;
58
- /**
59
- * [ref]:`subRunner` 的无手孪生 —— 同 deps,唯独不挂 `executionEnvFactory`。hands=none 的场景把它交给
60
- * 自己造的子任务工具(council 的 lens/arbiter、discuss 的成员/synthesizer):那些子任务只该读仓库/讨论,
61
- * 拿全量可写手是 [ref] 病灶里最意外的一支(零工具人格照拿 Bash)。取用一律走 {@link pickHandsRunner},
62
- * 不在场景体内手挑 —— 判别位是唯一开关。
63
- */
64
- handslessSubRunner: Runner;
65
57
  model: string;
66
58
  skills: LoadedSkill[];
67
59
  repoClient?: RepoReadClient;
@@ -77,12 +69,11 @@ export interface ScenarioDeps {
77
69
  * 模型摘要而非整页灌上下文([ref] L1 的 ~660× 放大就是这缝);缺席 ⇒ `webFetch` 键不铸(修前
78
70
  * 字节形,core 落「summarization unavailable」诚实兜底)。 */
79
71
  webFetchSummarize?: NonNullable<NonNullable<WebFetchConfig["summarize"]>>;
80
- metrics?: Metrics;
81
72
  logger?: Logger;
82
73
  /**
83
74
  * When true, the `default` scenario carries the Sema product identity (see {@link semaIdentityProvider}).
84
75
  * Set by the local/TOC deployment (CONFIG_PROVIDER=local / run-local) so the local engine answers "who
85
- * are you" as Sema instead of the model's hallucinated identity. Business scenarios (discuss/code-review)
76
+ * are you" as Sema instead of the model's hallucinated identity. Business scenarios (code-review)
86
77
  * keep their own prompts; cloud (CONFIG_PROVIDER≠local) leaves the default scenario neutral unless turned
87
78
  * on, so integrators that inject their own persona are unaffected.
88
79
  */
@@ -205,9 +196,13 @@ export interface ScenarioDetail {
205
196
  /** 机读原因键;仅 `available:false` 时在场(缺席=没有理由)。消费方按键分支,禁匹配英文文案。 */
206
197
  unavailableReason?: ScenarioUnavailableReason;
207
198
  }
208
- /** 内建五场景详情。default/code/discuss 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan 是
199
+ /** 内建四场景详情。default/code 工厂对良性请求无副作用可真调(拿真实工具名单);code-review/scan 是
209
200
  * fail-loud 语义(缺 GIT_API 配置/principal 即 throw)→ 探针失败落静态表兜底(表↔工厂一致性由测试锁:
210
201
  * 测试喂 fake deps 真调工厂对账工具名)。enabled 对内建恒 true(约定②)。 */
202
+ /** 内建场景名的闭集(`GET /v1/capabilities.scenarios` 与场景详情口的内建成员)。{@link builtinScenarioDetails} 的返回型按它键控 ⇒ 加 / 删一个内建场景而
203
+ * 这里没跟 = 编译红;wire 变更清单的自铸登记表(`trace/self-minted-wire.ts` 的 `builtinScenario`)同按它 `satisfies`。 */
204
+ export declare const BUILTIN_SCENARIO_NAMES: readonly ["code", "default", "code-review", "scan"];
205
+ export type BuiltinScenarioName = (typeof BUILTIN_SCENARIO_NAMES)[number];
211
206
  export declare function builtinScenarioDetails(scenarios: Record<string, Scenario>, deps: Pick<ScenarioDeps, "brandIdentity" | "repoClient">): Record<string, ScenarioDetail>;
212
207
  /** center 条目详情(有效性判定与 centerScenarios 完全同款:无效条目既不进 overlay 也不进详情——
213
208
  * 保证约定①「详情显示的来源=运行实际用的定义」永不错位)。 */
@@ -236,7 +231,7 @@ export type ScenarioAvailability = {
236
231
  *
237
232
  * 判据键 = **toolset**:内建详情与 center 条目都带这个字段,故两条产线天然共用同一份判据。
238
233
  * `requiresRepo` 的 toolset 需要部署配好 git 后端(`GIT_API_BASEURL` ⇒ `deps.repoClient`)。
239
- * 词表外的 toolset(内建的 `full-body`/`discuss`)不依赖后端 ⇒ 恒可用。
234
+ * 词表外的 toolset(内建的 `full-body`)不依赖后端 ⇒ 恒可用。
240
235
  */
241
236
  export declare function scenarioAvailability(deps: Pick<ScenarioDeps, "repoClient">, toolset: string): ScenarioAvailability;
242
237
  /** 拒绝文案的唯一属主:闭集穷举 switch(新增原因词不在这里表态即编译红)。 */
@@ -2,12 +2,9 @@ import { CODE_AGENT_PROMPT, CODE_SYSTEM_PROMPT, DEFAULT_SYSTEM_PROMPT, WEB_FETCH
2
2
  import { HttpError, createCodedHttpError } from "../security.js";
3
3
  import { createSubagentExtraTools, extraToolsErrorFields, nowTool } from "./builtin-tools.js";
4
4
  import { parseRepo, repoToolsFor } from "./repo-tools.js";
5
- import { createCouncilTool, clampRounds } from "./code-review-council.js";
6
5
  import { stablePrompt } from "./prompt.js";
7
- import { createDiscussionTools, discussionCoordinatorPrompt, getTeam, teamNames } from "./team.js";
8
6
  import { createWebSearchBackend, judgeRequestWebSearch } from "../plugins/web-search.js";
9
7
  import { skillsForScenario } from "./skills.js";
10
- import { pickHandsRunner } from "./hands-lane.js";
11
8
  export { SEMA_IDENTITY } from "./prompts/identity.js";
12
9
  import { SEMA_IDENTITY } from "./prompts/identity.js";
13
10
  export function semaIdentityProvider() {
@@ -28,9 +25,6 @@ export function codePromptProvider(brand) {
28
25
  ],
29
26
  };
30
27
  }
31
- function subRunnerFor(deps, hands) {
32
- return pickHandsRunner(hands, { full: deps.subRunner, handsless: deps.handslessSubRunner });
33
- }
34
28
  function requestWebSearchBackend(verdict, deployment, logger) {
35
29
  switch (verdict.kind) {
36
30
  case "absent":
@@ -61,7 +55,7 @@ export function buildScenarios(deps) {
61
55
  ? { taskListStore: deps.taskListStoreFor(ctx.sessionId) }
62
56
  : {}),
63
57
  subagent: {
64
- runner: subRunnerFor(deps, "full"),
58
+ runner: deps.subRunner,
65
59
  background: {
66
60
  registry: defaultTaskRegistry,
67
61
  owner: "unattributed",
@@ -101,22 +95,6 @@ export function buildScenarios(deps) {
101
95
  skills: skillsForScenario(deps.skills, "scan"),
102
96
  };
103
97
  },
104
- discuss: (req) => {
105
- const name = typeof req.team === "string" ? req.team : "default";
106
- const template = getTeam(name);
107
- if (!template) {
108
- throw new HttpError(404, `unknown discussion roster "${name}" (available: ${teamNames().join(", ")})`);
109
- }
110
- const objective = typeof req.objective === "string" ? req.objective : "Discuss the task.";
111
- const businessContext = typeof req.businessContext === "string" ? req.businessContext : undefined;
112
- const hands = "none";
113
- return {
114
- tools: createDiscussionTools(subRunnerFor(deps, hands), template, { objective, businessContext, ...(deps.logger ? { logger: deps.logger } : {}) }),
115
- hands,
116
- skills: skillsForScenario(deps.skills, "discuss"),
117
- promptProvider: discussionCoordinatorPrompt,
118
- };
119
- },
120
98
  };
121
99
  }
122
100
  export function selectScenario(scenarios, name) {
@@ -131,6 +109,7 @@ function availabilityFields(deps, toolset) {
131
109
  const verdict = scenarioAvailability(deps, toolset);
132
110
  return verdict.available ? { available: true } : { available: false, unavailableReason: verdict.reason };
133
111
  }
112
+ export const BUILTIN_SCENARIO_NAMES = ["code", "default", "code-review", "scan"];
134
113
  export function builtinScenarioDetails(scenarios, deps) {
135
114
  const probe = (name, req, principal, fallback) => {
136
115
  try {
@@ -145,15 +124,15 @@ export function builtinScenarioDetails(scenarios, deps) {
145
124
  name, source: "builtin", builtin: true, summary, toolset, tools, promptSummary, enabled: true,
146
125
  ...availabilityFields(deps, toolset),
147
126
  });
148
- return {
127
+ const details = {
149
128
  code: mk("code", "编码主场景([891] 出厂缺省;CC 编码 persona 蒸馏版,default 全量工具面;终验旋钮独立)", "full-body", probe("code", {}, undefined, ["Now"]), deps.brandIdentity
150
129
  ? "Sema 产品身份 + core 编码完全体提示词(CODE_AGENT persona+自治自检;CC 血统对表)"
151
130
  : "core 编码完全体提示词(CODE_AGENT persona+自治自检;CC 血统对表)"),
152
131
  default: mk("default", "通用/助理场景(域中性 persona,CC agent loop 全量工具面;[891] 起非出厂缺省,显式可选)", "full-body", probe("default", {}, undefined, ["Now"]), deps.brandIdentity ? "Sema 产品身份 + core 默认宪法(结构层由引擎合成)" : "core 默认宪法(中性,无场景附加 persona)"),
153
- "code-review": mk("code-review", "代码评审(clone-free 只读仓库工具;council=true 升多镜头评审团)", "repo-readonly", probe("code-review", { repo: "probe/probe" }, undefined, REPO_TOOLS), "评审员/评审团协调提示词(镜头分工+产出契约)"),
132
+ "code-review": mk("code-review", "代码评审(clone-free 只读仓库工具)", "repo-readonly", probe("code-review", { repo: "probe/probe" }, undefined, REPO_TOOLS), "评审员提示词(产出契约)"),
154
133
  scan: mk("scan", "中性只读仓库扫描(输出契约由 objective+skills 决定)", "repo-readonly", probe("scan", { repo: "probe/probe" }, undefined, REPO_TOOLS), "无场景附加提示词(objective+center skills 拥有输出契约)"),
155
- discuss: mk("discuss", "多智能体讨论圆桌(模板成员+synthesizer,模板可由 registry 配置)", "discuss", probe("discuss", {}, undefined, ["run_discussion"]), "讨论协调者提示词(圆桌纪律+合成流程)"),
156
134
  };
135
+ return details;
157
136
  }
158
137
  export function centerScenarioDetails(specs, builtinNames, deps) {
159
138
  const out = {};
@@ -249,28 +228,7 @@ function codeReview(deps, req) {
249
228
  const coords = parseRepo(req.repo);
250
229
  const repoTools = repoToolsFor(repoClient, coords);
251
230
  const skills = skillsForScenario(deps.skills, "code-review");
252
- const objective = typeof req.objective === "string" ? req.objective : "Review this repository.";
253
231
  const hands = "none";
254
- if (req.lenses !== undefined && typeof req.lenses !== "number") {
255
- 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" });
256
- }
257
- if (req.council === true) {
258
- const rounds = clampRounds(req.rounds);
259
- return {
260
- tools: [
261
- createCouncilTool(subRunnerFor(deps, hands), repoTools, objective, {
262
- metrics: deps.metrics,
263
- logger: deps.logger,
264
- debate: req.debate === true,
265
- ...(typeof req.lenses === "number" ? { lensCount: req.lenses } : {}),
266
- ...(rounds !== undefined ? { rounds } : {}),
267
- }),
268
- ],
269
- hands,
270
- skills,
271
- promptProvider: coordinatorPrompt,
272
- };
273
- }
274
232
  return { tools: [...repoTools, nowTool()], hands, skills, promptProvider: directReviewerPrompt };
275
233
  }
276
234
  const reviewBody = "Produce ONE prioritized review: a short summary, then findings grouped by severity " +
@@ -278,9 +236,6 @@ const reviewBody = "Produce ONE prioritized review: a short summary, then findin
278
236
  const directReviewerPrompt = stablePrompt("You are a senior code reviewer. Call repo_tree to map the repository, then repo_read_file to read " +
279
237
  "the files in scope, then review them yourself. " +
280
238
  reviewBody);
281
- const coordinatorPrompt = stablePrompt("You coordinate a code-review council. Call `run_council` exactly once (no arguments). When it " +
282
- "returns the arbitrated review, output that review as your final answer VERBATIM — do not " +
283
- "summarize, shorten, or rewrite it.");
284
239
  export function mergeUserSkills(scenarioSkills, userSkills, logger) {
285
240
  if (!userSkills || userSkills.length === 0)
286
241
  return scenarioSkills;
@@ -24,6 +24,7 @@ import type { OwnerAwareSessionStore } from "./security.js";
24
24
  import type { ServiceFileHistoryStore } from "./plugins/store-backend.js";
25
25
  import { resolveStreamApprovalGate } from "./tool-approval.js";
26
26
  import { type WorkspaceSyncCapability } from "./workspace-space.js";
27
+ import type { WorkspaceCacheCapability } from "./workspace-cache.js";
27
28
  /** why 闭集:词 → 给运维看的一句义(诊断 / 测试读它;wire 上的拒句由各门自写)。 */
28
29
  export declare const CAPABILITY_FACT_WHY: {
29
30
  readonly run_store_absent: "no durable run store is wired (DB_BACKEND=mysql|pg|local)";
@@ -83,6 +84,7 @@ export declare const CAPABILITY_FACT_WHY: {
83
84
  readonly not_host_lane: "the execution lane is not the single-user host lane (REMOTE_EXEC=host)";
84
85
  readonly not_k8s_lane: "the execution lane is not the k8s pod sandbox (REMOTE_EXEC=k8s)";
85
86
  readonly workspace_snapshot_store_absent: "no workspace snapshot store is configured (MINIO_ENDPOINT / MINIO_ACCESS_KEY / MINIO_SECRET_KEY)";
87
+ readonly workspace_cache_off: "the build-cache volume per workspace space is off (WORKSPACE_CACHE=off, the default)";
86
88
  };
87
89
  export type CapabilityFactWhy = keyof typeof CAPABILITY_FACT_WHY;
88
90
  /** 不可用臂。 */
@@ -155,11 +157,11 @@ export declare function sessionPolicy(d: Pick<Deps, "sessionPolicyStore" | "sess
155
157
  policyStore: Store<"sessionPolicyStore">;
156
158
  sessionStorage: OwnedSessionStore;
157
159
  }>;
158
- type SyncSessionStore = WithMethods<OwnerAwareSessionStore, "ownerOf" | "exportEntries" | "exportEntriesStream" | "beginImportStaging" | "sessionFaceOf" | "sessionSummaryOf" | "patchSessionMeta">;
159
- type SessionSyncWhy = "store_backend_absent" | "session_owner_face_absent" | "file_history_store_absent" | "session_export_absent" | "session_import_staging_absent" | "session_row_faces_absent" | "policy_purge_absent";
160
- /** 会话对等同步的**对话半族**(manifest / entries / plan / import 两相):后端 ∧ 属主面 ∧ 文件历史店 ∧ 导出两面 ∧
161
- * staged 导入面 ∧ 会话行三口(manifest 带 `title` / `pinned`、导入经唯一写口按导入落定的那一位住客重放它们 —— 会话面读住客)∧
162
- * policy 店能清(overwrite-dst 的 commit 要擦目的端旧 policy 行)。
160
+ type SyncSessionStore = WithMethods<OwnerAwareSessionStore, "exportEntries" | "exportEntriesStream" | "beginImportStaging" | "sessionFaceOf" | "sessionSummaryOf" | "patchSessionMeta">;
161
+ type SessionSyncWhy = "store_backend_absent" | "file_history_store_absent" | "session_export_absent" | "session_import_staging_absent" | "session_row_faces_absent" | "policy_purge_absent";
162
+ /** 会话对等同步的**对话半族**(manifest / entries / plan / import 两相):后端 ∧ 文件历史店 ∧ 导出两面 ∧ staged 导入面 ∧
163
+ * 会话行三口(门读会话面 —— 属主 + 化身一次读,属主判门、化身给 identical 短路腿;manifest 带 `title` / `pinned`;导入经唯一写口按导入落定的
164
+ * 那一位住客重放它们)∧ policy 店能清(overwrite-dst 的 commit 要擦目的端旧 policy 行)。
163
165
  * 文件历史的 blob 搬运是另一只事实。 */
164
166
  export declare function sessionSync(d: Pick<Deps, "backend" | "sessionStorage" | "fileHistoryStore" | "sessionPolicyStore">): Fact<SessionSyncWhy, {
165
167
  backend: Store<"backend">;
@@ -303,18 +305,27 @@ export declare function workflowsJournal(d: Pick<Deps, "workflowRunStore" | "wor
303
305
  journal: Store<"workflowJournalStore">;
304
306
  }>;
305
307
  /**
306
- * k8s 车道工作区跨轮保留的有效策略 —— k8s 车道 ∧ 快照存储在场 ⇒ `{ persist, keep }`(未表态的旋钮取缺省,缺省值属主
307
- * `workspace-space.ts`;`persist` 缺省 `off`,opt-in `space`)。**唯一读法**:能力位 `workspaceSync` 投影它的见证,k8s 适配器工厂(`boot/execution-env.ts`)把同一份
308
+ * k8s 车道工作区跨轮保留的有效策略 —— k8s 车道 ∧ 快照存储在场 ⇒ `{ persist, keep, timeoutMs }`(未表态的旋钮取缺省,缺省值属主
309
+ * `workspace-space.ts`;`persist` 缺省 `space`,`off` = 每轮新建)。**唯一读法**:能力位 `workspaceSync` 投影它的见证,k8s 适配器工厂(`boot/execution-env.ts`)把同一份
308
310
  * 见证交给适配器(适配器自己不再解析策略)。`persist: "off"` 仍是 available(存储在、没开 —— 缺省或运维显式 off)—— 与缺席不同义。
309
311
  *
310
- * **为何只读配置仍恒真**:`space` 的空间按会话住客分区,住客只有一只读口 —— 会话店的 `sessionFaceOf`。会话店没有它(内存会话店)的
311
- * 组合在起服时就被拒(`boot/execution-env.ts` 的 `assertWorkspaceSpaceReadable`,装配第 5 段,读的正是本事实的见证)⇒ 凡是起得来、
312
- * 报 `space` 的部署都读得出住客,本事实不需要运行期依赖(再加一位 = 同一前提两处判)。剩下的「这条 run 读不到住客」是逐 run 的读失败
313
- * (适配器的 `unavailable` 臂,响亮一行),不是部署形。
312
+ * **为何只读配置仍恒真**:`space` 的空间按会话住客分区、按 run 店的一行提交 —— 住客只有一只按 id 读(会话店的 `incarnationOf`,
313
+ * 会话行上的那一格),行住在存储后端里。缺一的组合在起服时就被拒(`boot/execution-env.ts` 的 `assertWorkspaceSpaceReadable`,装配第 5 段,
314
+ * 读的正是本事实的见证)⇒ 凡是起得来、报 `space` 的部署都读得出住客、写得下行,本事实不需要运行期依赖(再加一位 = 同一前提两处判)。
315
+ * 剩下的「这条 run 读不到住客」是逐 run 的读失败(适配器的 `unavailable` 臂,响亮一行),不是部署形。
314
316
  */
315
317
  export declare function workspaceSpace(c: Pick<ServiceConfig, "remoteExec">): Fact<"not_k8s_lane" | "workspace_snapshot_store_absent", {
316
318
  policy: WorkspaceSyncCapability;
317
319
  }>;
320
+ /**
321
+ * k8s 车道按工作区空间的构建缓存卷的有效策略 —— k8s 车道 ∧ `WORKSPACE_CACHE=volume` ⇒ `{ mount, size }`(缺省值属主 `workspace-cache.ts`)。
322
+ * **唯一读法**:能力位 `workspaceSync.cache` 投影它的见证,k8s 适配器工厂(`boot/execution-env.ts`)把同一份见证交给适配器。与快照存储无关
323
+ * (卷属于空间,不属于快照)。为何只读配置仍恒真:卷按会话住客键控,住客读口缺席的组合起服即拒(`assertWorkspaceSpaceReadable`);剩下的
324
+ * 「这条 run 拿不到卷」是逐 run 的 F 类放行(`server.workspace-cache.unavailable` 计数 + 一行),不是部署形。
325
+ */
326
+ export declare function workspaceCache(c: Pick<ServiceConfig, "remoteExec">): Fact<"not_k8s_lane" | "workspace_cache_off", {
327
+ cache: WorkspaceCacheCapability;
328
+ }>;
318
329
  /** 自唤醒调度器挂在宿主 env 上:`SCHEDULER_ENABLED` ∧ 单用户 ∧ host 车道。装配(`boot/execution-env.ts`)与能力位同调。 */
319
330
  export declare function hostScheduler(c: Pick<ServiceConfig, "schedulerEnabled" | "requirePrincipal" | "remoteExec">): Fact<"scheduler_disabled" | "multi_tenant_deployment" | "not_host_lane">;
320
331
  export {};
@@ -3,7 +3,7 @@ import { resolveStreamApprovalGate } from "./tool-approval.js";
3
3
  import { selfOrchestrationDenial } from "./task-workflow.js";
4
4
  import { centerEntitlementSourceWired } from "./runtime-caps-resolver.js";
5
5
  import { overwriteWipeIncapable } from "./session-sync.js";
6
- import { WORKSPACE_PERSIST_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_DEFAULT } from "./workspace-space.js";
6
+ import { WORKSPACE_PERSIST_DEFAULT, WORKSPACE_SNAPSHOT_KEEP_DEFAULT, workspaceSnapshotTimeoutOf } from "./workspace-space.js";
7
7
  export const CAPABILITY_FACT_WHY = {
8
8
  run_store_absent: "no durable run store is wired (DB_BACKEND=mysql|pg|local)",
9
9
  subagent_registry_absent: "the sub-agent steer registry is not wired",
@@ -62,6 +62,7 @@ export const CAPABILITY_FACT_WHY = {
62
62
  not_host_lane: "the execution lane is not the single-user host lane (REMOTE_EXEC=host)",
63
63
  not_k8s_lane: "the execution lane is not the k8s pod sandbox (REMOTE_EXEC=k8s)",
64
64
  workspace_snapshot_store_absent: "no workspace snapshot store is configured (MINIO_ENDPOINT / MINIO_ACCESS_KEY / MINIO_SECRET_KEY)",
65
+ workspace_cache_off: "the build-cache volume per workspace space is off (WORKSPACE_CACHE=off, the default)",
65
66
  };
66
67
  const no = (why) => ({ available: false, why });
67
68
  const yes = (w) => ({ available: true, ...w });
@@ -152,12 +153,10 @@ export function sessionPolicy(d) {
152
153
  export function sessionSync(d) {
153
154
  if (d.backend === undefined)
154
155
  return no("store_backend_absent");
155
- const s = d.sessionStorage;
156
- if (s?.ownerOf === undefined)
157
- return no("session_owner_face_absent");
158
156
  if (d.fileHistoryStore === undefined)
159
157
  return no("file_history_store_absent");
160
- if (s.exportEntries === undefined || s.exportEntriesStream === undefined)
158
+ const s = d.sessionStorage;
159
+ if (s?.exportEntries === undefined || s.exportEntriesStream === undefined)
161
160
  return no("session_export_absent");
162
161
  if (s.beginImportStaging === undefined)
163
162
  return no("session_import_staging_absent");
@@ -360,7 +359,15 @@ export function workspaceSpace(c) {
360
359
  return no("not_k8s_lane");
361
360
  if (!rx.s3Snapshot)
362
361
  return no("workspace_snapshot_store_absent");
363
- return yes({ policy: { persist: rx.workspacePersist ?? WORKSPACE_PERSIST_DEFAULT, keep: rx.workspaceSnapshotKeep ?? WORKSPACE_SNAPSHOT_KEEP_DEFAULT } });
362
+ return yes({ policy: { persist: rx.workspacePersist ?? WORKSPACE_PERSIST_DEFAULT, keep: rx.workspaceSnapshotKeep ?? WORKSPACE_SNAPSHOT_KEEP_DEFAULT, timeoutMs: workspaceSnapshotTimeoutOf(rx) } });
363
+ }
364
+ export function workspaceCache(c) {
365
+ const rx = c.remoteExec;
366
+ if (rx?.provider !== "k8s")
367
+ return no("not_k8s_lane");
368
+ if (rx.workspaceCache === undefined)
369
+ return no("workspace_cache_off");
370
+ return yes({ cache: { mount: rx.workspaceCache.mount, size: rx.workspaceCache.size } });
364
371
  }
365
372
  export function hostScheduler(c) {
366
373
  if (c.schedulerEnabled !== true)