@sema-agent/server 7.142.0 → 7.143.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/MIGRATION.md +442 -0
  2. package/USAGE.md +41 -24
  3. package/dist/adoption/plan.js +22 -0
  4. package/dist/approval-ask-audit-store.d.ts +7 -5
  5. package/dist/approval-ask-audit-store.js +12 -6
  6. package/dist/approval-ask-machine.d.ts +15 -7
  7. package/dist/approval-ask-machine.js +10 -4
  8. package/dist/approval-card.d.ts +124 -25
  9. package/dist/approval-card.js +103 -23
  10. package/dist/approval-queue-kind.d.ts +27 -0
  11. package/dist/approval-queue-kind.js +6 -0
  12. package/dist/approval-reconciler.d.ts +8 -8
  13. package/dist/approval-reconciler.js +3 -3
  14. package/dist/audit.js +4 -2
  15. package/dist/boot/budget-tracing.d.ts +1 -1
  16. package/dist/boot/governance-seams.d.ts +26 -41
  17. package/dist/boot/governance-seams.js +61 -28
  18. package/dist/boot/late-binds.d.ts +7 -4
  19. package/dist/boot/late-binds.js +3 -1
  20. package/dist/boot/leader.js +2 -4
  21. package/dist/boot/memory-consolidation.d.ts +0 -8
  22. package/dist/boot/memory-consolidation.js +3 -3
  23. package/dist/boot/reapers.js +5 -4
  24. package/dist/boot/resolve-spec.d.ts +1 -1
  25. package/dist/boot/resolve-spec.js +8 -8
  26. package/dist/boot/runner-deps.d.ts +10 -7
  27. package/dist/boot/runner-deps.js +5 -17
  28. package/dist/boot/shutdown.d.ts +9 -0
  29. package/dist/boot/shutdown.js +16 -2
  30. package/dist/boot/stage-02-web-admin-pull.js +3 -0
  31. package/dist/boot/stage-03-store-backend.d.ts +4 -1
  32. package/dist/boot/stage-03-store-backend.js +1 -2
  33. package/dist/boot/stage-04-stores.d.ts +5 -2
  34. package/dist/boot/stage-04-stores.js +3 -5
  35. package/dist/boot/stage-05-execution-env.d.ts +5 -2
  36. package/dist/boot/stage-06-runners.d.ts +5 -2
  37. package/dist/boot/stage-06-runners.js +1 -3
  38. package/dist/boot/stage-07-capability-layer.d.ts +5 -2
  39. package/dist/boot/stage-07-capability-layer.js +5 -1
  40. package/dist/boot/stage-08-reapers.d.ts +5 -2
  41. package/dist/boot/stage-09-leader.d.ts +5 -2
  42. package/dist/boot/stage-10-http-server.d.ts +5 -2
  43. package/dist/boot/stage-10-http-server.js +9 -1
  44. package/dist/boot/stores.d.ts +5 -2
  45. package/dist/boot/stores.js +7 -7
  46. package/dist/boot/web-admin.d.ts +5 -1
  47. package/dist/boot/web-admin.js +31 -19
  48. package/dist/capabilities/hands-lane.d.ts +9 -39
  49. package/dist/capabilities/hands-lane.js +8 -13
  50. package/dist/capabilities/mcp-probe.d.ts +3 -2
  51. package/dist/capabilities/scenarios.d.ts +11 -1
  52. package/dist/capabilities/scenarios.js +1 -0
  53. package/dist/capabilities/skills.d.ts +1 -17
  54. package/dist/capabilities/skills.js +0 -8
  55. package/dist/capability-facts.d.ts +8 -8
  56. package/dist/capability-facts.js +4 -4
  57. package/dist/card-void.d.ts +6 -9
  58. package/dist/config-catalog.js +1 -1
  59. package/dist/config-dir-trust.js +58 -29
  60. package/dist/config-invariants.d.ts +7 -1
  61. package/dist/config-invariants.js +7 -0
  62. package/dist/config-provider.d.ts +10 -0
  63. package/dist/config-provider.js +15 -4
  64. package/dist/config-root-migration.d.ts +13 -0
  65. package/dist/config-root-migration.js +99 -0
  66. package/dist/config-types.d.ts +5 -5
  67. package/dist/config.d.ts +4 -3
  68. package/dist/config.js +1 -1
  69. package/dist/delegated-session-owner.d.ts +5 -3
  70. package/dist/delegated-session-owner.js +1 -1
  71. package/dist/device-ws-protocol.d.ts +1 -0
  72. package/dist/device-ws-protocol.js +2 -1
  73. package/dist/env-model-table-gate.d.ts +53 -0
  74. package/dist/env-model-table-gate.js +53 -0
  75. package/dist/fleet/fleet-bus.d.ts +28 -52
  76. package/dist/fleet/fleet-bus.js +35 -100
  77. package/dist/fleet/fleet-reconciler.d.ts +4 -4
  78. package/dist/fleet/fleet-reconciler.js +1 -1
  79. package/dist/fleet/subagent-tail-bus.d.ts +14 -11
  80. package/dist/fleet/subagent-tail-bus.js +15 -28
  81. package/dist/hooks/hook-runner.js +28 -7
  82. package/dist/host-decision.d.ts +54 -20
  83. package/dist/host-decision.js +42 -14
  84. package/dist/http/admission.d.ts +1 -1
  85. package/dist/http/admission.js +7 -3
  86. package/dist/http/approval-sweeps.js +11 -8
  87. package/dist/http/caller-trust.d.ts +1 -1
  88. package/dist/http/durable-run-leg.js +2 -2
  89. package/dist/http/engine-refusal.d.ts +16 -0
  90. package/dist/http/engine-refusal.js +17 -0
  91. package/dist/http/memory-compliance-failure-reply.d.ts +1 -3
  92. package/dist/http/memory-compliance-failure-reply.js +4 -6
  93. package/dist/http/owner-access.d.ts +11 -0
  94. package/dist/http/owner-access.js +13 -0
  95. package/dist/http/resume-legs.d.ts +21 -7
  96. package/dist/http/resume-legs.js +71 -49
  97. package/dist/http/resume-restatement.d.ts +39 -0
  98. package/dist/http/resume-restatement.js +25 -0
  99. package/dist/http/route-ctx.d.ts +35 -10
  100. package/dist/http/route-ctx.js +1 -1
  101. package/dist/http/routes/approvals-assistant.d.ts +9 -1
  102. package/dist/http/routes/approvals-assistant.js +142 -31
  103. package/dist/http/routes/capabilities.js +11 -2
  104. package/dist/http/routes/memory-policy.d.ts +2 -2
  105. package/dist/http/routes/memory-policy.js +1 -1
  106. package/dist/http/routes/notify-wake.js +23 -25
  107. package/dist/http/routes/rules.js +48 -13
  108. package/dist/http/routes/runs.js +9 -8
  109. package/dist/http/routes/session-memory-sync.d.ts +34 -6
  110. package/dist/http/routes/session-memory-sync.js +27 -10
  111. package/dist/http/routes/session-sync.js +13 -20
  112. package/dist/http/routes/sessions.d.ts +2 -2
  113. package/dist/http/routes/sessions.js +22 -23
  114. package/dist/http/routes/tasks.js +27 -49
  115. package/dist/http/server.d.ts +13 -7
  116. package/dist/http/server.js +2 -2
  117. package/dist/http/stored-body-replay.js +15 -4
  118. package/dist/http/wire-types.d.ts +17 -7
  119. package/dist/leader/leader.js +5 -2
  120. package/dist/leader/wire.d.ts +7 -1
  121. package/dist/leader/wire.js +6 -5
  122. package/dist/leg-facts.d.ts +37 -0
  123. package/dist/leg-facts.js +15 -0
  124. package/dist/memory-bundle-engine.d.ts +1 -4
  125. package/dist/memory-bundle-engine.js +2 -2
  126. package/dist/memory-operator-faces.d.ts +30 -66
  127. package/dist/memory-operator-faces.js +17 -88
  128. package/dist/memory-scope-stores.d.ts +38 -40
  129. package/dist/memory-scope-stores.js +51 -36
  130. package/dist/memory-scope.d.ts +0 -9
  131. package/dist/memory-scope.js +0 -9
  132. package/dist/memory-sync-client.js +14 -3
  133. package/dist/memory-sync.d.ts +20 -4
  134. package/dist/memory-sync.js +23 -4
  135. package/dist/model-plane-face.d.ts +13 -7
  136. package/dist/model-plane-face.js +2 -1
  137. package/dist/observability/fail-open.d.ts +20 -4
  138. package/dist/observability/fail-open.js +17 -4
  139. package/dist/observability/run-terminal.js +4 -0
  140. package/dist/orchestration/workflow-completion-inbox.d.ts +5 -19
  141. package/dist/orchestration/workflow-completion-inbox.js +0 -12
  142. package/dist/orchestration/workflow-notify-journal.d.ts +3 -2
  143. package/dist/orchestration/workflow-park-decision.d.ts +8 -3
  144. package/dist/orchestration/workflow-park-decision.js +5 -3
  145. package/dist/org-delivery-gate.d.ts +11 -0
  146. package/dist/org-delivery-gate.js +19 -1
  147. package/dist/parked-decide.d.ts +13 -9
  148. package/dist/parked-decide.js +7 -7
  149. package/dist/parked-revive-surface.js +1 -1
  150. package/dist/peer-inbound-face.d.ts +127 -0
  151. package/dist/peer-inbound-face.js +158 -0
  152. package/dist/permission-rule-list.d.ts +13 -9
  153. package/dist/permission-rule-list.js +7 -3
  154. package/dist/plugins/approval-ask-store-file.js +18 -6
  155. package/dist/plugins/approval-ask-store-memory.js +8 -6
  156. package/dist/plugins/approval-ask-store-sql.d.ts +28 -13
  157. package/dist/plugins/approval-ask-store-sql.js +30 -11
  158. package/dist/plugins/checkpoint-store-sql.d.ts +4 -4
  159. package/dist/plugins/checkpoint-store-sql.js +9 -7
  160. package/dist/plugins/file-history-store-sql.d.ts +24 -18
  161. package/dist/plugins/file-history-store-sql.js +24 -19
  162. package/dist/plugins/file-run-store.d.ts +29 -0
  163. package/dist/plugins/file-run-store.js +97 -8
  164. package/dist/plugins/imported-layer-root-retirement.d.ts +82 -0
  165. package/dist/plugins/imported-layer-root-retirement.js +96 -0
  166. package/dist/plugins/local-checkpoint-store.d.ts +1 -1
  167. package/dist/plugins/local-checkpoint-store.js +2 -2
  168. package/dist/plugins/local-session-store.d.ts +6 -2
  169. package/dist/plugins/local-session-store.js +8 -2
  170. package/dist/plugins/mailbox-recipient-lifecycle.d.ts +9 -31
  171. package/dist/plugins/mailbox-recipient-lifecycle.js +4 -0
  172. package/dist/plugins/mailbox-store-sql.d.ts +12 -8
  173. package/dist/plugins/mailbox-store-sql.js +76 -7
  174. package/dist/plugins/memory-sync-store-pg.d.ts +15 -0
  175. package/dist/plugins/memory-sync-store-pg.js +21 -0
  176. package/dist/plugins/memory-sync-store-tidb.d.ts +2 -0
  177. package/dist/plugins/memory-sync-store-tidb.js +13 -1
  178. package/dist/plugins/one-time-migrations.d.ts +32 -2
  179. package/dist/plugins/one-time-migrations.js +106 -3
  180. package/dist/plugins/org-delivery-quota-store.d.ts +1 -1
  181. package/dist/plugins/org-delivery-quota-store.js +2 -12
  182. package/dist/plugins/park-commit-watch.d.ts +1 -1
  183. package/dist/plugins/permission-rule-store-file.d.ts +10 -1
  184. package/dist/plugins/permission-rule-store-file.js +15 -2
  185. package/dist/plugins/permission-rule-store-sql.d.ts +31 -1
  186. package/dist/plugins/permission-rule-store-sql.js +35 -2
  187. package/dist/plugins/remote-env-device.d.ts +8 -11
  188. package/dist/plugins/remote-env-device.js +6 -3
  189. package/dist/plugins/remote-env-k8s.d.ts +37 -24
  190. package/dist/plugins/remote-env-k8s.js +50 -17
  191. package/dist/plugins/retention-store-sql.d.ts +2 -2
  192. package/dist/plugins/sandbox-read-floor.d.ts +5 -7
  193. package/dist/plugins/sandbox-read-floor.js +2 -7
  194. package/dist/plugins/sql-errors.d.ts +10 -1
  195. package/dist/plugins/sql-errors.js +13 -0
  196. package/dist/plugins/store-backend.d.ts +23 -28
  197. package/dist/plugins/store-backend.js +9 -1
  198. package/dist/plugins/store-contracts.d.ts +29 -5
  199. package/dist/plugins/store-contracts.js +4 -1
  200. package/dist/plugins/task-attachment-store.d.ts +2 -2
  201. package/dist/plugins/task-attachment-store.js +37 -52
  202. package/dist/plugins/tool-result-store-sql.d.ts +9 -8
  203. package/dist/plugins/tool-result-store-sql.js +23 -2
  204. package/dist/plugins/wiring-section-codec.d.ts +32 -0
  205. package/dist/plugins/wiring-section-codec.js +55 -0
  206. package/dist/plugins/workspace-fill.d.ts +1 -1
  207. package/dist/rule-entry-list.d.ts +3 -2
  208. package/dist/rule-entry-list.js +5 -1
  209. package/dist/rules-consent.d.ts +28 -5
  210. package/dist/rules-consent.js +40 -28
  211. package/dist/run-local.js +9 -5
  212. package/dist/runs.d.ts +12 -8
  213. package/dist/runs.js +6 -7
  214. package/dist/runtime-governance.d.ts +1 -1
  215. package/dist/server-secret-env.d.ts +4 -0
  216. package/dist/session-error-wire.d.ts +12 -0
  217. package/dist/session-error-wire.js +6 -0
  218. package/dist/session-incarnation.d.ts +1 -1
  219. package/dist/session-notify-door.d.ts +106 -0
  220. package/dist/session-notify-door.js +93 -0
  221. package/dist/session-sync.d.ts +9 -14
  222. package/dist/session-sync.js +5 -5
  223. package/dist/session-workspace.d.ts +3 -13
  224. package/dist/session-workspace.js +0 -9
  225. package/dist/standing-answer-grant.d.ts +5 -0
  226. package/dist/standing-answer-grant.js +10 -0
  227. package/dist/stores-sql/session-policy-store-sql.d.ts +4 -2
  228. package/dist/stores-sql/session-policy-store-sql.js +3 -13
  229. package/dist/task-mcp.d.ts +1 -1
  230. package/dist/task-settings.d.ts +11 -8
  231. package/dist/task-settings.js +31 -11
  232. package/dist/tool-approval.d.ts +52 -34
  233. package/dist/tool-approval.js +66 -57
  234. package/dist/trace/core-keyset-guard.d.ts +11 -8
  235. package/dist/trace/engine-notice-wire.d.ts +1 -1
  236. package/dist/trace/engine-notice-wire.js +3 -1
  237. package/dist/trace/exit-seat.d.ts +7 -1
  238. package/dist/trace/exit-seat.js +1 -0
  239. package/dist/trace/ledger-sink.d.ts +2 -2
  240. package/dist/trace/model-segment.d.ts +55 -25
  241. package/dist/trace/model-segment.js +101 -33
  242. package/dist/trace/project.d.ts +7 -4
  243. package/dist/trace/project.js +6 -0
  244. package/dist/trace/redact.d.ts +61 -9
  245. package/dist/trace/redact.js +1490 -468
  246. package/dist/trace/self-minted-wire.d.ts +510 -38
  247. package/dist/trace/self-minted-wire.js +165 -17
  248. package/dist/trace/wire-projection-faces.d.ts +1 -1
  249. package/dist/trace/wire-projection-faces.js +1 -1
  250. package/dist/verifier-pause-card.d.ts +33 -5
  251. package/dist/verifier-pause-card.js +15 -10
  252. package/dist/web-admin-client/apply-effective.d.ts +12 -16
  253. package/dist/web-admin-client/apply-effective.js +90 -10
  254. package/dist/workspace-space.d.ts +19 -3
  255. package/dist/workspace-space.js +8 -4
  256. package/docs/DEVICE-EXECUTOR-PROTOCOL.md +3 -1
  257. package/package.json +3 -2
  258. package/wire-changes/server@7.143.0.json +1167 -0
