@sema-agent/server 7.137.0 → 7.138.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 (209) hide show
  1. package/MIGRATION.md +328 -139
  2. package/README.md +1 -0
  3. package/README.zh-CN.md +1 -0
  4. package/USAGE.md +67 -20
  5. package/dist/approval-card.d.ts +21 -14
  6. package/dist/approval-card.js +1 -0
  7. package/dist/audit.js +1 -0
  8. package/dist/background-agent-name.d.ts +5 -0
  9. package/dist/background-agent-name.js +5 -0
  10. package/dist/boot/budget-tracing.d.ts +1 -12
  11. package/dist/boot/deployment-mods.d.ts +46 -16
  12. package/dist/boot/deployment-mods.js +82 -6
  13. package/dist/boot/governance-seams.d.ts +10 -17
  14. package/dist/boot/governance-seams.js +5 -17
  15. package/dist/boot/leader.d.ts +3 -0
  16. package/dist/boot/leader.js +1 -0
  17. package/dist/boot/org-delivery.js +15 -2
  18. package/dist/boot/resolve-spec.d.ts +3 -0
  19. package/dist/boot/resolve-spec.js +23 -12
  20. package/dist/boot/retention-lane.d.ts +11 -5
  21. package/dist/boot/retention-lane.js +2 -2
  22. package/dist/boot/runner-deps.d.ts +7 -3
  23. package/dist/boot/runner-deps.js +1 -0
  24. package/dist/boot/stage-04-stores.d.ts +1 -12
  25. package/dist/boot/stage-05-execution-env.d.ts +3 -12
  26. package/dist/boot/stage-05-execution-env.js +7 -4
  27. package/dist/boot/stage-06-runners.d.ts +3 -12
  28. package/dist/boot/stage-06-runners.js +5 -2
  29. package/dist/boot/stage-07-capability-layer.d.ts +3 -12
  30. package/dist/boot/stage-07-capability-layer.js +2 -2
  31. package/dist/boot/stage-08-reapers.d.ts +3 -12
  32. package/dist/boot/stage-09-leader.d.ts +3 -12
  33. package/dist/boot/stage-09-leader.js +2 -2
  34. package/dist/boot/stage-10-http-server.d.ts +3 -12
  35. package/dist/boot/stage-10-http-server.js +4 -2
  36. package/dist/boot/web-admin.d.ts +5 -2
  37. package/dist/boot/web-admin.js +3 -3
  38. package/dist/brain.js +5 -5
  39. package/dist/budget.d.ts +17 -15
  40. package/dist/budget.js +2 -2
  41. package/dist/capabilities/mcp-probe.d.ts +36 -11
  42. package/dist/capabilities/mcp-probe.js +44 -5
  43. package/dist/capability-facts.d.ts +6 -0
  44. package/dist/capability-facts.js +5 -0
  45. package/dist/checkpoint-identity.d.ts +13 -6
  46. package/dist/config-catalog.js +7 -1
  47. package/dist/config-types.d.ts +15 -7
  48. package/dist/config.js +8 -5
  49. package/dist/deployment-shape.d.ts +6 -6
  50. package/dist/gate-settlement.d.ts +71 -0
  51. package/dist/gate-settlement.js +40 -0
  52. package/dist/hooks/classic-stations.d.ts +1 -0
  53. package/dist/hooks/classic-stations.js +1 -0
  54. package/dist/hooks/command-spawn.d.ts +2 -2
  55. package/dist/hooks/hook-runner.js +14 -14
  56. package/dist/http/admission.js +4 -2
  57. package/dist/http/dispatch.d.ts +4 -0
  58. package/dist/http/dispatch.js +3 -0
  59. package/dist/http/durable-run-leg.js +4 -2
  60. package/dist/http/memory-compliance-failure-reply.js +3 -0
  61. package/dist/http/resume-legs.d.ts +5 -11
  62. package/dist/http/resume-legs.js +136 -91
  63. package/dist/http/route-ctx.d.ts +30 -51
  64. package/dist/http/route-ctx.js +2 -5
  65. package/dist/http/routes/a2a-serve.js +10 -4
  66. package/dist/http/routes/agents-roster.d.ts +1 -1
  67. package/dist/http/routes/agents-roster.js +5 -1
  68. package/dist/http/routes/approvals-assistant.js +6 -6
  69. package/dist/http/routes/capabilities.js +12 -10
  70. package/dist/http/routes/mcp-reconnect.d.ts +3 -2
  71. package/dist/http/routes/mcp-reconnect.js +7 -5
  72. package/dist/http/routes/memory-consolidation.d.ts +1 -1
  73. package/dist/http/routes/memory-origin.d.ts +1 -1
  74. package/dist/http/routes/rules.js +3 -3
  75. package/dist/http/routes/runs.js +11 -7
  76. package/dist/http/routes/sessions.js +6 -4
  77. package/dist/http/routes/side-query.js +2 -3
  78. package/dist/http/routes/tasks.js +792 -780
  79. package/dist/http/server.d.ts +31 -25
  80. package/dist/http/wire-types.d.ts +14 -6
  81. package/dist/identity-source.d.ts +1 -1
  82. package/dist/index.d.ts +2 -0
  83. package/dist/index.js +1 -0
  84. package/dist/leader/wire.d.ts +7 -0
  85. package/dist/leader/wire.js +5 -4
  86. package/dist/mcp-assertion-header.d.ts +51 -40
  87. package/dist/mcp-assertion-header.js +10 -11
  88. package/dist/mcp-authorization-face.d.ts +27 -0
  89. package/dist/mcp-authorization-face.js +15 -0
  90. package/dist/memory-operator-faces.d.ts +13 -4
  91. package/dist/memory-operator-faces.js +1 -17
  92. package/dist/memory-sync-client.js +72 -57
  93. package/dist/memory-sync.d.ts +1 -1
  94. package/dist/model-route-gap.d.ts +6 -22
  95. package/dist/model-route-gap.js +7 -5
  96. package/dist/mods-declaration.d.ts +110 -0
  97. package/dist/mods-declaration.js +109 -0
  98. package/dist/observability/err-text.d.ts +5 -0
  99. package/dist/observability/err-text.js +9 -0
  100. package/dist/observability/fail-open.d.ts +5 -1
  101. package/dist/observability/fail-open.js +5 -1
  102. package/dist/observability/secret-env-scrub.d.ts +1 -1
  103. package/dist/observability/secret-env-scrub.js +1 -1
  104. package/dist/parked-decide.d.ts +15 -2
  105. package/dist/parked-decide.js +14 -8
  106. package/dist/peer-seat.d.ts +150 -0
  107. package/dist/peer-seat.js +239 -0
  108. package/dist/permission-rule-vocab.d.ts +7 -7
  109. package/dist/permission-rule-vocab.js +3 -3
  110. package/dist/plain-string-record.d.ts +38 -0
  111. package/dist/plain-string-record.js +38 -0
  112. package/dist/plugins/background-agent-store-sql.js +6 -3
  113. package/dist/plugins/caching-session-store.d.ts +5 -0
  114. package/dist/plugins/caching-session-store.js +6 -0
  115. package/dist/plugins/checkpoint-store-sql.d.ts +15 -20
  116. package/dist/plugins/checkpoint-store-sql.js +8 -8
  117. package/dist/plugins/file-history-store-sql.js +3 -2
  118. package/dist/plugins/k8s-pod-reaper.d.ts +2 -8
  119. package/dist/plugins/k8s-pod-reaper.js +3 -3
  120. package/dist/plugins/local-checkpoint-store.d.ts +1 -11
  121. package/dist/plugins/local-checkpoint-store.js +0 -1
  122. package/dist/plugins/local-session-store.d.ts +66 -27
  123. package/dist/plugins/local-session-store.js +60 -28
  124. package/dist/plugins/mailbox-store-sql.d.ts +11 -3
  125. package/dist/plugins/mailbox-store-sql.js +103 -15
  126. package/dist/plugins/memory-sync-store-pg.d.ts +16 -5
  127. package/dist/plugins/memory-sync-store-tidb.d.ts +3 -3
  128. package/dist/plugins/one-time-migrations.d.ts +6 -3
  129. package/dist/plugins/one-time-migrations.js +3 -4
  130. package/dist/plugins/permission-rule-store-sql.js +21 -13
  131. package/dist/plugins/pg-session-storage.d.ts +10 -2
  132. package/dist/plugins/pg-session-storage.js +18 -5
  133. package/dist/plugins/remote-env-adb.d.ts +3 -1
  134. package/dist/plugins/remote-env-adb.js +1 -1
  135. package/dist/plugins/remote-env-device.d.ts +3 -1
  136. package/dist/plugins/remote-env-device.js +1 -1
  137. package/dist/plugins/remote-env-e2b.d.ts +2 -0
  138. package/dist/plugins/remote-env-e2b.js +1 -1
  139. package/dist/plugins/remote-env-host.d.ts +3 -1
  140. package/dist/plugins/remote-env-host.js +1 -1
  141. package/dist/plugins/remote-env-k8s.d.ts +3 -1
  142. package/dist/plugins/remote-env-k8s.js +1 -1
  143. package/dist/plugins/remote-env-local-docker.d.ts +2 -0
  144. package/dist/plugins/remote-env-local-docker.js +1 -1
  145. package/dist/plugins/remote-env-ssh.d.ts +3 -1
  146. package/dist/plugins/remote-env-ssh.js +1 -1
  147. package/dist/plugins/retention-store-sql.d.ts +14 -2
  148. package/dist/plugins/retention-store-sql.js +6 -0
  149. package/dist/plugins/session-fork.d.ts +10 -13
  150. package/dist/plugins/session-fork.js +5 -25
  151. package/dist/plugins/session-ownership.d.ts +7 -0
  152. package/dist/plugins/session-ownership.js +5 -0
  153. package/dist/plugins/session-workspace-cell.d.ts +17 -0
  154. package/dist/plugins/session-workspace-cell.js +38 -0
  155. package/dist/plugins/store-backend.js +7 -7
  156. package/dist/plugins/tidb-pool.js +6 -0
  157. package/dist/plugins/tidb-session-store.d.ts +17 -2
  158. package/dist/plugins/tidb-session-store.js +16 -5
  159. package/dist/plugins/tool-result-store-sql.d.ts +13 -6
  160. package/dist/plugins/tool-result-store-sql.js +8 -7
  161. package/dist/plugins/worktree-isolation.d.ts +3 -2
  162. package/dist/plugins/worktree-isolation.js +1 -7
  163. package/dist/project-identity.d.ts +1 -14
  164. package/dist/project-identity.js +0 -4
  165. package/dist/rules-consent.js +6 -4
  166. package/dist/run-local.js +12 -6
  167. package/dist/runs.d.ts +7 -1
  168. package/dist/runs.js +6 -1
  169. package/dist/sandbox-git-plane.d.ts +2 -2
  170. package/dist/server-secret-env.d.ts +6 -3
  171. package/dist/server-secret-env.js +4 -2
  172. package/dist/session-incarnation.d.ts +4 -5
  173. package/dist/session-incarnation.js +2 -2
  174. package/dist/session-workspace.d.ts +130 -0
  175. package/dist/session-workspace.js +83 -0
  176. package/dist/spec-fields.d.ts +1 -1
  177. package/dist/standing-answer-grant.d.ts +2 -13
  178. package/dist/standing-answer-grant.js +0 -7
  179. package/dist/task-a2a.d.ts +44 -14
  180. package/dist/task-a2a.js +46 -20
  181. package/dist/task-mcp.d.ts +33 -20
  182. package/dist/task-mcp.js +46 -22
  183. package/dist/tool-approval.d.ts +32 -8
  184. package/dist/tool-approval.js +49 -12
  185. package/dist/trace/core-keyset-guard.d.ts +33 -18
  186. package/dist/trace/engine-notice-wire.d.ts +1 -1
  187. package/dist/trace/engine-notice-wire.js +2 -0
  188. package/dist/trace/exit-seat.d.ts +20 -1
  189. package/dist/trace/exit-seat.js +3 -0
  190. package/dist/trace/extension-call-face.d.ts +33 -0
  191. package/dist/trace/extension-call-face.js +39 -0
  192. package/dist/trace/project.d.ts +25 -7
  193. package/dist/trace/project.js +29 -3
  194. package/dist/trace/redact.js +42 -13
  195. package/dist/trace/self-minted-wire.d.ts +129 -10
  196. package/dist/trace/self-minted-wire.js +62 -3
  197. package/dist/trace/wire-projection-faces.d.ts +6 -6
  198. package/dist/trace/wire-projection-faces.js +6 -5
  199. package/dist/user-authored-text.d.ts +2 -2
  200. package/dist/user-authored-text.js +16 -1
  201. package/dist/web-admin-client/center-request.d.ts +3 -2
  202. package/dist/web-admin-client/center-request.js +2 -3
  203. package/dist/web-admin-client/skills-mcp.d.ts +8 -4
  204. package/dist/web-admin-client/skills-mcp.js +31 -18
  205. package/dist/web-admin-client/types.d.ts +5 -0
  206. package/package.json +3 -3
  207. package/wire-changes/server@7.138.0.json +446 -0
  208. package/dist/http/mcp-remote-text.d.ts +0 -5
  209. package/dist/http/mcp-remote-text.js +0 -7