@@ -2,11 +2,12 @@
2
2
  * **规则条目表在本仓受理口的判** —— 一份,四张表同调(core 7.42.0 起「这个调用不用问」只有一个去处:引擎的 allow 层;条目的文法、匹配、
3
3
  * 名字、读成空的通告都归引擎,本仓不写第二套):
4
4
  * · 请求自带技能的 `allowedTools` / `disallowedTools`(顶层 `skills[]` 与子代 `agents[].skills[]` 两处受理口,`spec-fields.ts` / `http/admission.ts`);
5
- * · 请求的 `settings.permissions.allow`(整份原样投 `TaskSpec.allowedTools`,`task-settings.ts`);
5
+ * · 请求的 `settings.permissions.allow`(整份按位置投 `TaskSpec.allowedTools`,`task-settings.ts`;条目也可以是设置层的 `{ rule, layer }`,本文件按它的规则原文读,层只选锚);
6
6
  * · 运维的 `APPROVAL_GRANT`(整份投 `RunnerDeps.allowedTools`,`config.ts` 启动校验)。
7
7
  *
8
8
  * 本仓在这些表上只剩下面几问(内容形、`*`、写路径形、名字引擎怎么读就怎么算):
9
- * ① **形**({@link ruleEntryListShapeOk}):字符串数组。引擎逐条迭代;一个裸字符串会被逐字符读成几条(deny 侧 = 意图静默落空)。
9
+ * ① **形**({@link ruleEntryListShapeOk}):字符串数组。引擎逐条迭代;一个裸字符串会被逐字符读成几条(deny 侧 = 意图静默落空)。请求的 allow 桶的形另由桶门判
10
+ * (`task-settings.ts` 的 `settingsPermissionBucketIssue`:串或带层条目),它进下面 ③ 时带层条目读成规则原文({@link toolListsOf})。
10
11
  * 本谓词不带条数帽(引擎的 `screenAllowedToolsValue` 只判形);条数帽是另一只函数 {@link allowLayerEntryCountIssue}(`MAX_ALLOW_LAYER_ENTRIES`,
11
12
  * 请求 allow 桶、技能两只清单、运维表四张表同一只、同一句)。