@@ -1,11 +1,14 @@
1
1
  import http from "node:http";
2
+ import type { MemoryComplianceFaces } from "../memory-operator-faces.js";
3
+ import type { SideQueryAccounting } from "../budget.js";
2
4
  import type { IncomingMessage } from "node:http";
3
- import { type Runner, type TaskSpec, type TaskResult, type WorkflowRunStore, type MemoryEntry, type MemoryExportBundle, type MemoryImportReport, type EntryProvenanceAccount, type MemoryErasureAttestation, type MemoryEntryOrigin, type OriginClearanceRow, type SessionMemoryStatus, type ConsolidationRunReceipt, type ConsolidationDriverRunRow, type SideQuerySpec, type SideQueryResult, type Brain, type EngineNotice } from "@sema-agent/core";
5
+ import { type Runner, type TaskSpec, type TaskResult, type WorkflowRunStore, type MemoryEntry, type MemoryExportBundle, type MemoryImportReport, type EntryProvenanceAccount, type MemoryEntryOrigin, type OriginClearanceRow, type SessionMemoryStatus, type ConsolidationRunReceipt, type ConsolidationDriverRunRow, type SideQuerySpec, type SideQueryResult, type Brain, type EngineNotice } from "@sema-agent/core";
4
6
  import type { TaskRequestBody } from "./wire-types.js";
5
7
  import type { ServiceConfig } from "../config-types.js";
6
8
  import { type SessionMirrorRuling } from "../web-admin-client/facade.js";
7
9
  import type { RestartSignal } from "@sema-agent/settings-schema/fleet";
8
10
  import { type OwnerAwareSessionStore } from "../security.js";
11
+ import type { PeerDesk, PeerLease } from "../peer-seat.js";
9
12
  import type { RunStore, ImageIndex, ImageBake, CheckpointStoreFull, ResumeAnchorStore, ApprovalExemptionStore, ApprovalNonceStore, ServiceSessionPolicyStore, ServiceFileHistoryStore, ToolResultStoreFull, StoreBackend } from "../plugins/store-backend.js";
10
13
  import type { RuleConsentLane } from "../rules-consent.js";
11
14
  import { type MemorySyncRequest, type MemorySyncResponse } from "../memory-sync.js";