12
13
  * ② (7.116.0–7.122.x 的「引擎读不到的一形」—— 工具段为空的协议名 `mcp__srv__` 四张表都拒 —— 随 core 7.48.0 [ref] 退役:引擎判官给它判词
@@ -3,6 +3,7 @@ import { displayListEntry } from "./list-entry-display.js";
3
3
  import { limitRefusal } from "./limit-refusal.js";
4
4
  import { TOOL_NAME_ROSTER } from "./namespaced-rule-name.js";
5
5
  import { NOT_IN_ROSTER, entryProblem, entryProblemsFacts } from "./entry-problems.js";
6
+ import { layeredRuleOk } from "./permission-rule-list.js";
6
7
  export const ruleEntryListShapeOk = (v) => Array.isArray(v) && v.every((x) => typeof x === "string");
7
8
  export const MAX_ALLOW_LAYER_ENTRIES = 1_000;
8
9
  const ALLOW_LAYER_OVER_CAP_TAIL = "Refused rather than truncated: a truncated list silently drops the entries past the cap. Merge entries into broader ones (mcp__<server>__*, Bash(<prefix>:*), Read(//<dir>/**)) or trim the list";
@@ -162,7 +163,10 @@ function* toolListsOf(body) {
162
163
  const settings = body["settings"];
163
164
  const permissions = isPlainRecord(settings) ? settings["permissions"] : undefined;
164
165
  if (isPlainRecord(permissions))
165
- yield { layer: "settings.permissions.allow", field: "settings.permissions.allow", prefix: "", at: "settings.permissions.allow", list: permissions["allow"] };
166
+ yield { layer: "settings.permissions.allow", field: "settings.permissions.allow", prefix: "", at: "settings.permissions.allow", list: ruleTextsOfBucket(permissions["allow"]) };
167
+ }
168
+ function ruleTextsOfBucket(list) {
169
+ return Array.isArray(list) ? list.map((x) => (layeredRuleOk(x) ? x.rule : x)) : list;
166
170
  }
167
171
  function inertReadingOfList(l) {
168
172
  const face = TOOL_LIST_FACES[l.layer];
@@ -219,16 +219,24 @@ export type LocalImportIndeterminateCode =
219
219
  /** 兑付走本身没走完(本层抛出/中断)⇒ 这一条连走都没走到。 */
220
220
  | "walk_failed";
221
221
  /** prepare 面的一行。判别式联合:被收下的行**没有**拒因字段可读,被拒的行**必然**带拒因 ——
222
- * 「admitted 却带 reason」这种矛盾态在类型上不可表达。 */
222
+ * 「admitted 却带 reason」这种矛盾态在类型上不可表达。
223
+ * 收下的**收紧**(deny / ask)在 prepare 当场经直写接缝落店,行带它的结局 `outcome`(`persisted` = 这次写了 / `no-op` = 等价规则已在店);
224
+ * 收下的**放行**没有 `outcome` —— 它的结局在 redeem 回执里(放行要同意,收紧不等同意;见 `prepareLocalImport`)。 */
223
225
  export type LocalImportPreviewMember = {
224
226
  key: LocalImportRuleKey;
225
227
  admitted: true;
228
+ } | {
229
+ key: LocalImportRuleKey;
230
+ admitted: true;
231
+ outcome: LocalImportTighteningOutcome;
226
232
  } | {
227
233
  key: LocalImportRuleKey;
228
234
  admitted: false;
229
235
  reason: LocalImportRefusalCode;
230
236
  detail: string;
231
237
  };
238
+ /** 一条收紧在 prepare 当场落店的结局(与兑付回执的 `persisted` / `no-op` 同词同义;直写接缝 `addPersistedRule` 的 `added` / `no-op`)。 */
239
+ export type LocalImportTighteningOutcome = "persisted" | "no-op";
232
240
  /** 兑付回执的一行。 */
233
241
  export interface LocalImportOutcomeMember {
234
242
  key: LocalImportRuleKey;
@@ -266,6 +274,14 @@ export type LocalImportPrepared = {
266
274
  ok: false;
267
275
  reason: "batch-too-large";
268
276
  bytes: number;
277
+ }
278
+ /** 一条收紧写不进店(直写接缝答 `failed`)⇒ 这一批的放行全部扣下(不铸记录、不铸票)。`committed` 原样是 core 的两词闭集,
279
+ * wire 上与 `POST /v1/rules` 的写失败同一码同一键;此前已落的收紧留着(收紧方向),同一批重发即幂等(已落的答 `no-op`)。 */
280
+ | {
281
+ ok: false;
282
+ reason: "tightening-write-failed";
283
+ committed: RuleWriteCommitted;
284
+ detail: string;
269
285
  };
270
286
  /** A7 redeem 的结果。 */
271
287
  export type LocalImportRedeemed =
@@ -456,13 +472,20 @@ export type CardRulePersisted = {
456
472
  * 🔴 不 switch、不翻译、不折叠:词表属主是 core,本仓照转。 */
457
473
  code?: string;
458
474
  };
459
- /** 一层用户交上来的 settings(HTTP 载荷已 zod 校验过的形)。 */
460
- export interface RuleImportLayerInput {
461
- layer: ImportedSettingsLayer;
475
+ /**
476
+ * 一层用户交上来的 settings(HTTP 载荷已 zod 校验过的形)—— 按 `layer` 的联合,与引擎的 `CcImportLayer` 同形:
477
+ * 用户层**没有** `root`(它的 `/x` 在它自己的配置目录读,引擎不把它带进店;交了 root 的体在边界 400),项目层与本地层带 `root`(规则的作用域根)。
478
+ */
479
+ export type RuleImportLayerInput = {
480
+ layer: "userSettings";
481
+ path: string;
482
+ content: string;
483
+ } | {
484
+ layer: Exclude<ImportedSettingsLayer, "userSettings">;
462
485
  path: string;
463
486
  root: string;
464
487
  content: string;
465
- }
488
+ };
466
489
  /**
467
490
  * scope 的 **wire 形**:`global` | `project:<root>` 的判别式串([ref] v2 §6 F5「显式成文」)。
468
491
  *
@@ -232,25 +232,14 @@ function buildLocalImportLayers(candidates) {
232
232
  const byRoot = new Map();
233
233
  for (const c of candidates) {
234
234
  const key = c.scope.kind === "global" ? "" : c.scope.root;
235
- let bucket = byRoot.get(key);
236
- if (bucket === undefined) {
237
- bucket = { deny: [], ask: [], allow: [] };
238
- byRoot.set(key, bucket);
239
- }
240
- bucket[c.behavior].push(c.rule);
235
+ const bucket = byRoot.get(key) ?? [];
236
+ bucket.push(c.rule);
237
+ byRoot.set(key, bucket);
241
238
  }
242
- return [...byRoot].map(([root, lists]) => ({
243
- layer: root === "" ? "userSettings" : "projectSettings",
244
- path: root === "" ? LOCAL_IMPORT_LAYER_PATH : `${LOCAL_IMPORT_LAYER_PATH}:${root}`,
245
- root: root === "" ? "/" : root,
246
- readFile: async () => JSON.stringify({
247
- permissions: {
248
- ...(lists.deny.length > 0 ? { deny: lists.deny } : {}),
249
- ...(lists.ask.length > 0 ? { ask: lists.ask } : {}),
250
- ...(lists.allow.length > 0 ? { allow: lists.allow } : {}),
251
- },
252
- }),
253
- }));
239
+ return [...byRoot].map(([root, allow]) => {
240
+ const readFile = async () => JSON.stringify({ permissions: { allow } });
241
+ return root === "" ? { layer: "userSettings", path: LOCAL_IMPORT_LAYER_PATH, readFile } : { layer: "projectSettings", path: `${LOCAL_IMPORT_LAYER_PATH}:${root}`, root, readFile };
242
+ });
254
243
  }
255
244
  function buildLocalImportMembers(members) {
256
245
  return members.map((m) => {
@@ -367,12 +356,10 @@ export function createRuleConsentLane(stores, opts) {
367
356
  return { ok: true, kind: "single", rule: redeemed.rule, rev: redeemed.rev, alreadyRedeemed: redeemed.alreadyRedeemed };
368
357
  },
369
358
  async prepareImport(principal, layers) {
370
- const ccLayers = layers.map((l) => ({
371
- layer: l.layer,
372
- path: l.path,
373
- root: l.root,
374
- readFile: async () => l.content,
375
- }));
359
+ const ccLayers = layers.map((l) => {
360
+ const readFile = async () => l.content;
361
+ return l.layer === "userSettings" ? { layer: l.layer, path: l.path, readFile } : { layer: l.layer, path: l.path, root: l.root, readFile };
362
+ });
376
363
  const { preview, approvalId } = await prepareCcImport({ layers: ccLayers, principal, deps });
377
364
  if (approvalId === undefined) {
378
365
  return { ok: true, preview };
@@ -494,16 +481,41 @@ export function createRuleConsentLane(stores, opts) {
494
481
  continue;
495
482
  }
496
483
  seen.add(dedupKey);
484
+ admitted.push({ at: members.length, row: screened });
497
485
  members.push({ key, admitted: true });
498
- admitted.push(screened);
499
486
  }
500
- if (admitted.length === 0)
487
+ const allows = [];
488
+ for (const { at, row: c } of admitted) {
489
+ const key = members[at].key;
490
+ const behavior = c.behavior;
491
+ if (!isDirectWritableRuleBehavior(behavior)) {
492
+ allows.push({ ...c, behavior });
493
+ continue;
494
+ }
495
+ const added = await addPersistedRule({ behavior, rule: c.rule, scope: c.scope, principal, provider: stores.provider });
496
+ switch (added.status) {
497
+ case "added":
498
+ members[at] = { key, admitted: true, outcome: "persisted" };
499
+ break;
500
+ case "no-op":
501
+ members[at] = { key, admitted: true, outcome: "no-op" };
502
+ break;
503
+ case "failed":
504
+ localImportLogger.warn("local_import_tightening_write_failed", { behavior, scope: key.scope, committed: added.committed, note: "a deny / ask of this batch did not land — the batch's allows are withheld (no import record, no ticket); sending the same batch again is idempotent" });
505
+ return { ok: false, reason: "tightening-write-failed", committed: added.committed, detail: added.error };
506
+ default: {
507
+ const unknown = added;
508
+ throw new Error(`unknown addPersistedRule outcome: ${JSON.stringify(unknown)}`);
509
+ }
510
+ }
511
+ }
512
+ if (allows.length === 0)
501
513
  return { ok: true, preview: { members } };
502
- const { preview, approvalId } = await prepareCcImport({ layers: buildLocalImportLayers(admitted), principal, deps });
514
+ const { preview, approvalId } = await prepareCcImport({ layers: buildLocalImportLayers(allows), principal, deps });
503
515
  const engineKeys = new Set(preview.candidates.map((c) => `${c.behavior}${LOCAL_IMPORT_KEY_SEP}${serializeRuleScope(c.scope)}${LOCAL_IMPORT_KEY_SEP}${c.rule}`));
504
516
  for (let i = 0; i < members.length; i++) {
505
517
  const m = members[i];
506
- if (m === undefined || !m.admitted)
518
+ if (m === undefined || !m.admitted || "outcome" in m)
507
519
  continue;
508
520
  if (engineKeys.has(`${m.key.behavior}${LOCAL_IMPORT_KEY_SEP}${m.key.scope}${LOCAL_IMPORT_KEY_SEP}${m.key.rule}`))
509
521
  continue;
package/dist/run-local.js CHANGED
@@ -19,7 +19,7 @@ import { applyRuntimeGovernance, autonomyModeFloor, entryPermissionMode } from "
19
19
  import { ForkRoutingSessionStore } from "./plugins/fork-routing-session-store.js";
20
20
  import { taskWallClockSec } from "./task-workflow.js";
21
21
  import { applyCenterArtifact, centerSourceOnError } from "./capabilities/center-prompts.js";
22
- import { buildUnpricedCeilingNotice, loadConfig, logConfigDiagnostics, parseConfigProvider, parseNumOrFailNonNegative } from "./config.js";
22
+ import { buildUnpricedCeilingNotice, isRosterGateArmed, loadConfig, logConfigDiagnostics, parseConfigProvider, parseNumOrFailNonNegative } from "./config.js";
23
23
  import { buildStoreOpenRefusalTail } from "./plugins/store-open-refusal.js";
24
24
  import { acquireLocalBackendWithWait } from "./plugins/store-lock-wait.js";
25
25
  import { configLocalRoot, createConfigProvider } from "./config-provider.js";
@@ -28,6 +28,7 @@ import { buildUnreadConfigFileFields, buildUnreadConfigKeyFields } from "./web-a
28
28
  import { composeExecutionLane, settleHostLaneShell, warnReadDenyFloorUnkept } from "./boot/execution-lane.js";
29
29
  import { judgeConsolidationValve } from "./boot/memory-consolidation.js";
30
30
  import { logConfigDirMigration, migrateLocalConfigDirectory } from "./config-d-migration.js";
31
+ import { logEnvModelsMigration, runEnvModelsMigration } from "./config-root-migration.js";
31
32
  import { assertLocalConfigDirTrusted, ConfigDirUntrustedError } from "./config-dir-trust.js";
32
33
  import { applyEffective, applyCenterReadFace, applySkillsManifest, resolveMcpServers, mcpForScenario, resolveA2aPeers, a2aForScenario } from "./web-admin-client/facade.js";
33
34
  import { hostExecutionEnvFactory } from "./plugins/remote-env-host.js";
@@ -42,7 +43,7 @@ import { createTaskListLane } from "./boot/task-list-lane.js";
42
43
  import { pickHandsRunner, withoutHands } from "./capabilities/hands-lane.js";
43
44
  import { HttpError, sessionIdShapeIssue } from "./security.js";
44
45
  import { createMemoryEngineIncidentSink } from "./memory-incident-sink.js";
45
- import { fileMemoryEngineRoot, memoryEngineBackendFor, memoryLayersNamedBy, memorySpecForRequest } from "./memory-scope.js";
46
+ import { fileMemoryEngineRoot, memoryEngineBackendFor, memorySpecForRequest } from "./memory-scope.js";
46
47
  import { createMemoryScopeStores } from "./memory-scope-stores.js";
47
48
  import { createMemoryProjectLayers } from "./memory-project-layer.js";
48
49
  import { assertProjectLayerOwnable } from "./project-identity.js";
@@ -52,7 +53,7 @@ import { startMemorySyncRunner } from "./memory-sync-client.js";
52
53
  import { buildPricing, cappedCeiling, createTracer } from "./budget.js";
53
54
  import { createSharedRunnerDeps } from "./boot/runner-deps.js";
54
55
  import { createPeerDesk } from "./peer-seat.js";
55
- import { createGovernanceSeams } from "./boot/governance-seams.js";
56
+ import { assertGovernanceAssemblable, createGovernanceSeams } from "./boot/governance-seams.js";
56
57
  import { assertRetentionLaneWirable } from "./boot/retention-lane.js";
57
58
  import { createOrgMemoryAdmissionWiring } from "./boot/org-memory.js";
58
59
  import { assembleDeploymentMods } from "./boot/deployment-mods.js";
@@ -362,6 +363,8 @@ export async function runLocal(argv, deps = {}) {
362
363
  printErr(err instanceof Error ? err.message : String(err));
363
364
  return 2;
364
365
  }
366
+ const envModelsMigration = { root, env: process.env, envTableIsPlaceholder: isRosterGateArmed(config.webAdmin) };
367
+ logEnvModelsMigration(logger, envModelsMigration, await runEnvModelsMigration(envModelsMigration));
365
368
  logConfigDirMigration(logger, await migrateLocalConfigDirectory(root));
366
369
  try {
367
370
  await composeExecutionLane(config, { configSource: "local", localRoot: root, logger, drivesFromFile: ["host"] });
@@ -393,7 +396,7 @@ export async function runLocal(argv, deps = {}) {
393
396
  for (const d of r.droppedEntries ?? [])
394
397
  logger.warn("config_entry_dropped", { domain: d.domain, entry: d.entry, reasonCode: d.reasonCode, error: d.error, version: r.effective.version, phase: "boot", note: "this one entry failed its domain schema and was dropped on its own — the rest of the candidate applies (per-entry tolerance); fix the named entry to bring it back" });
395
398
  applying = true;
396
- applyEffective(config, r.effective, logger, { leg: "boot" });
399
+ applyEffective(config, r.effective, logger, { leg: "boot", catalogSource: r.catalogSource });
397
400
  effective = r.effective;
398
401
  candidatePrompts = verdict.prompts;
399
402
  if (effective?.mcp)
@@ -487,7 +490,7 @@ export async function runLocal(argv, deps = {}) {
487
490
  const engineDataRoot = fileMemoryEngineRoot(config, root);
488
491
  const fileEngine = memoryEngineBackendFor(config, root, memoryEngineIncident);
489
492
  const memoryEngine = fileEngine !== undefined
490
- ? createMemoryScopeStores({ backend: fileEngine.backend, root: engineDataRoot, namedLayers: () => memoryLayersNamedBy({ ...config, memoryScope: args.user ?? "local" }), onIncident: memoryEngineIncident })
493
+ ? createMemoryScopeStores({ backend: fileEngine.backend, root: engineDataRoot, onIncident: memoryEngineIncident })
491
494
  : undefined;
492
495
  try {
493
496
  judgeConsolidationValve({ config, memoryEngine });
@@ -582,6 +585,7 @@ export async function runLocal(argv, deps = {}) {
582
585
  ],
583
586
  retentionExecutor: null,
584
587
  });
588
+ assertGovernanceAssemblable(config);
585
589
  const { registrations: deploymentMods } = await assembleDeploymentMods({ metrics, brandIdentity: true, declaration: config.modsDeclaration, logger });
586
590
  const sharedRunnerDeps = createSharedRunnerDeps({
587
591
  metrics,
package/dist/runs.d.ts CHANGED
@@ -4,6 +4,7 @@ import { type EntryProblem } from "./entry-problems.js";
4
4
  import type { SendUserFileEmitter } from "./capabilities/send-user-file-tool.js";
5
5
  import type { RunStore } from "./plugins/store-backend.js";
6
6
  import type { Metrics } from "./observability/metrics.js";
7
+ import type { RunSkillEntry } from "./capabilities/skills.js";
7
8
  import { type RunAbort } from "./run-cancel-context.js";
8
9
  import type { ModelUsageTracker, PromptManifestTracker } from "./budget.js";
9
10
  import type { ElicitationCoordinator } from "./elicitation.js";
@@ -39,16 +40,15 @@ import type { UserInputLedger } from "./user-input-ledger.js";
39
40
  * first-marker-wins guards live inside the seam.
40
41
  */
41
42
  /** [ref]③(b) + codex R2: the bg agent-handle OUTPUT read, kind-gated BEFORE any poll. `pollTask` is the
42
- * generic registry face and polling is NOT side-effect-free for other kinds — a terminal workflow poll
43
- * fires `onServedTerminal` (acknowledges the completion as served → suppresses its notification) and a
44
- * bash poll advances the output cursor. `getAccessibleTask` is a pure lookup: resolve + access-check +
43
+ * generic registry face and polling is NOT side-effect-free for other kinds — a bash poll advances the
44
+ * output cursor (and a workflow's read face is its journal, not this one). `getAccessibleTask` is a pure lookup: resolve + access-check +
45
45
  * type-check FIRST; only a `background_agent` row is ever polled (non-blocking). Everything else returns
46
46
  * the registry's own not_found shape, indistinguishable from an unknown handle (the route 404s it).
47
47
  *
48
48
  * 1.250 durable arm (core 1.364, [ref] follow-up delivered): a FULL in-process miss (`!row`) falls
49
49
  * through to `pollTask(…, { agentStore })` — core's durable arm only fires for `a*`-shaped handles
50
50
  * (DURABLE_AGENT_HANDLE_RE) behind `canAccessAgentRecord`, and with NO in-process handle the live
51
- * workflow/bash branches are unreachable, so the kind-gate's reason (onServedTerminal / cursor advance)
51
+ * workflow/bash branches are unreachable, so the kind-gate's reason (cursor advance / the journal owning workflow reads)
52
52
  * cannot trigger. Terminal rows serve the durable snapshot cross-instance/post-restart; a foreign-writer
53
53
  * running row answers the honest "outcome unknown here"; anything else is core's own not_found (byte-same
54
54
  * route 404 as today). A row that IS in-process but of the wrong type stays a hard 404 — the process
@@ -372,9 +372,8 @@ approval?: {
372
372
  executionLane: import("./execution-lane-caps.js").ExecutionLane;
373
373
  },
374
374
  /** 本腿结算用的 checkpoint 店(`deps.checkpointStore`),两件事:①[ref] park 投影腿的读口 —— 在场时 durable park 的
375
- * `suspended` 事件带 `toolCallId`(graft 见 {@link parkToolCallId});②验证门结算时作废验证器自己的停驻卡
376
- * ({@link voidVerifierPauseCard}:本服务兑现不了的卡不留在待决面上);③停驻没落定就以失败收场时作废手里那张卡({@link voidHeldParkCard})。
377
- * 缺席 ⇒ ①旧字节逐字不变;②③带凭据的停驻响亮留痕、卡由兜底收割。 */
375
+ * `suspended` 事件带 `toolCallId`(graft 见 {@link parkToolCallId});②停驻没落定就以失败收场时作废手里那张卡({@link voidHeldParkCard})。
376
+ * 缺席 ⇒ ①旧字节逐字不变;②带凭据的停驻响亮留痕、卡由兜底收割。(验证器自己的停驻卡不经本店:结算时交本腿的 Runner 动词,见 {@link voidVerifierPauseCard}。) */
378
377
  checkpointCards?: ParkCheckpointReader & CardVoidPort,
379
378
  /** [ref]:装配期(`prepareSpec` → `resolveSpec`)铸下的用户面通告 —— 那一刻本腿的通告口还没开,
380
379
  * 所以由本腿在 `registerEngineNoticeLeg` 那一拍连同 `pending` 一起投(投递单点仍是 `route()`)。
@@ -412,7 +411,12 @@ acceptance?: RunAcceptance,
412
411
  * 会话行的位置与项目那一格的记录座(`session-workspace.ts`;唯一写者):普通流形逐帧喂(这条腿自己的 `workspace_changed` 排一次写),
413
412
  * `done` 时等写落定再落终局。只回结果的 verify / cascade 形宿主手里没有流 ⇒ 不记。缺席 ⇒ 不记行(与本参数存在之前逐字相同)。
414
413
  */
415
- workspaceRecorder?: SessionWorkspaceRecorder): Promise<void>;
414
+ workspaceRecorder?: SessionWorkspaceRecorder,
415
+ /**
416
+ * 这条 run 交给引擎的技能名册(`wiring_manifest.skills` 段;部署事实 `LegFacts.skills`,`leg-facts.ts`:受理漏斗在复制之前读出)。
417
+ * 缺席 = 嵌入方自带的装配器没铸 ⇒ 账本行不带技能段。
418
+ */
419
+ runSkills?: readonly RunSkillEntry[]): Promise<void>;
416
420
  /** How often a running instance refreshes its run's updated_at (liveness, independent of events).
417
421
  * Must stay strictly below `runStaleSec` (asserted at startup AND on every hot config candidate) or the
418
422
  * reaper would race live runs. [ref]:数值的单源是 `config-invariants.ts`(那条不变量的属主),两个消费面
package/dist/runs.js CHANGED
@@ -9,7 +9,6 @@ import { isSessionErrorCode, sessionErrorHttp } from "./session-error-wire.js";
9
9
  import { redactErrorMessage } from "./observability/run-terminal.js";
10
10
  import { withPrincipal } from "./observability/principal-context.js";
11
11
  import { withModelReachScope } from "./model-reach-scope.js";
12
- import { runSkillsOf } from "./capabilities/skills.js";
13
12
  import { createInstructionSelectionLeg } from "./instruction-selection.js";
14
13
  import { redactSecrets } from "./trace/redact.js";
15
14
  import { taskNotificationEventData, appendModelUsageDelta, appendPromptManifest, attachModelUsage } from "./trace/project.js";
@@ -18,7 +17,7 @@ import { registerEngineNoticeLeg } from "./trace/engine-notice-wire.js";
18
17
  import { recordTurnActivity } from "./turn-activity.js";
19
18
  import { buildCancelledLedgerRow, CANCELLED_CODE, createRunAbort, mintCancelledResult } from "./run-cancel-context.js";
20
19
  import { createApprovalCardEmitter, resolveApprovalLeg } from "./tool-approval.js";
21
- import { fleetRunResiduals, isFleetAgentTerminalNotification } from "./fleet/fleet-bus.js";
20
+ import { fleetRunResiduals } from "./fleet/fleet-bus.js";
22
21
  import { defaultSubagentTailBus } from "./fleet/subagent-tail-bus.js";
23
22
  import { emitPendingWorkflowCompletions, taskNotificationInboxEntry, taskNotificationStreamKeyOf, NotifiedKeys } from "./orchestration/workflow-completion-inbox.js";
24
23
  import { isParkLedgerEventType } from "./trace/ledger-events.js";
@@ -93,6 +92,7 @@ const PREPARE_REFUSAL_HTTP_STATUS = {
93
92
  "config.memory_project_marker": 409,
94
93
  "config.memory_project_marker_nested": 409,
95
94
  "config.read_deny_floor_unkept": 409,
95
+ "config.mcp_server_name_collision": 409,
96
96
  };
97
97
  export function resumeAtHttpStatus(result) {
98
98
  const code = failureCodeOf(result.terminal);
@@ -277,7 +277,7 @@ export async function voidHeldParkCard(held, hostSessionId, cards, leg) {
277
277
  return;
278
278
  await voidPendingCard({ token: held.terminal.token, sessionId: hostSessionId }, cards, "server.park.held-card-void-failed", `leg=${leg}`);
279
279
  }
280
- export async function runInBackground(runner, spec, runStore, taskId, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, checkpointCards, pendingEngineNotices, trustedInternals, userInputLedger, acceptance, workspaceRecorder) {
280
+ export async function runInBackground(runner, spec, runStore, taskId, metrics, principal, verify, cascade, instrumentDegenerate, planCacheProbe, persistThinking = false, inflight, preemptable, steerable, modelUsage, elicitation, elicitOwner, captureTurnAnchor, fleetPublisher, captureUserMessageAnchor, question, workflowCompletionInbox, subagentSteer, completionDiagLog, sendUserFile, promptManifests, approval, checkpointCards, pendingEngineNotices, trustedInternals, userInputLedger, acceptance, workspaceRecorder, runSkills) {
281
281
  const startedAt = Date.now();
282
282
  metrics?.addGauge("runs_active", 1);
283
283
  fleetPublisher?.onStart();
@@ -375,7 +375,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
375
375
  acceptance?.settle(safe);
376
376
  };
377
377
  const instructionLeg = createInstructionSelectionLeg();
378
- const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData), legSections: () => ({ instructions: instructionLeg.segment(), skills: runSkillsOf(spec) }), usage: { modelUsage, taskId } });
378
+ const sink = createLedgerSink({ appendEvent: (seq, type, data) => runStore.appendEvent(taskId, seq, type, data), persistThinking, notifiedKeys, onActivity: (kind, statusData) => recordTurnActivity(taskId, kind, statusData), legSections: () => ({ instructions: instructionLeg.segment(), skills: runSkills }), usage: { modelUsage, taskId } });
379
379
  const append = (type, data) => sink.append(type, data);
380
380
  const flush = () => sink.flush();
381
381
  const flushModelUsage = () => appendModelUsageDelta(append, modelUsage, taskId);
@@ -430,9 +430,8 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
430
430
  if (defaultSubagentTailBus.hasSubscribers(n.task_id)) {
431
431
  defaultSubagentTailBus.publish(n.task_id, { type: "task_settled", taskId: n.task_id, status: n.status, ...(typeof n.seq === "number" ? { seq: n.seq } : {}), ...(n.summary ? { summary: redactSecrets(n.summary) } : {}) });
432
432
  }
433
- const hadRow = isFleetAgentTerminalNotification(n) ? (fleetPublisher?.onChildTerminal(n.sessionId ?? n.task_id, n.status, n.task_id, n.toolUseId) ?? false) : false;
434
433
  const parked = !legLive && Boolean(workflowCompletionInbox && spec.sessionId);
435
- completionDiagLog?.("task_notification_observed", { route: "bg-run-start", taskId: n.task_id, taskType: n.task_type, status: n.status, hadFleetRow: hadRow, legLive, parkedDurable: parked });
434
+ completionDiagLog?.("task_notification_observed", { route: "bg-run-start", taskId: n.task_id, taskType: n.task_type, status: n.status, legLive, parkedDurable: parked });
436
435
  if (parked) {
437
436
  void workflowCompletionInbox.enqueue(taskNotificationInboxEntry(spec.sessionId, elicitOwner ?? owner, n, Date.now(), taskId, opts?.priority)).catch((err) => completionDiagLog?.("park_enqueue_failed", { route: "bg-run-start", taskId: n.task_id, err: err instanceof Error ? err.message : String(err) }));
438
437
  }
@@ -467,7 +466,7 @@ export async function runInBackground(runner, spec, runStore, taskId, metrics, p
467
466
  const result = await compositeLeg();
468
467
  observedDone = result;
469
468
  causeAtDone = cancelCtrl.cause();
470
- await voidVerifierPauseCard(result, checkpointCards);
469
+ await voidVerifierPauseCard(result, runner);
471
470
  await flush();
472
471
  await flushPromptManifest();
473
472
  await settleLegResult(result);
@@ -230,7 +230,7 @@ export interface GovernancePosture {
230
230
  * (checkpoint → ask)今天**没有读口**:两条 list 读口都硬过滤 `state='STREAM_PENDING'`,而 park 完的行是
231
231
  * `PARKED`;唯一精确的连接键是 ask 行上的 `gate_token`,要按它反查得给店加一个新读口(SQL 面 = 双库集成
232
232
  * 门)。派生 `deriveAskId(...)` 也不行:它吃 `legKey`(= 上一腿 resume token 的摘要)、`parentToolCallId` 与这一问的
233
- * 类集([ref]);前两维从 checkpoint 行推不出来,类集在 park 行上老行缺席、续跑不补 —— 二腿/子代/老行形上会算出一个
233
+ * 类集([ref])与展示的动作(工具名 + 入参摘要,7.143.0);前两维从 checkpoint 行推不出来,类集在 park 行上老行缺席、续跑不补 —— 二腿/子代/老行形上会算出一个
234
234
  * **错**的 id —— 在「等的谁」这条通道上,错 id 比缺席坏得多(`http/active-run-conflict.ts` 的 `resumeEntryForGate` 的 default 臂是同一条纪律)。故诚实缺席。
235
235
  */
236
236
  export declare function governanceOriginOf(gate: {
@@ -76,6 +76,10 @@ export declare function serverCredentialValueSources(modelApiKeyEnv?: ModelApiKe
76
76
  * `holdOwnCredentialValues` 同一姿势:不收 + 说一次),经留痕座结构化点名那个变量一次 —— `warn` 行 `own_credential_value_not_withheld { name, reason }`
77
77
  * ({@link recordOwnCredentialValueNotWithheld};`reason` 是引擎拒句原文,从不回显值;起服第一步还没有 logger 时当拍经进程默认的 JSON 行日志器写 stderr,不缓冲),能力位 `inherit_env.own_credential_values_withheld`
78
78
  * 因此答 false(位真 ⇔ 值臂真扣)。引擎拒一个值时声明保持上一份,所以逐个交表的中间态都是上一份的超集。
79
+ * 🔴 **这一行是本服务唯一的写者**:值是网关密钥(`MODEL_API_KEY` / `ANTHROPIC_API_KEY`)时,进程输出里另有一行**引擎**的裸 `console.warn`(`a credential the engine was handed is …`)——
80
+ * 它写在 `createOpenAIBrain` / `createAnthropicBrain` 的收值口,那个口没有通告席(适配器配置没有 `onNotice`;`holdOwnCredentialValues` 不在 core 根导出),core 把这一形记为已知限制。
81
+ * 本仓不在引擎外面吞 console、也不拿声明口去抢引擎的「每进程每值说一次」账(下游不改上游座的语义);等 core 给席或让声明口的拒收记作已说。读数与翻面条件:
82
+ * `test/own-credential-value-boot-lines.test.ts`(真主进程;零裸行的期望以 `it.fails` 立着,core 改形那天转红再翻成常规钉)。
79
83
  * 返回声明的名单(只名、不值)。
80
84
  */
81
85
  export declare function declareServerWithheldEnv(modelApiKeyEnv?: ModelApiKeyEnvTable): readonly string[];
@@ -102,6 +102,18 @@ export declare class StampedSessionArchiveError extends SessionError {
102
102
  readonly sessionId: string;
103
103
  constructor(sessionId: string);
104
104
  }
105
+ /**
106
+ * local 店的具名拒(唯一铸点 `plugins/local-session-store.ts` 的 `metaRead`):这只会话的**属主读不出**。会话的行事实(属主 / 化身)只住在会话元数据边档的那一格;
107
+ * 7.104.0 之前写下、此后从没被读过的会话没有这一格,要从存档补铸它(第一次读时的惯例),存档又用不了(会话头坏 / 截断 / 日志结构走不通)——
108
+ * 于是没有任何一位调用方能被判成它的属主。要读它的行事实的店方法(`headOf` / 执行席的读 / 会话元数据读口)抛本错。
109
+ *
110
+ * 码与句子**就是**那只存档故障的(`cause` = 它;码不另铸 —— 只会是 `corrupt_session` / `invalid_session`,读不出而说不出在不在的 I/O 级故障不换型):
111
+ * 不认识本型的站点看到的与此前逐字相同(`sessionErrorHttp` 按码映射,wire 与运维日志不变)。认识本型的站点 —— 会话读面的属主读
112
+ * (`http/owner-access.ts` 的 `foldOwnerUnreadable`)—— 把它读成「没有这一行」:与对非属主、对不存在的会话同一只 404,原故障进运维日志。
113
+ */
114
+ export declare class SessionOwnerUnreadableError extends SessionError {
115
+ constructor(code: "corrupt_session" | "invalid_session", fault: SessionError);
116
+ }
105
117
  /** {@link StampedSessionArchiveError} 的 wire 固定句(fork 与改日志的推送同一句;不点名会话 id;出路在句里 —— 从头再同步照样撞)。 */
106
118
  export declare const STAMPED_SESSION_ARCHIVE_SENTENCE = "this session's stored archive still carries an owner stamp written by an earlier server version, so it cannot be forked or have its log replaced; nothing was written \u2014 delete the session and push it again";
107
119
  /**
@@ -13,6 +13,12 @@ export class StampedSessionArchiveError extends SessionError {
13
13
  this.name = "StampedSessionArchiveError";
14
14
  }
15
15
  }
16
+ export class SessionOwnerUnreadableError extends SessionError {
17
+ constructor(code, fault) {
18
+ super(code, fault.message, fault);
19
+ this.name = "SessionOwnerUnreadableError";
20
+ }
21
+ }
16
22
  export const STAMPED_SESSION_ARCHIVE_SENTENCE = "this session's stored archive still carries an owner stamp written by an earlier server version, so it cannot be forked or have its log replaced; nothing was written — delete the session and push it again";
17
23
  const CORRUPT_SESSION = { status: 500, errorCode: "corrupt_session" };
18
24
  const STORED_LOG_INVALID = { status: 500, errorCode: "invalid_session" };
@@ -11,7 +11,7 @@
11
11
  * 所以化身与属主不是同一条律(读法分三种,见下「两读」)。删除随行消失,同 id 重新登记铸新的。
12
12
  * · **两读(住客的三种问法,各读各的格,不混用)**:
13
13
  * K1 **住客自己的记录**(工作区、记忆、空间分区……按「这一位住客」落的东西)只按化身绑 —— 同一位住客的属主换代(推送重戳 / 认领)不换住客,记录照落;
14
- * K2 **以「这一行的这一代」为条件的写**(执行放置的换绑 / 收回、执行上下文的复核)比属主与化身整对,判官 = core 的行期望判官 `sessionRowMatches`
14
+ * K2 **以「这一行的这一代」为条件的写**(执行放置的换绑 / 收回、执行上下文的复核、会话通知门投递前的复核 —— 条目按属主排空)比属主与化身整对,判官 = core 的行期望判官 `sessionRowMatches`
15
15
  * (`execution-placement.ts` 的 `sameOccupant`,SQL `incarnation = ? AND owner <=> ?` 的对位)—— 读与写之间属主换了一代 ⇒ 不是判过的那一行;
16
16
  * K0 **只问属主的门**(授权门、暂存提交的属主 CAS)只读属主那一格。三种都读会话**行**;local 店的行 = 会话元数据边档那一格(引擎仓里的存档是匿名内容仓,
17
17
  * 存档头上的属主不是行事实 —— `plugins/local-session-store.ts` 头注)。
@@ -0,0 +1,106 @@
1
+ /**
2
+ * 🔴 **会话通知门 —— 一只会话外的事件进一只会话,只有这一处,两臂**。
3
+ *
4
+ * ## 门
5
+ * · **目标** = 会话 + 调用方判过的那一位住客(那一次头读的属主与化身令牌)+ 那一位无主时条目盖谁。门**不**替调用方判授权(属主门在调用方);门只保证交到的是那一位。
6
+ * · **活**:这只会话在本副本有活流 —— 判据一读:`runStore.getActiveTaskId(会话)` 交回的 run 在本副本的活流登记簿里(`steerableRuns`,属主是 HTTP 服务)
7
+ * ⇒ core `TaskStream.notify(事件, { priority: "next" })`。core 铸 `task_type: "external"`(外来的事件穿不上引擎内部车道的类型),载荷校验 / 消毒 / 围栏全在
8
+ * core;档位**写死** `"next"`(最早边界投递;core 的无参缺省是收官座 `"later"`,不写就静默推迟到 run 收尾)。core 的明确拒(码以 `notify.` 开头)原样交回;
9
+ * 别的抛 = 活流刚收尾的竞态 ⇒ 落空闲臂。
10
+ * · **空闲**:这只会话的完成收件箱(`externalNotificationInboxEntry`,external 键域;脱敏 / 限长在入箱口内)⇒ 下次开流排空成 `task_notification` 帧。
11
+ * 部署没有收件箱 ⇒ 如实交回 `no_inbox`(调用方各自答:HTTP 门 501,引擎完成告警)。
12
+ * · **复核住客**:两臂的投递动作(注入 / 入箱)之前**紧邻**一次头读,与目标的住客比 —— 判官是仓里那一只 `sameOccupant`(`execution-placement.ts`:属主 ∧ 化身,
13
+ * 写口与执行上下文解析器判的也是它):不在 ⇒ `withdrawn: gone`;两样里任一样变了 ⇒ `withdrawn: replaced`。会话店不铸化身(嵌入方自建的店)时两侧都没有令牌,
14
+ * 判官只剩属主那一半:属主没变 ⇒ 投,变了 ⇒ 撤回。这一读是动作之前的最后一次让出:判活读(及活臂刚收尾时的那次注入)都在它前面 ——
15
+ * 令牌永不复用,此刻还是那一位 ⇒ 判活读那一刻也是那一位 ⇒ 判出的 run 是它的 run。
16
+ * · **条目属主** = 目标住客的属主(调用方判过的那一读;复核放行 ⇒ 与这一读的属主逐字相等);那一位无主 ⇒ `ownerlessEntryOwner`(调用方交)。
17
+ *
18
+ * ## 竞态(两个调用方同一句话,只写在这里)
19
+ * · 判活之后、注入之前活流收尾:core 对已收尾的 run 不抛,按会话暂存、本进程同一会话的下一个 run 开轮时投递(core `notify` 契约);抛了 ⇒ 空闲臂(入箱前再复核一次)。
20
+ * · 判空闲之后、入箱之前同一会话开了新流:新腿开流那一拍的排空可能已读过收件箱 ⇒ 这一条在**再下一次**开流排空(至少一次,晚一次开流)。
21
+ * · 跨副本:活流在别的副本 ⇒ 本副本判空闲 ⇒ 入箱(收件箱随 run 店同一后端,哪个副本开流都排得到)。
22
+ * · 复核之后、入箱写落库之前别的副本删了这只会话并同 id 重新登记:条目带的是判过那一位的属主 —— 排空时按属主比开流者,另一位属主排不到;
23
+ * 同一属主重新登记的那一只会排到它(余窗 = 一次入箱写的时长;这一形不再收窄)。
24
+ *
25
+ * ## 调用方(两只,都经 {@link SessionNotifyDoor.deliver},不各写一份臂)
26
+ * · `POST /v1/sessions/:id/notify`(`http/routes/notify-wake.ts`):入站鉴权、验型、属主门在路由;属主 = 会话行的属主。
27
+ * · **Runner 之外的后台子代完成**({@link createBackgroundCompletionRelay}):停驻赎回腿的复活、操作员 `handle.resume` —— 这些续跑不是哪一次 run 内的调用,
28
+ * 没有调用车道(`ToolExecuteContext.onTaskNotification`),引擎把完成交给委派工具挂载期的 `background.notify`;那只 sink 在 `capabilities/scenarios.ts` 接一次,
29
+ * 交到委派树根会话的这只门。Runner 之内的调用照旧走调用车道(引擎 `resolveCompletionSink` = 调用 ?? 挂载),这只门不经手。
30
+ */
31
+ import type { ExternalNotificationInput, SessionHead, SubagentToolOptions, TaskStream } from "@sema-agent/core";
32
+ import { type WorkflowCompletionInbox } from "./orchestration/workflow-completion-inbox.js";
33
+ import type { DelegationRootWitness } from "./delegated-session-owner.js";
34
+ import type { HeadRead } from "./session-incarnation.js";
35
+ import type { Logger } from "./observability/logger.js";
36
+ /** 门的一次投递结局(闭集;调用方 `switch` 穷尽)。 */
37
+ export type SessionNotifyOutcome = {
38
+ readonly delivery: "live";
39
+ } | {
40
+ readonly delivery: "parked";
41
+ }
42
+ /** core 的明确拒(`notify.invalid_payload` 等):码与句原样。 */
43
+ | {
44
+ readonly delivery: "refused";
45
+ readonly code: string;
46
+ readonly message: string;
47
+ }
48
+ /** 会话空闲而部署没有收件箱:这一条无处可放。 */
49
+ | {
50
+ readonly delivery: "no_inbox";
51
+ }
52
+ /** 投递动作之前的复核:会话不在(`gone`)/ 住客不是目标那一位(`replaced`)⇒ 没交。 */
53
+ | {
54
+ readonly delivery: "withdrawn";
55
+ readonly reason: "gone" | "replaced";
56
+ };
57
+ /** 投递目标。 */
58
+ export interface SessionNotifyTarget {
59
+ readonly sessionId: string;
60
+ /** 调用方判过的那一位住客:那一次头读的属主与化身令牌(化身缺席 = 那一读里没有令牌;复核见头注)。它的属主也是收件箱条目的属主(排空时与开流者比对)。 */
61
+ readonly occupant: Pick<SessionHead, "owner" | "incarnation">;
62
+ /** 那一位无主(`occupant.owner === null`)时条目盖的属主(`null` = 无主条目,谁开流都排)。 */
63
+ readonly ownerlessEntryOwner: string | null;
64
+ }
65
+ export interface SessionNotifyDoor {
66
+ deliver(target: SessionNotifyTarget, event: ExternalNotificationInput): Promise<SessionNotifyOutcome>;
67
+ }
68
+ /** 门要读的四样(都由 HTTP 服务交:活流登记簿是它的,run 店、会话店与收件箱是它的依赖)。 */
69
+ export interface SessionNotifyDoorIo {
70
+ /** 会话 → 它的在飞 run(run 店的单活认领读)。部署没有 run 店 ⇒ 交 `undefined` ⇒ 恒判空闲。读失败按空闲。 */
71
+ readonly activeTaskIdOf: (sessionId: string) => Promise<string | null | undefined>;
72
+ /** 本副本持活 core 流的腿(taskId → 流)。 */
73
+ readonly liveRuns: ReadonlyMap<string, Pick<TaskStream, "notify">>;
74
+ readonly inbox: WorkflowCompletionInbox | undefined;
75
+ /** 会话店的按 id 头读(复核住客的那一读)。店没有 ⇒ `undefined`:调用方先判(路由 501;后台完成 relay 照交,门响亮抛 ⇒ relay 告警)。 */
76
+ readonly headOf: HeadRead | undefined;
77
+ }
78
+ export declare function createSessionNotifyDoor(io: SessionNotifyDoorIo): SessionNotifyDoor;
79
+ /** 委派工具挂载期的完成 sink 的型(取自 core 的选项型,不手抄)。 */
80
+ export type BackgroundCompletionSink = NonNullable<NonNullable<SubagentToolOptions["background"]>["notify"]>;
81
+ /** 把一只委派树根见证变成挂载期 sink 的工厂(`ScenarioDeps.backgroundCompletionSinkFor` 的型)。 */
82
+ export type BackgroundCompletionSinkFor = (root: DelegationRootWitness) => BackgroundCompletionSink;
83
+ export interface BackgroundCompletionRelayIo {
84
+ /** 会话通知门。门的属主是 HTTP 服务、场景层先于它装配 ⇒ 晚绑取值;未绑 ⇒ 交不出去(告警)。 */
85
+ readonly door: () => SessionNotifyDoor | undefined;
86
+ /** 交不出去的那一行告警写到这里(本仓的 `Logger`:写口自己吞掉输出故障、不抛 —— relay 的收尾臂依赖这一条)。 */
87
+ readonly logger: Pick<Logger, "warn">;
88
+ }
89
+ /** 交不出去的成因(闭集;告警 `background_completion_undelivered` 的 `reason`)。 */
90
+ export type BackgroundCompletionUndelivered = "root_gone" | "root_replaced" | "door_unbound" | "refused" | "no_inbox" | "relay_failed";
91
+ /**
92
+ * **Runner 之外的后台子代完成 → 委派树根会话的会话通知门**(挂载期 `background.notify` 的唯一实现)。
93
+ *
94
+ * · **地址** = 场景层交出的委派树根见证(`ScenarioContext.delegationRoot`:会话 id + 判断那一刻的根住客)—— 停驻赎回面按行上的 `rootSessionId`
95
+ * 与赎回腿写口放行的那一位住客交,请求腿按本请求会话与受理时那一读交;不读完成帧上的任何键定址。一层行的根就是父会话;更深的行(孙代)报给根 ——
96
+ * 委派终态的受众是整棵委派树的根会话(孙代的父是一只没人开流的委派会话,交给它等于没交)。
97
+ * · **目标住客 = 见证的那一位**(属主与化身,见证铸点那一读):门在投递动作之前紧邻地复核(头注「复核住客」)—— 不在 ⇒ 不交(`root_gone`);
98
+ * 不是见证的那一位(删了、同 id 被重新登记、属主变了)⇒ 不交(`root_replaced`):这棵树的结果不交给同 id 的下一位住客。条目属主 = 见证的属主;
99
+ * 根无主 ⇒ 无主条目(relay 不是谁的请求,没有请求主体可落)。见证那一读里根行不在(化身 `null`)⇒ 目标住客 = 「无主、无令牌」,只有不铸化身的店上
100
+ * 此刻也无主的根与它同(铸化身的店上它恒不同 ⇒ 不交,与停驻挂点「见证不在而根在 ⇒ 否决」同向)。
101
+ * · **事件** = 完成帧的 `task_id` / `status` / `summary` / `result` / `seq`(后台子代的 `seq` = 停驻周期号,同一只子代两次复活的完成在收件箱折叠键上不相撞)。
102
+ * 帧上别的键不过门:门的事件型只有这几位(活臂 core 铸 external,空闲臂入箱同一形),两臂一个形。
103
+ * · 交不出去一律一行 warn(`background_completion_undelivered`,带成因),不抛回引擎(sink 是 fire-and-forget)。
104
+ */
105
+ export declare function createBackgroundCompletionRelay(io: BackgroundCompletionRelayIo): BackgroundCompletionSinkFor;
106
+ //# sourceMappingURL=session-notify-door.d.ts.map