@@ -57,7 +60,7 @@ export interface RequestAuth {
57
60
  * 平铺形的直接后果 = main.ts 一个上千行的装配字面量,键与键之间没有任何「属于同一件事」的编译期表达,
58
61
  * 键落错家/从未接线只能靠源码钉逐个盯(1.300/1.301/1.304/1.307/1.310 五起真缺陷都是这个病族)。
59
62
  *
60
- * 药:七个**窄**接口 + 顶层必填五件。装配点按组构造(每组一个带类型标注的中间 const ⇒ 受 TS 多余
63
+ * 药:七个**窄**接口 + 顶层必填七件。装配点按组构造(每组一个带类型标注的中间 const ⇒ 受 TS 多余
61
64
  * 属性检查保护),键放错组当场编译红。
62
65
  *
63
66
  * 兼容形(本批**不动消费点**):`ServiceDeps` = 平铺视图 ∩ 组槽。旧的平铺构造(96 个测试文件 +
@@ -65,8 +68,8 @@ export interface RequestAuth {
65
68
  * 入口摊平回平铺视图,server.ts 内的 ~700 处 `deps.x` 消费点原样保留。消费点迁移是后批的事——
66
69
  * 一步到位需要同批改 ~700 处 src + ~800 处 test 键位,与「先立形、后搬家」相比没有额外收益。
67
70
  */
68
- /** createHttpServer 的**必填五件**——引擎、部署配置、请求→TaskSpec 的映射、side-query 执行席、读口场景门。
69
- * 五者恒在场,故留在顶层(不进任何可选组):没有它们 server 根本起不来,包一层可选组只会把编译期
71
+ /** createHttpServer 的**必填七件**——引擎、部署配置、请求→TaskSpec 的映射、选路席、side-query 执行席、读口场景门、对端座的 desk。
72
+ * 七者恒在场,故留在顶层(不进任何可选组):没有它们 server 根本起不来,包一层可选组只会把编译期
70
73
  * 错误推迟到运行期(本仓 E-HIGH-1 病族的成因就是「可选键从未接线、编译期无声」)。 */
71
74
  export interface ServiceCoreDeps {
72
75
  runner: Runner;
@@ -105,11 +108,15 @@ export interface ServiceCoreDeps {
105
108
  * `opts.unattendedLeg`([ref] 候裁 18,codex r3 F1;[ref] ① 改形):续跑家族在 ctx 带无人值守标(`CheckpointCtx.unattendedLeg`)时递 `true` ——
106
109
  * 装配器在**全部折叠之后**经首跑同一只合并口 `withUnattendedLeg` 重挂那条腿(`default` + 按类作答的 onAsk)。只由续跑腿
107
110
  * 按服务端写的 ctx 位递,绝不从请求体读。 */
108
- resolveSpec: (body: TaskRequestBody, req: IncomingMessage | undefined, auth?: RequestAuth, opts?: {
111
+ /** `opts.lease`(必填):这条腿的**宿主租约** —— 装配在它之下登记本腿交给引擎的对端声明(`peerDesk.declare(草稿, opts.lease)`)。
112
+ * 租约由两只漏斗(受理 `prepareSpec` / 续跑 `resolveResumeSpecOrBlocked`)在调用本函数**之前**开、由漏斗的失败出口或拿到产物的那段作用域放;
113
+ * 装配只借用,不放、不留。实现方自铸的声明归自己的 desk 时可以不理它。 */
114
+ resolveSpec: (body: TaskRequestBody, req: IncomingMessage | undefined, auth: RequestAuth | undefined, opts: {
109
115
  leg?: "fresh" | "resume";
110
116
  liveStream?: boolean;
111
117
  onNotice?: (notice: EngineNotice) => void;
112
118
  unattendedLeg?: true;
119
+ lease: PeerLease;
113
120
  }) => TaskSpec | Promise<TaskSpec>;
114
121
  /**
115
122
  * [ref] 场景 hands lane 选路:按 `resolveSpec` 刚产出的 TaskSpec 取本任务的执行 Runner。
@@ -134,6 +141,16 @@ export interface ServiceCoreDeps {
134
141
  * `createScenarioGate` / `ScenarioGate` / `ScenarioGateLeg` / `ScenarioGateSources`)。
135
142
  */
136
143
  scenarioGate: import("../capabilities/scenarios.js").ScenarioGate;
144
+ /**
145
+ * 对端座的 desk(`peer-seat.ts`):MCP http 请求的凭据头 / stdio 子进程的环境 / A2A 发送的头,发送时只有它一只作答者;登记一条活到它的 holder 都空。
146
+ * 本层用它开**宿主租约**:受理与续跑两只漏斗在装配之前各开一只(`lease("fresh")` / `lease("resume")`),经 `resolveSpec` 的 `opts.lease` 交给装配,
147
+ * 随产物(`PreparedTaskSubmission.peerLease` / 续跑结果)交给拿到它的那段作用域,在那段作用域的 `finally` 放;活过那段作用域的驱动经 `heldFor` 自持一只。
148
+ * 这只 desk 另有三处用法,不止这两只漏斗:① 探测口 `POST /v1/capabilities/mcp/probe` 的调用方臂在走查里开一只 `probe` 租约(三道前置拒之后开,拨号落地才放);
149
+ * ② 两张会话面板(`GET /v1/sessions/:id/mcp` · `…/a2a`)与探测走查拨名册声明时递它的座(`desk.seat.answer`),不开租约;③ 名册声明在起服时登记在一只进程寿命的 `roster` 租约下
150
+ * (stage-07 / run-local 开,从不放),并且每只 Runner 的 `peerSeat` 是它的座。登记表的两个读数是 `/metrics` 的 `peer_registry_entries` / `peer_registry_holders`。
151
+ * 🔴 **必填**:没有 MCP / A2A 凭据的嵌入也给一只 `createPeerDesk()`(零 IO)—— 一个部署只有一张登记表,缺席不是「没有座」的另一种读法。
152
+ */
153
+ peerDesk: PeerDesk;
137
154
  }
138
155
  /** **durable 持久面**:每个键 present ⇔ 对应路由/能力位在场,absent ⇒ 501(诚实缺席),
139
156
  * 绝不静默降级。装配点 = main.ts 的 store backend 开箱段。 */
@@ -471,12 +488,7 @@ export interface ServiceSeamDeps {
471
488
  * 🔴 `erase` 的入参是 `unknown` 而不是 `EraseMemoryEntriesInput`:HTTP 层拿到的是一个**未校验**的对象,
472
489
  * 三选一选择子的整体判决是 core 的单一属主面(`erasureRequestInvalid`)。写成具体类型就得在路由里断言
473
490
  * 一次「它是合法输入」——那句断言是假的,而且正好把类型检查关灯。 */
474
- memoryCompliance?: {
475
- provenanceOf: (entryId: string) => Promise<EntryProvenanceAccount>;
476
- erase: (input: unknown, opts?: {
477
- allowedScopes?: readonly string[];
478
- }) => Promise<MemoryErasureAttestation>;
479
- };
491
+ memoryCompliance?: MemoryComplianceFaces;
480
492
  /** [ref] seam①([ref] §S-7,core 7.0.2 [ref] 件1):**会话记忆状态读面**。启用
481
493
  * `GET /v1/sessions/:id/memory-status`(owner 门,tenant 自助读 —— 不是 operator 面)。
482
494
  * main.ts 把它接到 `createSessionMemoryStatusFace` 的产物上(单实例引擎,SQL capture 店同座喂入)。
@@ -592,19 +604,7 @@ export interface ServiceSeamDeps {
592
604
  /** [ref] side-query 记账 seam(budget.ts createSideQueryAccountant——与 createTracer 同一装配点、
593
605
  * 同一组 sink 实例:costQuota/model_cost 指标/fleetUsage/fleetLease,四路同语义)。缺席=零记账
594
606
  * (手搭测试 deps;生产 main.ts 恒传)。 */
595
- sideQueryAccounting?: (principal: string | undefined, r: {
596
- model: string;
597
- family?: "input-includes-cached" | "input-excludes-cached";
598
- usage?: {
599
- input?: number;
600
- output?: number;
601
- cacheRead?: number;
602
- cacheWrite?: number;
603
- cost?: {
604
- total?: number;
605
- };
606
- };
607
- }) => void;
607
+ sideQueryAccounting?: SideQueryAccounting;
608
608
  /** [ref](core 7.32.0):parked 赎回面工厂(按行解析驱动哪只 Agent 工具 + 同出一张列表的继承座;属主 `parked-revive-surface.ts`)。
609
609
  * 缺席 = 这台部署没有 park 底座(checkpointStore / backgroundAgentStore 任一缺席),bg 赎回臂不接线。 */
610
610
  parkedReviveSurface?: import("../parked-revive-surface.js").ParkedReviveSurfaceFactory;
@@ -699,6 +699,12 @@ export interface ServiceDeploymentDeps {
699
699
  * seat answer + the static patch judge). Caps `credentialPlane.sandboxGit` projects it as is. Absent = not built by the composition root
700
700
  * (a fixture) ⇒ the same judge on config alone, the seat counted as not asked. */
701
701
  sandboxGitPlane?: import("../sandbox-git-plane.js").SandboxGitPlane;
702
+ /**
703
+ * 本进程起服装上的**运维声明** mod 包数(部署座装配口 `assembleDeploymentMods` 的 `loaded.length`;本服务自铸的注册不算)。一个事实两个出口,同读这一格:
704
+ * `/health` 的 `mods: { deploymentPackages }` 与 `/v1/capabilities` 的 `modsDeploymentPackages: true` —— 都只在 > 0 时在场;0 / 缺席(没声明,或不是由
705
+ * composition root 装起来的夹具)⇒ 两键都缺席,两面与没有声明的部署逐字节同。
706
+ */
707
+ deploymentModPackages?: number;
702
708
  /**
703
709
  * Graceful drain — shared mutable state between main.ts (flips `draining` on SIGTERM) and this server:
704
710
  * • while `draining`, NEW model-running submissions (the billable submit paths) get 503 + Retry-After, so an
@@ -861,7 +867,7 @@ export interface ServiceDepGroups {
861
867
  deployment?: ServiceDeploymentDeps;
862
868
  knobs?: ServiceKnobDeps;
863
869
  }
864
- /** 平铺视图 = 必填五件 + 其余七组的全可选并集。**派生**自组接口,故与分组形永不漂。 */
870
+ /** 平铺视图 = 必填七件 + 其余七组的全可选并集。**派生**自组接口,故与分组形永不漂。 */
865
871
  export type FlatServiceDeps = ServiceCoreDeps & Partial<ServiceStoreDeps & ServiceCoordinatorDeps & ServiceSeamDeps & ServiceObservabilityDeps & ServiceGovernanceDeps & ServiceDeploymentDeps & ServiceKnobDeps>;
866
872
  /** createHttpServer 的入参:两形皆可(平铺=存量,分组=推荐)。同名键两形都给时,**顶层显式键赢**
867
873
  * (含显式 `undefined`)——一处装配点只该用一形,混用时以肉眼最近的那个为准。 */
@@ -6,8 +6,9 @@
6
6
  * cut: ONLY TaskRequestBody (its field types are inline or core imports — no other server.ts-local satellites);
7
7
  * sibling request/response shapes can migrate here later on the same pattern.
8
8
  */
9
- import type { A2aServerSpec, AgentDefinition, ApprovalReach, PermissionMode, SkillSpec, TaskSpec } from "@sema-agent/core";
9
+ import type { AgentDefinition, ApprovalReach, PermissionMode, SkillSpec, TaskSpec } from "@sema-agent/core";
10
10
  import type { RequestMcpServerSpec } from "../mcp-assertion-header.js";
11
+ import type { RequestA2aPeer } from "../task-a2a.js";
11
12
  import type { wiringManifestFaceEventData } from "../trace/project.js";
12
13
  import type { buildSyncSubmitBody } from "./sync-submit-body.js";
13
14
  import type { EntryProblem } from "../entry-problems.js";
@@ -554,22 +555,29 @@ export interface TaskRequestBody {
554
555
  * 与 `principalHeader` **互斥**:同设 ⇒ 新提交腿同步 400 `request.body_shape`(拒句与契约包解析期那一句逐字相同);值不是字符串 ⇒ 400
555
556
  * `request.field_invalid`。耐久 resume 腿不拒(重放存量 body),也**不摘 server**(摘了,park 在它工具审批上的任务批准后撞 `resume.tool_unavailable`
556
557
  * 且每次重试重放同一份 body):降级到升级前的读法 —— server 照挂、`principalHeader` 胜、`assertionHeader` 不生效,一行 warn `mcp_assertion_header_ignored`。
557
- * ⚠️ 本版**只承载、不发送**:引擎的 http 传输尚无断言头座,
558
- * 声明被接收、server 照常挂载但不带断言头,并有一行运维 info `mcp_assertion_header_not_sent { server }`;发送随引擎座。 */
558
+ * ⚠️ **只承载、不发送**:引擎的断言头是固定的名、值由部署的对端座作答,本部署没有断言来源 ——
559
+ * 声明被接收、server 照常挂载但不带断言头,并有一行运维 info `mcp_assertion_header_not_sent { server }`。
560
+ *
561
+ * `transport.env`(stdio)/ `transport.headers`(http)是这台 server 的**件**:server 交给引擎的声明里不带它们,件由部署的对端座在起子进程 / 发请求时按原字节作答
562
+ * (只对胜出部署同名判的条目登记;件的名与值由引擎在每次发送时判,不合 ⇒ 那一台不连、兄弟照挂)。 */
559
563
  mcpServers?: RequestMcpServerSpec[];
560
564
  /** [DESIGN-269 车1] Caller-supplied per-request A2A peers (remote agents this task may call), aligned to
561
565
  * core `A2aServerSpec`. 🔒 honored on a SINGLE-USER deployment (task-a2a.ts `a2aInjectionHonored`, the same
562
566
  * predicate `capabilities.a2aInjection` advertises) — the requester is the super-admin of their OWN worker,
563
567
  * on ANY execution lane (an A2A call is a plain outbound HTTPS request from the worker, so the lane where
564
568
  * tool calls run is irrelevant); IGNORED on multi-tenant (a tenant can't point the shared worker at an
565
- * agent of their choosing). A2A has no operator lock and no compliance capability today. A new submission
569
+ * agent of their choosing). The gate also closes under the operator lock `a2a` and under a compliance posture that denies `a2a_peers`
570
+ * (the engine refuses a task that declares a peer under either): under the lock a request that carries `a2aPeers` is refused
571
+ * 400 `config.locked_key` at the head of the request (the same code and status table as the MCP lock); on a multi-tenant
572
+ * deployment or under a compliance denial the list is ignored and said (`a2a_injection_ignored`). A new submission
566
573
  * with a malformed list or entry is refused 400 (`assertRequestA2aEntries`: not an array / over the 32-entry
567
574
  * cap / an entry that is not an object / a key outside {@link A2A_PEER_ENTRY_KEYS} / a key whose value is
568
575
  * unusable / a url or cardUrl carrying a credential) — the same refusal shape as `mcpServers`, whatever the lane.
569
576
  * 🔴 Every skill a peer advertises mounts `egress:true` + `effect:"write"` (core's ruling) — declaring a
570
577
  * peer is declaring an outbound WRITE channel, not a data source. Validated + gated downstream; the
571
- * center/config baseline wins on a name clash (the peer name IS the tool namespace). */
572
- a2aPeers?: A2aServerSpec[];
578
+ * center/config baseline wins on a name clash (the peer name IS the tool namespace). `headers` are the peer's PIECES: the declaration the
579
+ * server hands the engine carries none of them — the deployment's peer seat answers them, as written, at each send to the declared origins. */
580
+ a2aPeers?: RequestA2aPeer[];
573
581
  /** CC parity (Workflow super-set): per-task activation of the LLM-authored workflow engine (core `run_workflow`).
574
582
  * `true` → `spec.selfOrchestration` (the engine mounts run_workflow), gated on the deployment's `selfOrchestrationEnabled`
575
583
  * (else a harmless no-op) + core's `allowWorkflows` per-principal entitlement on multi-tenant (single-user honors
@@ -13,7 +13,7 @@
13
13
  * `ALLOW_UNAUTHED_WRITES`)、以及别人替属主按下的那一跑(受理请求验过的主体不是这一行的属主:唤醒 / 决定投递铸的新一跑)。读者一律按「不合格」处置
14
14
  * (fail-closed),不分辨三种成因;存量行不回填(没有事实可回填)。
15
15
  *
16
- * 化身号 `principalInc`:令牌 `sub_inc` 声明原样,经契约包 `VerifiedIdentity.principalInc` 读(settings-schema 14.7.0 加的可选字段);本版契约包还没有这一位 ⇒ 恒 `null`,
16
+ * 化身号 `principalInc`:令牌 `sub_inc` 声明原样,经契约包 `VerifiedIdentity.principalInc` 读(settings-schema 14.7.0 加的可选字段,已提货:验证腿随身份带出);本版接线未做 ⇒ 恒 `null`,
17
17
  * 接线点在 `admitRunIdentity` 的 `sso` 臂(不在本仓另定一份读法)。
18
18
  */
19
19
  /** 验过的身份来源的**闭集**(序 = 判定次序 = `/health.auth.kinds` 的出现序;加一个来源 = 加一个词,读它的各只穷举 `switch` 不接就编译红)。 */
package/dist/index.d.ts CHANGED
@@ -26,6 +26,8 @@ export type { TaskRequestBody } from "./http/wire-types.js";
26
26
  export { ensureCheckpointCtxSealing } from "./sealed-key.js";
27
27
  export type { CheckpointCtxSealing } from "./http/checkpoint-ctx.js";
28
28
  export { createHttpServer, type ServiceDeps, type RequestAuth, type FlatServiceDeps, type ServiceDepGroups, type ServiceCoreDeps, type ServiceStoreDeps, type ServiceCoordinatorDeps, type ServiceSeamDeps, type ServiceObservabilityDeps, type ServiceGovernanceDeps, type ServiceDeploymentDeps, type ServiceKnobDeps, } from "./http/server.js";
29
+ export { createPeerDesk } from "./peer-seat.js";
30
+ export type { PeerDesk, PeerLease, McpDraft, A2aDraft } from "./peer-seat.js";
29
31
  export { createSideQueryLane, type KeyResolver, type SideQueryLane, type SideQueryLaneCtx } from "./boot/side-query-lane.js";
30
32
  export { createKeyResolver, resolveModelApiKey, type ModelKeyRef } from "./key-resolver.js";
31
33
  export { isModelKeyPoison, ModelKeyPoisonedError, type ModelKeyPoison, type ModelKeyPoisonReason } from "./key-resolver.js";
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ export { createAuthorizer, principalFrom, HttpError, } from "./security.js";
22
22
  export { memoryScopeFor, memoryEngineBackendFor } from "./memory-scope.js";
23
23
  export { ensureCheckpointCtxSealing } from "./sealed-key.js";
24
24
  export { createHttpServer, } from "./http/server.js";
25
+ export { createPeerDesk } from "./peer-seat.js";
25
26
  export { createSideQueryLane } from "./boot/side-query-lane.js";
26
27
  export { createKeyResolver, resolveModelApiKey } from "./key-resolver.js";
27
28
  export { isModelKeyPoison, ModelKeyPoisonedError } from "./key-resolver.js";
@@ -91,6 +91,13 @@ export interface LeaderWireConfig {
91
91
  * 分租户,只补席不包裹 = 归因恒 undefined = 假绿。缺席 ⇒ 展开空对象(与接线前逐字相同)。
92
92
  */
93
93
  tracer?: RunnerDeps["tracer"];
94
+ /**
95
+ * **部署座**(`RunnerDeps.mods`),原样递给本车道的每一只 Runner。唯一铸点 = 起服第 5 段的装配口 `assembleDeploymentMods`(本服务自铸的注册在前、
96
+ * 运维声明的 mod 包在后、整列过引擎准入);主 / 无手 / hook 代理 / 子代经共享基座吃的就是它,`boot/leader.ts` 搬的是**同一份**。
97
+ * leader 不自铸、不自己装配(一个进程两份部署注册 = 主车道与 leader 车道各按一套扩展跑)。缺失时整个部署座在这条车道上缺席:运维声明的守卫包
98
+ * 管不到 leader 派出的腿,拒绝计量 / 工具追踪也漏这条腿。缺席 ⇒ 展开空对象。
99
+ */
100
+ mods?: RunnerDeps["mods"];
94
101
  /**
95
102
  * [ref]([ref],[ref]-T1)—— **部署级 token 治理窗**两键,**成对**递给本车道的每一只 Runner。
96
103
  * 属主 = `config.usageWindows`(声明)+ `boot/stores.ts` 造的账本店(两者同真同假,与
@@ -166,6 +166,7 @@ export function createLeaderRunner(cfg) {
166
166
  const governanceSeat = cfg.governance ?? {};
167
167
  const deploymentPostureSeat = () => cfg.deploymentPosture?.() ?? {};
168
168
  const tracerSeat = cfg.tracer ? { tracer: cfg.tracer } : {};
169
+ const modsSeat = cfg.mods ? { mods: cfg.mods } : {};
169
170
  const usageWindowSeat = cfg.usageWindows && cfg.usageWindowStore ? { usageWindows: cfg.usageWindows, usageWindowStore: cfg.usageWindowStore } : {};
170
171
  const promptSourceSeat = cfg.promptSource ? { promptSource: cfg.promptSource } : {};
171
172
  const onErrorSeat = cfg.onError ? { onError: cfg.onError } : {};
@@ -186,7 +187,7 @@ export function createLeaderRunner(cfg) {
186
187
  throw new Error(`oracle still present after remove (${f.path}) — refusing repair (measurement integrity)`);
187
188
  }
188
189
  try {
189
- const runner = new Runner({ brain: cfg.brain, models: cfg.models, roles: cfg.roles, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, executionEnv: rawEnv, rootPath: repoDir });
190
+ const runner = new Runner({ brain: cfg.brain, models: cfg.models, roles: cfg.roles, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, ...modsSeat, executionEnv: rawEnv, rootPath: repoDir });
190
191
  const objective = [
191
192
  `The integrated project at ${repoDir} fails its build/test. Make the MINIMAL change to the working tree so this command exits 0 (cd into the repo and run it yourself to confirm):`,
192
193
  ` ${testCmd}`,
@@ -253,7 +254,7 @@ export function createLeaderRunner(cfg) {
253
254
  if (Array.isArray(body.subtasks) && body.subtasks.length > 0) {
254
255
  return validateSubtasks(body.subtasks);
255
256
  }
256
- const planRunner = new Runner({ brain: cfg.brain, models: cfg.models, roles: cfg.roles, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat });
257
+ const planRunner = new Runner({ brain: cfg.brain, models: cfg.models, roles: cfg.roles, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, ...modsSeat });
257
258
  const runRoute = async (prompt) => {
258
259
  const res = await planRunner.runTaskStream({
259
260
  objective: prompt,
@@ -347,7 +348,7 @@ export function createLeaderRunner(cfg) {
347
348
  workerId: sub.workerId, sessionId, branch: sub.branch,
348
349
  baseSha,
349
350
  runner: keepCtxWarm(new Runner({
350
- brain: cfg.brain, models: cfg.models, roles: cfg.roles, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat,
351
+ brain: cfg.brain, models: cfg.models, roles: cfg.roles, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, ...modsSeat,
351
352
  ...(cfg.toolResultStore ? { toolResultStore: cfg.toolResultStore } : {}),
352
353
  ...(cfg.sessionStore ? { sessionStore: cfg.sessionStore } : {}),
353
354
  executionEnvFactory: async (ctx) => {
@@ -372,7 +373,7 @@ export function createLeaderRunner(cfg) {
372
373
  const baseSha = await sh(env)(`cd ${repo} && git rev-parse HEAD`);
373
374
  return {
374
375
  workerId: sub.workerId, sessionId, branch: sub.branch, baseSha,
375
- runner: keepCtxWarm(new Runner({ brain: cfg.brain, models: cfg.models, roles: cfg.roles, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, ...(cfg.toolResultStore ? { toolResultStore: cfg.toolResultStore } : {}), ...(cfg.sessionStore ? { sessionStore: cfg.sessionStore } : {}), executionEnv: env })),
376
+ runner: keepCtxWarm(new Runner({ brain: cfg.brain, models: cfg.models, roles: cfg.roles, pricing: cfg.pricing, ...onNoticeSeat, ...governanceSeat, ...deploymentPostureSeat(), ...tracerSeat, ...usageWindowSeat, ...promptSourceSeat, ...onErrorSeat, ...modsSeat, ...(cfg.toolResultStore ? { toolResultStore: cfg.toolResultStore } : {}), ...(cfg.sessionStore ? { sessionStore: cfg.sessionStore } : {}), executionEnv: env })),
376
377
  diffEnv: env,
377
378
  destroy: () => env.destroy().then(() => { }),
378
379
  spec: withLeaderLeg({ ...workerLimits, ...sub.spec, ...durableSpec, ...resourceSpec, ...principalSpec() }, resumeBody),
@@ -1,12 +1,19 @@
1
1
  /**
2
- * http 传输的 `assertionHeader` —— 托管连接器「断言头」的**头名**(settings-schema ≥12.2.0 的 `McpTransport` http 形键)。
2
+ * 请求面一条 MCP 条目的形与它交给引擎之前的拆法({@link toMcpDraft}),以及 http 传输的 `assertionHeader` —— 托管连接器「断言头」的**头名**(settings-schema ≥12.2.0 的 `McpTransport` http 形键)。
3
3
  *
4
- * ## 一条律:一个键一种读法
4
+ * ## 请求面条目 → 草稿(一张按传输分臂的处置表)
5
+ * 请求面条目的形 = 契约包的请求传输形({@link RequestMcpServerSpec}:stdio 带 `env`、http 带 `headers` / `principalHeader` / `assertionHeader`),wire 上的形由它说。
6
+ * 交给引擎的声明不带件值:引擎的声明收据对带 `headers` / `env` 的声明整条拒,件由部署的对端座在发送时作答。所以每条条目按它那一臂的处置表
7
+ * ({@link MCP_STDIO_TRANSPORT_DISPOSITION} / {@link MCP_HTTP_TRANSPORT_DISPOSITION})拆成草稿:`env` / `headers` 进草稿的件,`assertionHeader` 承载不发送,其余进声明。
8
+ * 拆法只有 `plain-string-record.ts` 的 `splitDeclaration` 一只、键的去处只有表一处说;两张表各带三道编译格(请求臂加 / 减键、引擎臂加键、引擎臂删键都编译红)。
9
+ * 草稿只有胜出者(部署同名者赢,`task-mcp.ts` 的 `mergeRequestMcp`)才经对端座的唯一铸点(`peer-seat.ts` 的 `mintDeclaration`)登记,登记换来的引用放进声明的传输臂。
10
+ *
11
+ * ## `assertionHeader`:一条律,一个键一种读法
5
12
  * 两个面收下这个键,读法是同一个:
6
13
  * · **settings 面**(部署声明:中心下发 + 本地 `config.d`)—— 契约包在**解析期**受理它,并判它与 `principalHeader` 互斥
7
14
  * (同设 = 两个答案:「这台 http 服务器的请求上用哪个头送身份」);本仓这一面只承载,铸 spec 在 `resolveMcpServers`;
8
15
  * · **请求面**(任务提交体 `mcpServers[].transport`;探测口 `…/mcp/probe` 读同一张键表)—— 本仓在**受理期**兑现同一条规则:
9
- * 受理 = 字符串才收;互斥 = 同设即拒;投影 = 铸 spec 时不投(见下)。
16
+ * 受理 = 字符串才收;互斥 = 同设即拒;去处 = 处置表的 `withheld`(声明与件两边都不进)。
10
17
  * 同一条规则、两处执行者(契约包解析期 / 请求面受理期);判据 {@link mcpIdentityHeadersIssue} 与互斥拒句
11
18
  * {@link describeMcpIdentityHeadersConflict} 对契约包的原判官**逐字**对齐 —— 不是靠这句注释,而是
12
19
  * `test/task-mcp.test.ts` 的 [ref] 组把两边对拍(格子差分 + 拒句逐字相等),契约包改一个字那一组当场红。
@@ -18,38 +25,52 @@
18
25
  * 所以读器(契约包的 `readMcpIdentityHeaders`,`normalizeServer` 的 http 传输读两个身份头用它)的处置是**降级到升级前的读法**:`principalHeader` 胜(照挂,与升级前逐字相同),`assertionHeader` 不生效;
19
26
  * 不静默 —— 真挂上的这类条目由 {@link noteMcpAssertionHeaderIgnored} 说一行 warn(部署声明面没有这一格:配置不是被重放的存量任务,契约包解析期那一刀直接丢这一条)。
20
27
  *
21
- * ## 发送归引擎座(本版只承载,不发送)—— 「发不发」只有一只开关
22
- core 的 http 传输只有 `url` / `headers` / `principalHeader`,**没有**断言头座。所以本键承载到「铸 core 规格」为止:
23
- 请求面在 {@link toCoreMcpServerSpec} 剥掉它,settings 面的 `resolveMcpServers` 不投它;两处都用同一只
24
- {@link noteMcpAssertionHeaderNotSent} 说出一行 info(声明在、发送未接)—— 披露,不是兜底:server 照常挂载、不带断言头,
25
- 要求断言的托管连接器会拒这些调用,运维据这一行知道原因在哪。
26
- 「这台 server 发不发」是**一个事实**,只有一只开关 {@link MCP_ASSERTION_HEADER_SENT} 说它;读它的地方共五处,全经 {@link withholdsMcpAssertionHeader} 这一只谓词
27
- (能力位 `GET /v1/capabilities` 的 `mcpAssertionHeaderSent` 直读开关本人):请求面剥键、settings 面不投、一行 info、探测口整条拒、能力位。所以「位为真」与「还在说发送未接」
28
- 结构上不可能同时成立,消费方按位分支、位真时不再看到任何披露句;发送座到货的那天翻的就是这一行(与下面的退役门同批)。
29
-
30
- ## 退役(编译期自动逼,零人工记忆)
31
- * 引擎的 http 传输一旦长出同名座,{@link _CoreHasNoAssertionSeat} 当场编译红:届时删本文件里的 {@link RequestMcpServerSpec}
32
- * 扩展与 {@link toCoreMcpServerSpec} 的剥键、两处 info,把键直接投进引擎规格(发送由引擎座兑现)。
28
+ * ## 断言不发送 —— 「发不发」只有一处说
29
+ * 引擎的断言头是**固定的名**(引擎包根的 `MCP_ASSERTION_HEADER`),值由部署的对端座对 `assertion` 种类的引用作答;本部署没有断言来源,对端座对这一种恒拒
30
+ * (`peer-seat.ts` 的拒因 `kind_assertion`)。所以请求面写的这个头名**承载、不发送**:处置表那一格是 `withheld`,开关 {@link MCP_ASSERTION_HEADER_SENT} 从表那一格读出(恒假),
31
+ * 读它的地方全经 {@link withholdsMcpAssertionHeader} 这一只谓词(能力位 `GET /v1/capabilities` 的 `mcpAssertionHeaderSent` 直读开关本人):一行 info(请求面与 settings 面)、探测口整条拒、能力位;
32
+ * 两面铸声明时都不投这个键(请求面经处置表,settings 面不写它)。
33
+ * 声明在、发送未接 = 运维要知道的事实,不是兜底 —— server 照常挂载、不带断言头,要求断言的托管连接器会拒这些调用,运维据 {@link noteMcpAssertionHeaderNotSent} 那一行知道原因在哪。
34
+ * 退役门押在座上({@link _SeatRefusesAssertion}):对端座哪天开始对 `assertion` 作答,那一格当场编译红,届时重判这一格的去处(键本身随契约包删键一起退役)。
33
35
  */
34
36
  import type { McpServerSpec } from "@sema-agent/core";
35
- type CoreMcpTransport = McpServerSpec["transport"];
36
- type CoreStdioTransport = Extract<CoreMcpTransport, {
37
+ import type { RequestMcpTransport } from "@sema-agent/settings-schema";
38
+ import type { McpDraft } from "./peer-seat.js";
39
+ /** 请求面 stdio 传输的形(契约包):引擎的 stdio 传输去掉凭据引用位、加上 `env`(件)。 */
40
+ export type RequestStdioMcpTransport = Extract<RequestMcpTransport, {
37
41
  kind: "stdio";
38
42
  }>;
39
- type CoreHttpTransport = Extract<CoreMcpTransport, {
43
+ /** 请求面 http 传输的形(契约包):引擎的 http 传输去掉凭据引用位、加上 `headers`(件)与承载不发送的 `assertionHeader`。 */
44
+ export type RequestHttpMcpTransport = Extract<RequestMcpTransport, {
40
45
  kind: "http";
41
46
  }>;
42
- /** 请求面 http 传输的形:引擎的 http 传输 + 本仓承载的 `assertionHeader`(引擎无座,见头注)。 */
43
- export type RequestHttpMcpTransport = CoreHttpTransport & {
44
- assertionHeader?: string;
45
- };
46
47
  /**
47
48
  * 请求面一条 `mcpServers[]` 条目在 wire 上的形(= `TaskRequestBody.mcpServers` 的元素型,也是 `normalizeServer` 的产物):
48
- * 引擎的 {@link McpServerSpec} 加上 http 传输上的 `assertionHeader`。交给引擎之前一律经 {@link toCoreMcpServerSpec}。
49
+ * 引擎的 {@link McpServerSpec} 条目层 + 契约包的请求传输形。交给引擎之前一律经 {@link toMcpDraft} 拆成草稿。
49
50
  */
50
51
  export type RequestMcpServerSpec = Omit<McpServerSpec, "transport"> & {
51
- transport: CoreStdioTransport | RequestHttpMcpTransport;
52
+ transport: RequestMcpTransport;
53
+ };
54
+ /** stdio 臂的处置表(键序 = 键闭集序):`env` 是件,其余进声明。 */
55
+ export declare const MCP_STDIO_TRANSPORT_DISPOSITION: {
56
+ readonly args: "engine";
57
+ readonly command: "engine";
58
+ readonly env: "seat";
59
+ readonly kind: "engine";
52
60
  };
61
+ /** http 臂的处置表(键序 = 键闭集序):`headers` 是件,`assertionHeader` 承载不发送(见头注),其余进声明。 */
62
+ export declare const MCP_HTTP_TRANSPORT_DISPOSITION: {
63
+ readonly assertionHeader: "withheld";
64
+ readonly headers: "seat";
65
+ readonly kind: "engine";
66
+ readonly principalHeader: "engine";
67
+ readonly url: "engine";
68
+ };
69
+ /**
70
+ * 请求面条目 → 草稿:传输按它那一臂的处置表拆(`env` / `headers` 进件,`assertionHeader` 两边都不进,其余进声明);条目层的键原样进声明。件缺席或空 ⇒ 草稿不带件(登记时什么都不留)。
71
+ * 纯函数:不登记、不读部署状态;被扣下的 `assertionHeader` 由调用方另说一行 info。
72
+ */
73
+ export declare function toMcpDraft(spec: RequestMcpServerSpec): McpDraft;
53
74
  /** 一份原始 http 传输上两个身份头的问题类:`both_set` = 同设了 `principalHeader` 与 `assertionHeader`;`not_a_string` = `assertionHeader` 在场但不是字符串。 */
54
75
  export type McpIdentityHeadersIssue = "both_set" | "not_a_string";
55
76
  /**
@@ -64,27 +85,18 @@ export declare function describeMcpIdentityHeadersConflict(server: string): stri
64
85
  /** `assertionHeader` 值不是字符串的**原因句**(不带条目名与键路径:点名由请求面统一的拒形 `entryFieldRefusal` 负责;值的形状只说种类,不回显值本身:它是一个调用方写的头名位置,可能是任何东西)。 */
65
86
  export declare function describeMcpAssertionHeaderNotAString(value: unknown): string;
66
87
  /**
67
- * 引擎的 http 传输有没有断言头发送座 —— **全仓唯一的开关**。今天没有(core 的 http 传输无同名座,见头注与退役门)⇒ `false`:声明在、发送未接。
68
- * 翻真 = 这一行 + 删退役门 + 把 {@link RequestHttpMcpTransport} 并进引擎型;剥键、披露、探测口拒、settings 面投影、能力位 `mcpAssertionHeaderSent` 随它一起翻
69
- * (五处都读 {@link withholdsMcpAssertionHeader} / 本常量,没有第六处)。声明成 `boolean` 而不是字面 `false`:谓词里的真臂是活代码(单测传 `sent=true` 钉它),不是被收窄掉的死分支。
88
+ * 本部署发不发请求面声明的断言头 —— **全仓唯一的开关**,从 http 臂处置表的那一格读出(`withheld` ⇒ 假;依据见头注「断言不发送」与退役门)。
89
+ * 读它的地方全经 {@link withholdsMcpAssertionHeader} / 本常量(一行 info、探测口拒、能力位 `mcpAssertionHeaderSent`),所以表那一格与开关不可能脱钩。
90
+ * 声明成 `boolean` 而不是字面 `false`:谓词里的真臂是活代码(单测传 `sent=true` 钉它),不是被收窄掉的死分支。
70
91
  */
71
92
  export declare const MCP_ASSERTION_HEADER_SENT: boolean;
72
93
  /**
73
- * 这一份 `assertionHeader` 声明是不是「被扣下」的那一类(需要剥键、需要说一行 info、探测口要拒的那一类)—— 即**声明了,而发送座不在**。
74
- * 三处消费(剥键 / 披露 / 探测口拒)与 settings 面的投影共读这一只;`sent` 缺省读开关,单测传显式值钉另一臂。
94
+ * 这一份 `assertionHeader` 声明是不是「被扣下」的那一类(需要说一行 info、探测口要拒的那一类)—— 即**声明了,而本部署不发送**。
95
+ * 披露(请求面与 settings 面)/ 探测口拒共读这一只;`sent` 缺省读开关,单测传显式值钉另一臂。
75
96
  */
76
97
  export declare function withholdsMcpAssertionHeader(assertionHeader: unknown, sent?: boolean): boolean;
77
98
  /** {@link withholdsMcpAssertionHeader} 在请求面条目上的读法:只有 http 传输有这个键(stdio 形上它不存在)。 */
78
99
  export declare function specWithholdsMcpAssertionHeader(spec: RequestMcpServerSpec): boolean;
79
- /**
80
- * settings 面铸 spec 时「这个键投不投进引擎规格」:发送座在(`sent` 真)且声明了字符串 ⇒ 投,否则整键缺席。今天恒为空对象(开关假);
81
- * 开关翻真的那天 settings 面因此自动跟着投,不会出现「位说发了、部署声明面却没投」。
82
- */
83
- export declare function sentMcpAssertionHeaderMember(assertionHeader: unknown, sent?: boolean): {
84
- assertionHeader?: string;
85
- };
86
- /** 请求面条目 → 交给引擎的规格:被扣下的 `assertionHeader` 剥掉(引擎无座);其余逐字不动。被扣下的条目由调用方另说一行 info。 */
87
- export declare function toCoreMcpServerSpec(spec: RequestMcpServerSpec): McpServerSpec;
88
100
  /** `noteMcpAssertionHeaderNotSent` 的日志行名(运维 grep 用;请求面与 settings 面同一个名字)。 */
89
101
  export declare const MCP_ASSERTION_HEADER_NOT_SENT = "mcp_assertion_header_not_sent";
90
102
  /** `noteMcpAssertionHeaderIgnored` 的日志行名。 */
@@ -93,10 +105,9 @@ export declare const MCP_ASSERTION_HEADER_IGNORED = "mcp_assertion_header_ignore
93
105
  export declare function noteMcpAssertionHeaderIgnored(logger: {
94
106
  warn?(msg: string, meta?: unknown): void;
95
107
  } | undefined, server: string, reason: McpIdentityHeadersIssue): void;
96
- /** 说出「这台 server 声明了 `assertionHeader`,本版没有发送」—— 一台一行 info。两个铸 spec 点(请求面 `classifyHonored`、
108
+ /** 说出「这台 server 声明了 `assertionHeader`,本部署不发送」—— 一台一行 info。两个铸 spec 点(请求面 `classifyHonored`、
97
109
  * settings 面 `resolveMcpServers`)共用这一只,所以行名与措辞只有一份。 */
98
110
  export declare function noteMcpAssertionHeaderNotSent(logger: {
99
111
  info?(msg: string, meta?: unknown): void;
100
112
  } | undefined, server: string): void;
101
- export {};
102
113
  //# sourceMappingURL=mcp-assertion-header.d.ts.map
@@ -1,3 +1,11 @@
1
+ import { splitDeclaration } from "./plain-string-record.js";
2
+ export const MCP_STDIO_TRANSPORT_DISPOSITION = { args: "engine", command: "engine", env: "seat", kind: "engine" };
3
+ export const MCP_HTTP_TRANSPORT_DISPOSITION = { assertionHeader: "withheld", headers: "seat", kind: "engine", principalHeader: "engine", url: "engine" };
4
+ export function toMcpDraft(spec) {
5
+ const { transport, ...entry } = spec;
6
+ const split = transport.kind === "http" ? splitDeclaration(transport, MCP_HTTP_TRANSPORT_DISPOSITION) : splitDeclaration(transport, MCP_STDIO_TRANSPORT_DISPOSITION);
7
+ return { spec: { ...entry, transport: split.engine }, ...(split.pieces === undefined ? {} : { pieces: split.pieces }) };
8
+ }
1
9
  export function mcpIdentityHeadersIssue(transport) {
2
10
  if (transport.kind !== "http")
3
11
  return undefined;
@@ -14,22 +22,13 @@ export function describeMcpAssertionHeaderNotAString(value) {
14
22
  const kind = value === null ? "null" : Array.isArray(value) ? "an array" : `a ${typeof value}`;
15
23
  return `must be a string (the HTTP header NAME the connector assertion travels in) — got ${kind}; omit the key to send no assertion header`;
16
24
  }
17
- export const MCP_ASSERTION_HEADER_SENT = false;
25
+ export const MCP_ASSERTION_HEADER_SENT = MCP_HTTP_TRANSPORT_DISPOSITION.assertionHeader !== "withheld";
18
26
  export function withholdsMcpAssertionHeader(assertionHeader, sent = MCP_ASSERTION_HEADER_SENT) {
19
27
  return !sent && assertionHeader !== undefined;
20
28
  }
21
29
  export function specWithholdsMcpAssertionHeader(spec) {
22
30
  return spec.transport.kind === "http" && withholdsMcpAssertionHeader(spec.transport.assertionHeader);
23
31
  }
24
- export function sentMcpAssertionHeaderMember(assertionHeader, sent = MCP_ASSERTION_HEADER_SENT) {
25
- return sent && typeof assertionHeader === "string" ? { assertionHeader } : {};
26
- }
27
- export function toCoreMcpServerSpec(spec) {
28
- if (spec.transport.kind !== "http" || !withholdsMcpAssertionHeader(spec.transport.assertionHeader))
29
- return spec;
30
- const { assertionHeader: _declaredNotSent, ...transport } = spec.transport;
31
- return { ...spec, transport };
32
- }
33
32
  export const MCP_ASSERTION_HEADER_NOT_SENT = "mcp_assertion_header_not_sent";
34
33
  export const MCP_ASSERTION_HEADER_IGNORED = "mcp_assertion_header_ignored";
35
34
  export function noteMcpAssertionHeaderIgnored(logger, server, reason) {
@@ -44,7 +43,7 @@ export function noteMcpAssertionHeaderIgnored(logger, server, reason) {
44
43
  export function noteMcpAssertionHeaderNotSent(logger, server) {
45
44
  logger?.info?.(MCP_ASSERTION_HEADER_NOT_SENT, {
46
45
  server,
47
- note: "declaration KEPT but NOT SENT — the engine's http transport has no seat for an assertion header yet, so this server mounts WITHOUT one (a hosted connector that requires the assertion will refuse its calls); sending follows the engine seat",
46
+ note: "declaration KEPT but NOT SENT — the engine sends a connector assertion only under its own fixed header name, answered by the deployment's peer seat, and this deployment has no assertion source; this server mounts WITHOUT one (a hosted connector that requires the assertion will refuse its calls)",
48
47
  });
49
48
  }
50
49
  //# sourceMappingURL=mcp-assertion-header.js.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * MCP 授权事实(core 7.59.0 `McpAuthorization`:`{ reason, authorizeUrl?, resourceMetadataUrl? }`,与 `errorCode: "unauthorized"` 同行)的**出门投影** ——
3
+ * 本仓三张面(`wiring_manifest.mcp[]` 行、重拨回执的 `status`、`//mcp` 面板的 `servers[]`)同读这一只。
4
+ *
5
+ * 只投闭集词 `reason`(`rejected` = 端点答 401 且授权座的重问没给出能过的凭据 / `scope` = 403 `insufficient_scope` / `none` = 授权座答「未授权」):
6
+ * 判据直读引擎的 `MCP_AUTHORIZATION_REASONS`(不抄词表);词出闭集、非串、整只不是对象 ⇒ 整格缺席(最弱断言,不铸词)。
7
+ * 两只地址**不出门**:`authorizeUrl` 只来自部署的授权座,本部署今天没有授权座;`resourceMetadataUrl` 是远端在挑战头里自报的地址,不可信 —— 两只地址的去向
8
+ * (授权座 + 出口座)是授权记录面的设计,不在本投影里放行。用户面的「去授权」由会话通告 `mcp.authorization_required` 承载。
9
+ */
10
+ import { type McpAuthorizationReason } from "@sema-agent/core";
11
+ /** 授权记录的处置表(三面同读;`core-keyset-guard.ts` 读表 —— 门与投影一个写者):`reason` 认得才出门(闭集词;在场而认不得 ⇒ 丢并计一次),两只地址本面不出现。 */
12
+ export declare const MCP_AUTHORIZATION_DISPOSITIONS: {
13
+ readonly reason: {
14
+ readonly forward: (w: unknown) => w is McpAuthorizationReason;
15
+ };
16
+ readonly authorizeUrl: "strip";
17
+ readonly resourceMetadataUrl: "strip";
18
+ };
19
+ /** 处置表一格的放行谓词所窄化到的型(`forward: (w: unknown) => w is T` ⇒ `T`):表放行什么,出门值就是什么型。 */
20
+ type ForwardedAs<F> = F extends (w: unknown) => w is infer T ? T : never;
21
+ /** 本投影的产物 = 表里 `reason` 一格放行后的型。词的型只有表这一个写者,出口不再判第二遍。 */
22
+ export type McpAuthorizationFace = {
23
+ readonly reason: ForwardedAs<(typeof MCP_AUTHORIZATION_DISPOSITIONS)["reason"]["forward"]>;
24
+ };
25
+ export declare function projectMcpAuthorization(v: unknown): McpAuthorizationFace | undefined;
26
+ export {};
27
+ //# sourceMappingURL=mcp-authorization-face.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { MCP_AUTHORIZATION_REASONS } from "@sema-agent/core";
2
+ import { projectByTable } from "./trace/projection-table.js";
3
+ const isAuthorizationReason = (w) => typeof w === "string" && MCP_AUTHORIZATION_REASONS.includes(w);
4
+ export const MCP_AUTHORIZATION_DISPOSITIONS = {
5
+ reason: { forward: isAuthorizationReason },
6
+ authorizeUrl: "strip",
7
+ resourceMetadataUrl: "strip",
8
+ };
9
+ export function projectMcpAuthorization(v) {
10
+ if (typeof v !== "object" || v === null || Array.isArray(v))
11
+ return undefined;
12
+ const { reason } = projectByTable("mcp.authorization", MCP_AUTHORIZATION_DISPOSITIONS, v, undefined);
13
+ return reason !== undefined ? { reason } : undefined;
14
+ }
15
+ //# sourceMappingURL=mcp-authorization-face.js.map
@@ -63,11 +63,20 @@ export interface MemoryComplianceFaces {
63
63
  * 抹除。入参 `unknown`:调用方递交的是未校验的对象,三选一选择子的整体判决归 core
64
64
  * (`erasureRequestInvalid` 是它的单一属主面)—— 在本层抄一遍就是第二真源。
65
65
  *
66
- * `opts.allowedScopes` = **授权信封座**(见 {@link erasureEnvelopeRefusal} 的头注)。
66
+ * ## 授权座只有一只:引擎的 `EraseMemoryEntriesInput.expectedScopes`
67
+ *
68
+ * 「这个调用方能删哪些 scope」由引擎判:在场 ⇒ 选择子自己的 scope(地点选择子,读店之前判)与**每个解析出的 id 的绑定 scope**
69
+ * 都得是集内成员,判在解析之后、店锁之内;违者整单拒 `config.memory_erasure_scope`,拒句不带数、不带 id、不带 scope(集外 id
70
+ * 与从没存在过的 id 同句,不是存在性神谕)。本层不另设信封:一只只读得到 `{ scope }` 选择子字面量的信封判不了 `ids` /
71
+ * `sessionId` 两腿、只能整拒,而且让「哪些 scope 能删」有两个答者。集随**入参**走(与 `requestId` / `select` 同一个对象),
72
+ * 本面没有第二个形参。
73
+ *
74
+ * 今天谁传集:operator 面原样递交调用方的体(调用方自己写的 `expectedScopes` = 它自己收窄自己);路由不替 operator 铸集 ——
75
+ * 本仓没有任何授权源能对一个 operator 说「你只能动这些 scope」(`routes/memory-policy.ts` 的 `gateMemoryScope`:operator 支在查
76
+ * 目录之前就 allow;`org-memory-admission.ts`:v1 不中央收窄 operator 声明的 scope)。拿 org 目录的 grant 集去凑一个集就是在本层
77
+ * 发明第二套授权语义,与 `gateMemoryScope` 对同一个身份给出相反答案。会话自助面的选择子由路径铸,不带集。
67
78
  */
68
- erase: (input: unknown, opts?: {
69
- allowedScopes?: readonly string[];
70
- }) => Promise<MemoryErasureAttestation>;
79
+ erase: (input: unknown) => Promise<MemoryErasureAttestation>;
71
80
  }
72
81
  /**
73
82
  * **选择子替身**的拦截谓词(codex 交叉复审 [critical],亲验复现后修 —— 复现器与结论逐条在
@@ -24,19 +24,6 @@ export function erasureInputSmugglesPrototypeKey(input) {
24
24
  }
25
25
  return false;
26
26
  }
27
- function erasureEnvelopeRefusal(input, allowedScopes) {
28
- if (allowedScopes === undefined)
29
- return undefined;
30
- const select = typeof input === "object" && input !== null ? input.select : undefined;
31
- const scope = typeof select === "object" && select !== null ? select.scope : undefined;
32
- if (typeof scope === "string" && allowedScopes.includes(scope))
33
- return undefined;
34
- const e = new Error(typeof scope === "string"
35
- ? `memory erasure refused: scope ${JSON.stringify(scope)} is outside this caller's erasure envelope`
36
- : "memory erasure refused: this request's selector is not scope-addressable, and an envelope that cannot judge a selector must not admit it");
37
- e.code = "memory.erasure_scope_forbidden";
38
- return e;
39
- }
40
27
  export function createMemoryComplianceFaces(store, opts) {
41
28
  if (!backendOwnsControlPlane(store.backend))
42
29
  return undefined;
@@ -48,15 +35,12 @@ export function createMemoryComplianceFaces(store, opts) {
48
35
  });
49
36
  return {
50
37
  provenanceOf: (entryId) => engine.provenanceOf(entryId),
51
- erase: (input, opts) => {
38
+ erase: (input) => {
52
39
  if (erasureInputSmugglesPrototypeKey(input)) {
53
40
  const e = new Error("memory erasure refused: the request carries a key that rewrites an object's prototype — such a key is not a selector, and core's selector dispatch would read it in preference to the declared one");
54
41
  e.code = "memory.erasure_selector_unsafe";
55
42
  return Promise.reject(e);
56
43
  }
57
- const refusal = erasureEnvelopeRefusal(input, opts?.allowedScopes);
58
- if (refusal !== undefined)
59
- return Promise.reject(refusal);
60
44
  return engine.eraseMemoryEntries(input);
61
45
  },
62
46
  };