@sema-agent/core 7.54.1 → 7.55.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 (399) hide show
  1. package/CHANGELOG.md +437 -0
  2. package/dist/agents/agent-transcript-tool.js +2 -1
  3. package/dist/agents/builtin-agents.d.ts +12 -14
  4. package/dist/agents/cc-peer-directory.d.ts +36 -0
  5. package/dist/agents/cc-peer-directory.js +138 -0
  6. package/dist/agents/cc-peer-wire.d.ts +105 -0
  7. package/dist/agents/cc-peer-wire.js +98 -0
  8. package/dist/agents/cc-uds-peer-channel.d.ts +40 -0
  9. package/dist/agents/cc-uds-peer-channel.js +457 -0
  10. package/dist/agents/cross-session-envelope.d.ts +4 -1
  11. package/dist/agents/cross-session-envelope.js +8 -1
  12. package/dist/agents/cross-session-judge.d.ts +2 -2
  13. package/dist/agents/list-agents-tool.js +2 -1
  14. package/dist/agents/observer.d.ts +4 -5
  15. package/dist/agents/observer.js +2 -1
  16. package/dist/agents/peer-admission.d.ts +6 -0
  17. package/dist/agents/peer-admission.js +4 -1
  18. package/dist/agents/peer-directory.d.ts +43 -9
  19. package/dist/agents/peer-directory.js +66 -23
  20. package/dist/agents/peer-notices.d.ts +1 -1
  21. package/dist/agents/peer-session-drain.d.ts +3 -1
  22. package/dist/agents/peer-session-drain.js +1 -0
  23. package/dist/agents/read-inbox-tool.js +2 -1
  24. package/dist/agents/retain-ledger.d.ts +1 -1
  25. package/dist/agents/roster-store.d.ts +1 -1
  26. package/dist/agents/send-message-tool.d.ts +10 -0
  27. package/dist/agents/send-message-tool.js +34 -5
  28. package/dist/agents/subagent.d.ts +34 -79
  29. package/dist/agents/subagent.js +45 -46
  30. package/dist/agents/tool-filter.d.ts +29 -46
  31. package/dist/agents/tool-filter.js +8 -31
  32. package/dist/agents/verify.d.ts +3 -3
  33. package/dist/brain/anthropic.js +14 -9
  34. package/dist/brain/errors.d.ts +15 -0
  35. package/dist/brain/errors.js +10 -0
  36. package/dist/brain/model-presets.d.ts +4 -4
  37. package/dist/brain/open-responses.js +15 -10
  38. package/dist/brain/openai.js +17 -12
  39. package/dist/brain/provider-dialect.d.ts +39 -3
  40. package/dist/brain/provider-dialect.js +28 -0
  41. package/dist/brain/reasoning.d.ts +1 -1
  42. package/dist/brain/request-params.d.ts +55 -5
  43. package/dist/brain/request-params.js +35 -3
  44. package/dist/brain/retry.d.ts +1 -1
  45. package/dist/brain/route-adjudicator.d.ts +15 -0
  46. package/dist/brain/route-adjudicator.js +15 -0
  47. package/dist/brain/stream-engine.js +6 -5
  48. package/dist/brain/stream-shared.d.ts +1 -1
  49. package/dist/core/advisor-verdict.d.ts +120 -0
  50. package/dist/core/advisor-verdict.js +92 -0
  51. package/dist/core/agent-definition.d.ts +7 -8
  52. package/dist/core/allow-entry.d.ts +49 -23
  53. package/dist/core/allow-entry.js +57 -32
  54. package/dist/core/allow-rule-shadow.d.ts +7 -0
  55. package/dist/core/approval-object.d.ts +2 -1
  56. package/dist/core/approval-object.js +6 -6
  57. package/dist/core/ask-origin.d.ts +63 -11
  58. package/dist/core/ask-origin.js +27 -5
  59. package/dist/core/ask-question.js +10 -4
  60. package/dist/core/auto-compaction.d.ts +11 -8
  61. package/dist/core/auto-compaction.js +4 -2
  62. package/dist/core/auto-mode-arming.d.ts +2 -2
  63. package/dist/core/auto-mode-defaults.d.ts +8 -8
  64. package/dist/core/auto-mode-prompt-assets.d.ts +3 -3
  65. package/dist/core/auto-mode-prompt.d.ts +1 -1
  66. package/dist/core/auto-mode.d.ts +17 -9
  67. package/dist/core/auto-mode.js +9 -6
  68. package/dist/core/background-agent-store.d.ts +3 -3
  69. package/dist/core/canonical-json.d.ts +1 -1
  70. package/dist/core/canonical-json.js +2 -2
  71. package/dist/core/checkpoint-requirements.d.ts +6 -3
  72. package/dist/core/checkpoint-requirements.js +4 -14
  73. package/dist/core/checkpoint-store.d.ts +80 -51
  74. package/dist/core/checkpoint-store.js +56 -74
  75. package/dist/core/child-clamp-seats.d.ts +6 -0
  76. package/dist/core/child-clamp-seats.js +3 -16
  77. package/dist/core/compliance.d.ts +2 -2
  78. package/dist/core/context-edit.d.ts +6 -7
  79. package/dist/core/cross-boundary-taint.d.ts +9 -3
  80. package/dist/core/cross-boundary-taint.js +5 -3
  81. package/dist/core/decision-receipt.js +2 -2
  82. package/dist/core/effective-path-target.d.ts +1 -1
  83. package/dist/core/engine-notice.d.ts +19 -5
  84. package/dist/core/exec-output-tail.d.ts +1 -1
  85. package/dist/core/file-history-retention.d.ts +1 -1
  86. package/dist/core/file-history-store.d.ts +59 -9
  87. package/dist/core/file-history-store.js +35 -17
  88. package/dist/core/gate-fold.js +3 -2
  89. package/dist/core/gate-lanes.js +12 -8
  90. package/dist/core/gate-outcome.d.ts +17 -4
  91. package/dist/core/gate-outcome.js +30 -0
  92. package/dist/core/governance-codes.d.ts +13 -3
  93. package/dist/core/governance-codes.js +10 -0
  94. package/dist/core/hands-band.d.ts +1 -1
  95. package/dist/core/hooks.d.ts +4 -4
  96. package/dist/core/host-target.d.ts +49 -0
  97. package/dist/core/host-target.js +59 -0
  98. package/dist/core/image-downsample.d.ts +1 -1
  99. package/dist/core/lsp-diagnostics.d.ts +4 -4
  100. package/dist/core/lsp.d.ts +3 -3
  101. package/dist/core/mailbox-store.d.ts +4 -0
  102. package/dist/core/mcp-connection-pool.d.ts +18 -2
  103. package/dist/core/mcp-connection-pool.js +53 -6
  104. package/dist/core/mcp-failure.d.ts +23 -4
  105. package/dist/core/mcp-failure.js +26 -11
  106. package/dist/core/mcp-listing-drift.d.ts +10 -0
  107. package/dist/core/mcp-listing-drift.js +6 -0
  108. package/dist/core/mcp-server-link.d.ts +173 -0
  109. package/dist/core/mcp-server-link.js +127 -0
  110. package/dist/core/mcp-server-spec.d.ts +4 -2
  111. package/dist/core/mcp-session-end.d.ts +26 -0
  112. package/dist/core/mcp-session-end.js +29 -0
  113. package/dist/core/mcp-startup-request.d.ts +17 -0
  114. package/dist/core/mcp-startup-request.js +17 -0
  115. package/dist/core/mcp.d.ts +13 -13
  116. package/dist/core/mcp.js +187 -162
  117. package/dist/core/media-byte-cap.d.ts +1 -1
  118. package/dist/core/memory-engine/engine.d.ts +9 -9
  119. package/dist/core/memory-engine/provenance-wording.d.ts +1 -1
  120. package/dist/core/memory-engine/tools.js +4 -1
  121. package/dist/core/mode-policy.d.ts +1 -1
  122. package/dist/core/namespaced-rule-name.d.ts +33 -7
  123. package/dist/core/namespaced-rule-name.js +39 -25
  124. package/dist/core/output-style.d.ts +1 -1
  125. package/dist/core/param-rule-reach.d.ts +15 -0
  126. package/dist/core/param-rule-reach.js +30 -0
  127. package/dist/core/parent-spec-seat.d.ts +8 -1
  128. package/dist/core/parent-spec-seat.js +4 -0
  129. package/dist/core/pause-registry.d.ts +16 -5
  130. package/dist/core/pause-registry.js +6 -2
  131. package/dist/core/peer-channel.d.ts +70 -0
  132. package/dist/core/peer-channel.js +1 -0
  133. package/dist/core/permission-rule-consent.d.ts +11 -5
  134. package/dist/core/permission-rule-consent.js +37 -30
  135. package/dist/core/permission-rule-model.d.ts +233 -64
  136. package/dist/core/permission-rule-model.js +384 -85
  137. package/dist/core/permission-rule-provider.d.ts +2 -1
  138. package/dist/core/permission-rule-store.d.ts +20 -25
  139. package/dist/core/permission-rule-store.js +1 -4
  140. package/dist/core/permission-rule-sync.d.ts +11 -2
  141. package/dist/core/permission-rule-sync.js +8 -1
  142. package/dist/core/permission-rules.d.ts +49 -11
  143. package/dist/core/permission-rules.js +123 -89
  144. package/dist/core/persisted-rule-arms.d.ts +4 -2
  145. package/dist/core/present-plan-tool.js +3 -1
  146. package/dist/core/protocol-naming.d.ts +2 -2
  147. package/dist/core/read-only-shell-table.d.ts +1 -1
  148. package/dist/core/read-only-shell.d.ts +1 -1
  149. package/dist/core/roles.d.ts +6 -2
  150. package/dist/core/roles.js +45 -44
  151. package/dist/core/rule-offer-read.d.ts +53 -0
  152. package/dist/core/rule-offer-read.js +146 -0
  153. package/dist/core/rule-owner.d.ts +46 -0
  154. package/dist/core/rule-owner.js +28 -0
  155. package/dist/core/runner/advisor-instructions.d.ts +70 -0
  156. package/dist/core/runner/advisor-instructions.js +61 -0
  157. package/dist/core/runner/advisor-tool.d.ts +33 -0
  158. package/dist/core/runner/advisor-tool.js +112 -0
  159. package/dist/core/runner/announce-once-ledger.d.ts +4 -26
  160. package/dist/core/runner/announce-once-ledger.js +14 -46
  161. package/dist/core/runner/approval-feedback.js +6 -3
  162. package/dist/core/runner/assemble-result.d.ts +1 -1
  163. package/dist/core/runner/caller-roster.d.ts +7 -0
  164. package/dist/core/runner/caller-roster.js +11 -0
  165. package/dist/core/runner/classifier-ladder.d.ts +27 -5
  166. package/dist/core/runner/classifier-ladder.js +12 -11
  167. package/dist/core/runner/compaction-restate.d.ts +10 -6
  168. package/dist/core/runner/compaction-restate.js +9 -3
  169. package/dist/core/runner/contracts.d.ts +13 -9
  170. package/dist/core/runner/conversation-segment.d.ts +28 -0
  171. package/dist/core/runner/conversation-segment.js +42 -0
  172. package/dist/core/runner/cross-boundary-lane.d.ts +6 -5
  173. package/dist/core/runner/cross-boundary-lane.js +26 -15
  174. package/dist/core/runner/decide-continuation.d.ts +7 -4
  175. package/dist/core/runner/decide-continuation.js +2 -2
  176. package/dist/core/runner/denial-limit-arms.d.ts +2 -2
  177. package/dist/core/runner/derived-route-fallback.d.ts +24 -24
  178. package/dist/core/runner/derived-route-fallback.js +9 -7
  179. package/dist/core/runner/gate-exit.d.ts +6 -0
  180. package/dist/core/runner/gate-exit.js +7 -2
  181. package/dist/core/runner/initial-run-state.js +1 -1
  182. package/dist/core/runner/permission-rule-lanes.d.ts +2 -2
  183. package/dist/core/runner/permission-rule-lanes.js +14 -7
  184. package/dist/core/runner/person-path-rows.js +1 -1
  185. package/dist/core/runner/prepare-advisor.d.ts +63 -0
  186. package/dist/core/runner/prepare-advisor.js +77 -0
  187. package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
  188. package/dist/core/runner/prepare-config-doors.d.ts +6 -4
  189. package/dist/core/runner/prepare-config-doors.js +18 -12
  190. package/dist/core/runner/prepare-delegation-surface.d.ts +2 -2
  191. package/dist/core/runner/prepare-delegation-surface.js +1 -1
  192. package/dist/core/runner/prepare-file-history.js +39 -39
  193. package/dist/core/runner/prepare-gate-stations.d.ts +1 -4
  194. package/dist/core/runner/prepare-gate-stations.js +2 -7
  195. package/dist/core/runner/prepare-inherited-gate.d.ts +5 -4
  196. package/dist/core/runner/prepare-inherited-gate.js +3 -3
  197. package/dist/core/runner/prepare-park-ask.js +7 -7
  198. package/dist/core/runner/prepare-policy-chain.d.ts +1 -1
  199. package/dist/core/runner/prepare-policy-chain.js +4 -10
  200. package/dist/core/runner/prepare-project-context.d.ts +5 -2
  201. package/dist/core/runner/prepare-project-context.js +6 -3
  202. package/dist/core/runner/prepare-prompt-inputs.js +1 -1
  203. package/dist/core/runner/prepare-protocol-tools.d.ts +2 -2
  204. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  205. package/dist/core/runner/prepare-safety-scan.d.ts +9 -12
  206. package/dist/core/runner/prepare-safety-scan.js +8 -6
  207. package/dist/core/runner/prepare-task.js +15 -18
  208. package/dist/core/runner/prepare-tool-disclosure-mount.js +6 -4
  209. package/dist/core/runner/prepare-wiring-manifest.d.ts +6 -3
  210. package/dist/core/runner/prepare-wiring-manifest.js +2 -2
  211. package/dist/core/runner/resume-policy-outcome.js +1 -1
  212. package/dist/core/runner/retired-runner-deps.js +1 -1
  213. package/dist/core/runner/run-attachment-seats.js +0 -1
  214. package/dist/core/runner/run-compaction-machinery.d.ts +1 -1
  215. package/dist/core/runner/run-compaction-machinery.js +2 -12
  216. package/dist/core/runner/run-git-lane.d.ts +1 -0
  217. package/dist/core/runner/run-harness-handlers.js +2 -3
  218. package/dist/core/runner/run-leg.js +13 -0
  219. package/dist/core/runner/run-settle-and-teardown.js +1 -1
  220. package/dist/core/runner/run-telemetry-and-budget-seats.js +1 -1
  221. package/dist/core/runner/run-turn-boundary.d.ts +1 -0
  222. package/dist/core/runner/run-turn-boundary.js +14 -12
  223. package/dist/core/runner/session-rule-policy.d.ts +22 -21
  224. package/dist/core/runner/session-rule-policy.js +13 -28
  225. package/dist/core/runner/side-call-account.d.ts +32 -8
  226. package/dist/core/runner/side-call-account.js +23 -1
  227. package/dist/core/runner/strict-output-schema.d.ts +1 -1
  228. package/dist/core/runner/sub-agent-deny-lists.js +4 -3
  229. package/dist/core/runner/synthetic-tools.d.ts +10 -13
  230. package/dist/core/runner/synthetic-tools.js +4 -2
  231. package/dist/core/runner/tool-disclosure.js +2 -1
  232. package/dist/core/runner/tool-face-overlay.d.ts +2 -1
  233. package/dist/core/runner/tool-face-overlay.js +17 -1
  234. package/dist/core/runner/tool-progress-lane.d.ts +2 -2
  235. package/dist/core/runner/tool-turn-repair-seat.d.ts +3 -2
  236. package/dist/core/runner/turn-attachments.d.ts +60 -67
  237. package/dist/core/runner-deps.d.ts +37 -37
  238. package/dist/core/runtime-caps.d.ts +4 -4
  239. package/dist/core/safety-merge-corpus.js +17 -1
  240. package/dist/core/same-plain-value.d.ts +40 -0
  241. package/dist/core/same-plain-value.js +49 -0
  242. package/dist/core/scheduler.d.ts +1 -1
  243. package/dist/core/sensitive-path-policy.d.ts +1 -1
  244. package/dist/core/sensitive-path-policy.js +1 -1
  245. package/dist/core/session-policy-store.d.ts +77 -22
  246. package/dist/core/session-policy-store.js +56 -14
  247. package/dist/core/session-reconcile.d.ts +2 -2
  248. package/dist/core/session-store.d.ts +80 -30
  249. package/dist/core/session-store.js +118 -76
  250. package/dist/core/session.d.ts +24 -46
  251. package/dist/core/shared-memory/tools.js +3 -1
  252. package/dist/core/shared-memory/types.d.ts +1 -1
  253. package/dist/core/side-query.d.ts +10 -0
  254. package/dist/core/side-query.js +5 -0
  255. package/dist/core/store-contracts/checkpoint-store-contract.js +29 -2
  256. package/dist/core/store-contracts/file-history-store-contract.js +43 -3
  257. package/dist/core/store-contracts/session-policy-store-contract.d.ts +7 -0
  258. package/dist/core/store-contracts/session-policy-store-contract.js +130 -0
  259. package/dist/core/store-contracts/session-repo-contract.js +125 -0
  260. package/dist/core/store-contracts/session-store-contract.d.ts +4 -7
  261. package/dist/core/store-contracts/session-store-contract.js +215 -41
  262. package/dist/core/store-contracts/workflow-journal-store-contract.js +26 -1
  263. package/dist/core/surrogate-safe-slice.d.ts +3 -3
  264. package/dist/core/task-event.d.ts +10 -4
  265. package/dist/core/task-notification.d.ts +3 -3
  266. package/dist/core/task-registry-agent.d.ts +4 -4
  267. package/dist/core/task-registry-shared.d.ts +4 -4
  268. package/dist/core/task-registry-shared.js +1 -0
  269. package/dist/core/task-registry.d.ts +4 -4
  270. package/dist/core/task-registry.js +3 -1
  271. package/dist/core/task-result.d.ts +3 -3
  272. package/dist/core/task-spec.d.ts +52 -52
  273. package/dist/core/task-stream.d.ts +6 -6
  274. package/dist/core/task-tool-shape.d.ts +4 -4
  275. package/dist/core/tighten-task-spec.d.ts +1 -1
  276. package/dist/core/tool-catalog-entries.d.ts +19 -2
  277. package/dist/core/tool-catalog-entries.js +57 -42
  278. package/dist/core/tool-conformance.js +1 -0
  279. package/dist/core/tool-errors.d.ts +2 -2
  280. package/dist/core/tool-face-reader.js +5 -3
  281. package/dist/core/tool-face.d.ts +21 -2
  282. package/dist/core/tool-face.js +1 -1
  283. package/dist/core/tool-model-gate.d.ts +3 -3
  284. package/dist/core/tool-name-aliases.d.ts +29 -8
  285. package/dist/core/tool-name-aliases.js +27 -3
  286. package/dist/core/tool-name-readings.d.ts +5 -8
  287. package/dist/core/tool-name-readings.js +1 -2
  288. package/dist/core/tool-policy.d.ts +24 -5
  289. package/dist/core/tool-policy.js +34 -10
  290. package/dist/core/tool-registry.d.ts +14 -7
  291. package/dist/core/tool-registry.js +17 -7
  292. package/dist/core/tool-result-store.js +2 -1
  293. package/dist/core/tool-roster.d.ts +47 -18
  294. package/dist/core/tool-roster.js +53 -32
  295. package/dist/core/tool-spec-members.js +1 -1
  296. package/dist/core/tool-spec.d.ts +35 -21
  297. package/dist/core/tools.js +3 -2
  298. package/dist/core/trace.d.ts +4 -0
  299. package/dist/core/untrusted-egress.d.ts +1 -1
  300. package/dist/core/untrusted-text.d.ts +1 -1
  301. package/dist/core/warm-resume.d.ts +1 -1
  302. package/dist/core/wiring-manifest.d.ts +49 -0
  303. package/dist/core/wiring-manifest.js +13 -5
  304. package/dist/core/workflow-journal-store.d.ts +62 -0
  305. package/dist/core/workflow-journal-store.js +77 -1
  306. package/dist/core/workflow-run-store.d.ts +6 -0
  307. package/dist/core/workflow-run-store.js +2 -0
  308. package/dist/engine/compaction/compaction.d.ts +9 -9
  309. package/dist/engine/execution-env/node-execution-env.d.ts +19 -5
  310. package/dist/engine/execution-env/node-execution-env.js +43 -22
  311. package/dist/engine/harness/agent-harness.d.ts +14 -8
  312. package/dist/engine/harness/agent-harness.js +33 -26
  313. package/dist/engine/harness/types.d.ts +29 -7
  314. package/dist/engine/harness/types.js +12 -1
  315. package/dist/engine/llm/types.d.ts +9 -9
  316. package/dist/engine/llm/validation.d.ts +4 -1
  317. package/dist/engine/llm/validation.js +13 -1
  318. package/dist/engine/loop/agent-loop.d.ts +1 -1
  319. package/dist/engine/loop/agent-loop.js +10 -8
  320. package/dist/engine/loop/types.d.ts +15 -13
  321. package/dist/engine/lsp/node-lsp-manager.d.ts +2 -2
  322. package/dist/engine/session/memory-repo.d.ts +10 -11
  323. package/dist/engine/session/memory-repo.js +13 -8
  324. package/dist/engine/session/parent-chain-guard.d.ts +3 -2
  325. package/dist/engine/session/parent-chain-guard.js +1 -1
  326. package/dist/engine/session/repo-utils.d.ts +31 -3
  327. package/dist/engine/session/repo-utils.js +42 -5
  328. package/dist/index.d.ts +27 -15
  329. package/dist/index.js +21 -11
  330. package/dist/internal/harness-types.d.ts +1 -1
  331. package/dist/internal/harness.d.ts +3 -3
  332. package/dist/internal/harness.js +2 -2
  333. package/dist/internal/llm.d.ts +1 -1
  334. package/dist/internal/llm.js +1 -1
  335. package/dist/orchestration/goal.js +2 -2
  336. package/dist/orchestration/run-workflow-tool.d.ts +2 -2
  337. package/dist/orchestration/run-workflow-tool.js +5 -3
  338. package/dist/orchestration/workflow-size-guideline.d.ts +4 -4
  339. package/dist/orchestration/workflow-types.d.ts +18 -0
  340. package/dist/orchestration/workflow-types.js +6 -0
  341. package/dist/orchestration/workflow.d.ts +2 -13
  342. package/dist/orchestration/workflow.js +21 -31
  343. package/dist/prompt-assembly/event-registry.js +1 -0
  344. package/dist/prompt-assembly/tool-catalog.d.ts +3 -0
  345. package/dist/prompt-assembly/tool-catalog.js +3 -0
  346. package/dist/prompts/default.d.ts +14 -14
  347. package/dist/scenarios/full-body.d.ts +4 -4
  348. package/dist/stores/file/adoption/adopt.d.ts +4 -3
  349. package/dist/stores/file/adoption/adopt.js +24 -31
  350. package/dist/stores/file/adoption/marker.d.ts +6 -13
  351. package/dist/stores/file/adoption/marker.js +6 -15
  352. package/dist/stores/file/checkpoint-store.d.ts +3 -0
  353. package/dist/stores/file/checkpoint-store.js +9 -3
  354. package/dist/stores/file/file-history-store.d.ts +20 -19
  355. package/dist/stores/file/file-history-store.js +152 -135
  356. package/dist/stores/file/fs-atomic.d.ts +15 -0
  357. package/dist/stores/file/fs-atomic.js +89 -14
  358. package/dist/stores/file/index.d.ts +1 -1
  359. package/dist/stores/file/mailbox-store.d.ts +3 -0
  360. package/dist/stores/file/mailbox-store.js +3 -1
  361. package/dist/stores/file/permission-rule-adopt.d.ts +1 -1
  362. package/dist/stores/file/permission-rule-adopt.js +15 -16
  363. package/dist/stores/file/permission-rule-store.d.ts +18 -2
  364. package/dist/stores/file/permission-rule-store.js +21 -14
  365. package/dist/stores/file/session-policy-store.d.ts +3 -3
  366. package/dist/stores/file/session-store.d.ts +26 -11
  367. package/dist/stores/file/session-store.js +77 -40
  368. package/dist/stores/file/workflow-journal-store.d.ts +10 -4
  369. package/dist/stores/file/workflow-journal-store.js +28 -13
  370. package/dist/tools/artifact/artifact-text.d.ts +1 -1
  371. package/dist/tools/artifact/artifact-tool.js +2 -1
  372. package/dist/tools/fs/bash-readonly-classifier.d.ts +3 -3
  373. package/dist/tools/fs/fs-bash.d.ts +2 -2
  374. package/dist/tools/fs/fs-bash.js +3 -1
  375. package/dist/tools/fs/fs-shared.d.ts +8 -8
  376. package/dist/tools/fs/gh-rate-limit.d.ts +1 -1
  377. package/dist/tools/fs/notebook.d.ts +2 -2
  378. package/dist/tools/fs/read-deny.d.ts +29 -7
  379. package/dist/tools/fs/read-deny.js +63 -22
  380. package/dist/tools/fs/removal-targets.d.ts +1 -1
  381. package/dist/tools/fs/safety.d.ts +9 -9
  382. package/dist/tools/fs/search.d.ts +6 -6
  383. package/dist/tools/fs/shell-read-check.js +6 -1
  384. package/dist/tools/fs/shell-read-judge.d.ts +4 -0
  385. package/dist/tools/fs/shell-read-judge.js +5 -2
  386. package/dist/tools/loop-tick.d.ts +2 -2
  387. package/dist/tools/scheduler-tools.js +5 -1
  388. package/dist/tools/task-list.d.ts +7 -7
  389. package/dist/tools/task-list.js +5 -1
  390. package/dist/tools/todo.js +2 -1
  391. package/dist/tools/web.d.ts +1 -1
  392. package/dist/tools/web.js +40 -37
  393. package/dist/tools/worktree.d.ts +3 -3
  394. package/dist/tools/worktree.js +3 -1
  395. package/package.json +1 -1
  396. package/test/export-surface.snapshot.json +5756 -5638
  397. package/wire-changes/core@7.55.0.json +3318 -0
  398. package/dist/core/store-contracts/session-store-incarnation-contract.d.ts +0 -39
  399. package/dist/core/store-contracts/session-store-incarnation-contract.js +0 -95
package/dist/core/mcp.js CHANGED
@@ -7,10 +7,13 @@ import { disposeSectionEntry, sectionAnswerWarning } from "./prompt-section-seat
7
7
  import { findNamespacePrefixCollision, mintNamespacePrefix, mintNamespacedToolName, normalizeNameSegment } from "./protocol-naming.js";
8
8
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
9
9
  import { McpConnectionPool } from "./mcp-connection-pool.js";
10
+ import { SessionEndingTransport, requestSite } from "./mcp-session-end.js";
11
+ import { mcpToolGate, UNPROVEN_GATE } from "./mcp-listing-drift.js";
12
+ import { ConnectionListing, Flights, IDLE_WATCHDOG_ABORT_REASON, Resendable, armMcpIdleWatchdog, callThroughLink, listingRefusal } from "./mcp-server-link.js";
13
+ import { underStartupBound } from "./mcp-startup-request.js";
10
14
  import { LIST_MCP_RESOURCES, MCP_RESOURCE_LIST_ALLOW, MCP_RESOURCE_READ_ALLOW, READ_MCP_RESOURCE, READ_MCP_RESOURCE_DIR, retractInstructions, tellInstructions, toldOnDial, unretractInstructions, withdrawInstructionAdds } from "./mounted-mcp.js";
11
15
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
12
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
13
- import { classifyMcpFailure, describeHttpTransportFailure, livenessOf } from "./mcp-failure.js";
16
+ import { classifyMcpFailure, describeHttpTransportFailure, livenessOf, namingLostSession, sessionGone } from "./mcp-failure.js";
14
17
  import { lstat, mkdir, writeFile } from "node:fs/promises";
15
18
  import { tmpdir } from "node:os";
16
19
  import { join } from "node:path";
@@ -128,33 +131,6 @@ export function mcpIdleTimeoutMs(kind) {
128
131
  function idleKindOf(kind) {
129
132
  return kind === "stdio" ? "stdio" : "http";
130
133
  }
131
- const IDLE_WATCHDOG_ABORT_REASON = Symbol("mcp-idle-watchdog-abort");
132
- function armMcpIdleWatchdog(health, idleMs, outerSignal) {
133
- const idleController = new AbortController();
134
- let timer;
135
- const rearm = () => {
136
- if (timer !== undefined)
137
- clearTimeout(timer);
138
- const armedAt = Date.now();
139
- timer = setTimeout(() => {
140
- if (health.pendingElicitations > 0 || health.lastElicitationClosedAt > armedAt) {
141
- rearm();
142
- return;
143
- }
144
- idleController.abort(IDLE_WATCHDOG_ABORT_REASON);
145
- }, idleMs);
146
- };
147
- rearm();
148
- return {
149
- combinedSignal: outerSignal !== undefined ? AbortSignal.any([outerSignal, idleController.signal]) : idleController.signal,
150
- idleSignal: idleController.signal,
151
- rearm,
152
- dispose: () => {
153
- if (timer !== undefined)
154
- clearTimeout(timer);
155
- },
156
- };
157
- }
158
134
  export function mcpStartupTimeoutMs(server) {
159
135
  const fromEnv = parseEnvMs("MCP_TIMEOUT");
160
136
  const own = server?.startupTimeoutMs;
@@ -231,7 +207,7 @@ function rethrowHonestMcpError(err, ctx) {
231
207
  finish(err);
232
208
  const serverLabel = inlineUntrusted(ctx.server);
233
209
  const fenceServerText = (label, raw) => delimitUntrusted(label, truncateMcpErrorText(raw));
234
- const failure = classifyMcpFailure(err, { phase: "request" });
210
+ const failure = classifyMcpFailure(err, ctx.site);
235
211
  ctx.liveness?.observe?.(failure.kind);
236
212
  const details = (extra) => ({ code: failure.kind, delivered: failure.delivered, server: ctx.server, ...extra });
237
213
  switch (failure.kind) {
@@ -248,12 +224,14 @@ function rethrowHonestMcpError(err, ctx) {
248
224
  case "not_mcp_response":
249
225
  case "connect_refused":
250
226
  case "connection_failed": {
251
- const httpFailure = describeHttpTransportFailure(err);
227
+ const httpFailure = describeHttpTransportFailure(err, ctx.site);
252
228
  const detail = remoteErrorText(err);
253
229
  const fenced = `\nThe transport error follows as external/untrusted data:\n${fenceServerText(`${ctx.server} transport error`, detail)}`;
254
- finish(markMcpFailure(new Error(failure.delivered === "no"
255
- ? `${ctx.what} could not reach MCP server "${serverLabel}": ${httpFailure.condition}. The request was not delivered, so the server did not execute it. This server's tools and resources will keep failing until its endpoint is reachable again — do not retry them; use an alternative if one exists.${fenced}`
256
- : `${ctx.what} failed at the transport layer of MCP server "${serverLabel}": ${httpFailure.condition}. The request may or may not have executed on the server — the outcome is unknown.${writeEffectWarning(ctx.writeEffect)}${fenced}`, { cause: err }), details(failure.httpStatus !== undefined ? { httpStatus: failure.httpStatus } : undefined)));
230
+ finish(markMcpFailure(new Error(sessionGone(err, ctx.site)
231
+ ? `${ctx.what} met a lost session on MCP server "${serverLabel}": ${httpFailure.condition}; ${ctx.resent === true ? "the call was sent again once on a fresh session, and the server reported that one gone too — if the server executed it before answering, it ran twice" : "the call was not sent again — if the server executed it before answering, it ran"}. The outcome is unknown.${writeEffectWarning(ctx.writeEffect)}${fenced}`
232
+ : failure.delivered === "no"
233
+ ? `${ctx.what} could not reach MCP server "${serverLabel}": ${httpFailure.condition}. The request was not delivered, so the server did not execute it. This server's tools and resources will keep failing until its endpoint is reachable again — do not retry them; use an alternative if one exists.${fenced}`
234
+ : `${ctx.what} failed at the transport layer of MCP server "${serverLabel}": ${httpFailure.condition}. The request may or may not have executed on the server — the outcome is unknown.${writeEffectWarning(ctx.writeEffect)}${fenced}`, { cause: err }), details(failure.httpStatus !== undefined ? { httpStatus: failure.httpStatus } : undefined)));
257
235
  }
258
236
  case "protocol": {
259
237
  const code = err.code;
@@ -396,7 +374,7 @@ function buildTransport(spec, principal, kind) {
396
374
  if (!("url" in t))
397
375
  throw new Error(`MCP server "${spec.name}": transport kind "${String(kind)}" carries no url`);
398
376
  const headers = resolveProtocolHttpHeaders(MCP_NAMESPACE.id, t, principal);
399
- return new StreamableHTTPClientTransport(new URL(t.url), {
377
+ return new SessionEndingTransport(new URL(t.url), {
400
378
  requestInit: headers ? { headers } : undefined,
401
379
  fetch: declaredEndpointFetch,
402
380
  });
@@ -663,7 +641,7 @@ export function mcpToolSchemaProblem(schema) {
663
641
  }
664
642
  return undefined;
665
643
  }
666
- export async function materializeMcpTools(specs, principal, onElicit, imageResizer, reminderDisclosure, mcpRevocations, poolOpts, promptSections, dialable) {
644
+ export async function materializeMcpTools(specs, principal, onElicit, imageResizer, reminderDisclosure, mcpRevocations, poolOpts, promptSections, dialable, signal) {
667
645
  const snapshot = snapshotMcpEntries(specs);
668
646
  const axesRefused = new WeakMap();
669
647
  const entries = snapshot.map((spec) => {
@@ -722,11 +700,10 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
722
700
  const droppedTools = [];
723
701
  let disposing = false;
724
702
  const serverHandles = [];
725
- const redialsInFlight = new Set();
726
703
  const commitConnectedServer = (spec, s, transportKind) => {
727
704
  const raised = [];
728
705
  connections.push({ client: s.client, close: s.close });
729
- const handle = { name: spec.name, spec, client: s.client, openListing: s.openListing, health: s.health, tools: s.tools, listedRaw: s.listedTools, transportKind, announce: s.announce, instructions: s.instructions };
706
+ const handle = { name: spec.name, spec, client: s.client, openListing: s.openListing, health: s.health, tools: s.tools, listing: s.listing, gatesAt: s.gatesAt, gates: () => gatesNow(handle), transportKind, announce: s.announce, instructions: s.instructions };
730
707
  const hIdx = serverHandles.findIndex((h) => h.name === spec.name);
731
708
  if (hIdx === -1)
732
709
  serverHandles.push(handle);
@@ -786,9 +763,20 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
786
763
  }
787
764
  };
788
765
  const dialKinds = entries.map(readDialKind);
766
+ const liveOf = (server) => serverHandles.find((h) => h.name === server);
767
+ const gatesNow = (h) => {
768
+ const latest = h.listing.latest();
769
+ if (latest.at !== h.gatesAt.at)
770
+ h.gatesAt = { at: latest.at, gates: intakeListedTools({ tools: [...latest.entries], unproven: latest.unproven }, h.spec, linkOf(h.name), imageResizer, mcpDisclosure, isServerRevoked, h.transportKind).gates };
771
+ return h.gatesAt.gates;
772
+ };
773
+ const linkOf = (server) => ({
774
+ live: () => liveOf(server),
775
+ recover: (lost) => redialFlights.running(server) ?? (liveOf(server) === lost ? redialFlights.start(server, () => redialDial(server)) : Promise.resolve()),
776
+ });
789
777
  const dialServer = (spec, dialKind, dialOpts) => {
790
778
  const refusal = axesRefused.get(spec);
791
- return refusal !== undefined ? Promise.reject(refusal) : connectServer(spec, principal, onElicit, imageResizer, mcpDisclosure, isServerRevoked, dialKind, poolOpts?.pool, dialOpts);
779
+ return refusal !== undefined ? Promise.reject(refusal) : connectServer(spec, principal, onElicit, imageResizer, mcpDisclosure, isServerRevoked, dialKind, poolOpts?.pool, { ...dialOpts, link: linkOf(spec.name), ...(signal !== undefined ? { signal } : {}) });
792
780
  };
793
781
  const prefixOf = (name) => mintNamespacePrefix(MCP_NAMESPACE, name);
794
782
  const heldCount = (name) => serverHandles.find((h) => h.name === name)?.tools.length ?? 0;
@@ -834,89 +822,87 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
834
822
  await Promise.allSettled(settled.map((r) => (r.status === "fulfilled" && r.value !== null ? r.value.close() : Promise.resolve())));
835
823
  throw mergeErr;
836
824
  }
837
- const redialOnce = async (server) => {
825
+ const declineRedial = (server, outcome, reason) => ({ server, prefix: prefixOf(server), outcome, reason, toolCount: heldCount(server), added: [], removed: [] });
826
+ const redialFlights = new Flights();
827
+ const ownerStopped = () => (disposing ? "this materialization is being disposed — the task is tearing down" : signal?.aborted === true ? "the run was aborted" : undefined);
828
+ const redialDial = async (server) => {
838
829
  const prefix = prefixOf(server);
839
- const declined = (outcome, reason) => ({ server, prefix, outcome, reason, toolCount: heldCount(server), added: [], removed: [] });
840
- if (disposing)
841
- return declined("refused", "this materialization is being disposed — the task is tearing down");
830
+ const declined = (outcome, reason) => declineRedial(server, outcome, reason);
831
+ const stopped = ownerStopped();
832
+ if (stopped !== undefined)
833
+ return declined("refused", stopped);
842
834
  const spec = entries.find((e) => e.name === server);
843
835
  if (spec === undefined)
844
836
  return declined("not_declared", `no MCP server named ${inlineUntrusted(server, 120)} is declared for this task`);
845
837
  if (isServerRevoked(server))
846
838
  return declined("refused", "server revoked by the operator — the re-dial did not contact it");
847
- if (redialsInFlight.has(server))
848
- return declined("refused", "a re-dial of this server is already in flight — wait for it to settle");
849
- redialsInFlight.add(server);
850
- try {
851
- const hIdx = serverHandles.findIndex((h) => h.name === server);
852
- const prior = hIdx === -1 ? undefined : serverHandles[hIdx];
853
- const priorNames = prior ? prior.tools.map((t) => t.name) : [];
854
- if (prior) {
855
- prior.announce.fn = undefined;
856
- prior.health.observe = undefined;
857
- serverHandles.splice(hIdx, 1);
858
- const cIdx = connections.findIndex((c) => c.client === prior.client);
859
- const priorConnection = cIdx === -1 ? undefined : connections.splice(cIdx, 1)[0];
860
- await (priorConnection?.close() ?? prior.client.close()).catch(() => undefined);
861
- }
862
- const dialKind = readDialKind(spec);
863
- let connected;
864
- try {
865
- connected = await dialServer(spec, dialKind, { fresh: true, sections: promptSections });
866
- }
867
- catch (err) {
868
- const { kind, delivered, httpStatus } = classifyMcpFailure(err, { phase: "connect", transport: dialKind });
869
- const redialLiveness = livenessOf(kind, Date.now());
870
- const status = { name: server, status: "failed", errorCode: kind, delivered, ...(httpStatus !== undefined ? { httpStatus } : {}), error: namedMcpFailureText(err), ...(redialLiveness !== undefined ? { liveness: redialLiveness } : {}) };
871
- const sIdx = statuses.findIndex((st) => st.name === server);
872
- if (sIdx === -1)
873
- statuses.push(status);
874
- else
875
- statuses[sIdx] = status;
876
- const raised = asServerWarning(spec, err);
877
- warnings.push(raised);
878
- reconcileInstructions(server, undefined, false);
879
- return {
880
- server,
881
- prefix,
882
- outcome: "refused",
883
- reason: inlineUntrusted(namedMcpFailureText(err), 240),
884
- toolCount: 0,
885
- added: [],
886
- removed: priorNames,
887
- tools: [],
888
- axes: [],
889
- dropped: [],
890
- warnings: [raised.message],
891
- status,
892
- };
893
- }
894
- if (disposing) {
895
- await connected.close().catch(() => undefined);
896
- return declined("refused", "this materialization was disposed while the re-dial was in flight — the new connection was closed");
897
- }
898
- const raised = commitConnectedServer(spec, connected, dialKind);
899
- reconcileInstructions(server, connected.instructions, true);
900
- const newNames = connected.tools.map((t) => t.name);
839
+ const hIdx = serverHandles.findIndex((h) => h.name === server);
840
+ const prior = hIdx === -1 ? undefined : serverHandles[hIdx];
841
+ const priorNames = prior ? prior.tools.map((t) => t.name) : [];
842
+ if (prior) {
843
+ prior.announce.fn = undefined;
844
+ prior.health.observe = undefined;
845
+ serverHandles.splice(hIdx, 1);
846
+ const cIdx = connections.findIndex((c) => c.client === prior.client);
847
+ const priorConnection = cIdx === -1 ? undefined : connections.splice(cIdx, 1)[0];
848
+ await (priorConnection?.close() ?? prior.client.close()).catch(() => undefined);
849
+ }
850
+ const dialKind = readDialKind(spec);
851
+ const dialed = await dialServer(spec, dialKind, { fresh: true, sections: promptSections, ...(prior !== undefined ? { carried: prior.listing.latest().entries } : {}) }).then((connected) => ({ connected }), (err) => ({ err }));
852
+ const stoppedLate = ownerStopped();
853
+ if (stoppedLate !== undefined) {
854
+ if ("connected" in dialed)
855
+ await dialed.connected.close().catch(() => undefined);
856
+ return declined("refused", `${stoppedLate} while the re-dial was in flight — nothing new was kept or recorded`);
857
+ }
858
+ if ("err" in dialed) {
859
+ const err = dialed.err;
860
+ const { kind, delivered, httpStatus } = classifyMcpFailure(err, { phase: "connect", transport: dialKind });
861
+ const redialLiveness = livenessOf(kind, Date.now());
862
+ const status = { name: server, status: "failed", errorCode: kind, delivered, ...(httpStatus !== undefined ? { httpStatus } : {}), error: namedMcpFailureText(err), ...(redialLiveness !== undefined ? { liveness: redialLiveness } : {}) };
863
+ const sIdx = statuses.findIndex((st) => st.name === server);
864
+ if (sIdx === -1)
865
+ statuses.push(status);
866
+ else
867
+ statuses[sIdx] = status;
868
+ const raised = asServerWarning(spec, err);
869
+ warnings.push(raised);
870
+ reconcileInstructions(server, undefined, false);
901
871
  return {
902
872
  server,
903
873
  prefix,
904
- outcome: "reconnected",
905
- toolCount: newNames.length,
906
- added: newNames.filter((n) => !priorNames.includes(n)),
907
- removed: priorNames.filter((n) => !newNames.includes(n)),
908
- tools: connected.tools,
909
- axes: connected.axes,
910
- dropped: connected.dropped.map((d) => ({ server: inlineUntrusted(server, 160), ...d })),
911
- ...(raised.length > 0 ? { warnings: raised.map((w) => w.message) } : {}),
912
- status: connected.status,
913
- ...(connected.listingIncomplete !== undefined ? { listingIncomplete: connected.listingIncomplete } : {}),
874
+ outcome: "refused",
875
+ reason: inlineUntrusted(namedMcpFailureText(err), 240),
876
+ toolCount: 0,
877
+ added: [],
878
+ removed: priorNames,
879
+ tools: [],
880
+ axes: [],
881
+ dropped: [],
882
+ warnings: [raised.message],
883
+ status,
914
884
  };
915
885
  }
916
- finally {
917
- redialsInFlight.delete(server);
918
- }
886
+ const { connected } = dialed;
887
+ const raised = commitConnectedServer(spec, connected, dialKind);
888
+ reconcileInstructions(server, connected.instructions, true);
889
+ const newNames = connected.tools.map((t) => t.name);
890
+ return {
891
+ server,
892
+ prefix,
893
+ outcome: "reconnected",
894
+ toolCount: newNames.length,
895
+ added: newNames.filter((n) => !priorNames.includes(n)),
896
+ removed: priorNames.filter((n) => !newNames.includes(n)),
897
+ tools: connected.tools,
898
+ axes: connected.axes,
899
+ dropped: connected.dropped.map((d) => ({ server: inlineUntrusted(server, 160), ...d })),
900
+ ...(raised.length > 0 ? { warnings: raised.map((w) => w.message) } : {}),
901
+ status: connected.status,
902
+ ...(connected.listingIncomplete !== undefined ? { listingIncomplete: connected.listingIncomplete } : {}),
903
+ };
919
904
  };
905
+ const redialOnce = (server) => (redialFlights.running(server) === undefined ? redialFlights.start(server, () => redialDial(server)) : Promise.resolve(declineRedial(server, "refused", "a re-dial of this server is already in flight — wait for it to settle")));
920
906
  return {
921
907
  tools,
922
908
  toolAxes,
@@ -940,20 +926,25 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
940
926
  results.push({ server: h.name, prefix: prefixOf(h.name), status: "revoked", toolCount: h.tools.length, added: [], removed: [], error: "server revoked by the operator — the refresh did not contact it" });
941
927
  continue;
942
928
  }
929
+ const site = requestSite(h.client);
943
930
  try {
931
+ const at = h.listing.open();
944
932
  const cacheTools = h.openListing();
945
933
  const listed = await listToolsLenient(h.client);
946
- const advertised = new Set(listed.tools.map((t) => t.name));
947
- const retainedRaw = listed.incomplete !== undefined ? h.listedRaw.filter((t) => !advertised.has(t.name)) : [];
948
- const mergedRaw = retainedRaw.length > 0 ? [...listed.tools, ...retainedRaw] : listed.tools;
949
- const { serverTools, serverAxes, dropped, accepted } = intakeListedTools({ tools: mergedRaw }, h.spec, h.client, h.health, imageResizer, mcpDisclosure, isServerRevoked, h.transportKind);
950
- cacheTools(accepted, new Set(retainedRaw.map((t) => t.name)));
934
+ const answered = h.listing.answer(at, listed.tools, listed.incomplete !== undefined);
935
+ const mergedRaw = answered.view;
936
+ if (!answered.recorded) {
937
+ results.push({ server: h.name, prefix: prefixOf(h.name), status: "failed", toolCount: h.tools.length, added: [], removed: [], error: "a later listing of this server was opened and applied while this one was in the air — this one was discarded" });
938
+ continue;
939
+ }
940
+ const { serverTools, serverAxes, gates, dropped, accepted } = intakeListedTools({ tools: [...mergedRaw], unproven: answered.unproven }, h.spec, linkOf(h.name), imageResizer, mcpDisclosure, isServerRevoked, h.transportKind);
941
+ cacheTools(accepted, answered.kept);
951
942
  const priorNames = h.tools.map((t) => t.name);
952
943
  const newNames = serverTools.map((t) => t.name);
953
944
  const added = newNames.filter((n) => !priorNames.includes(n));
954
945
  const removed = priorNames.filter((n) => !newNames.includes(n));
955
946
  h.tools = serverTools;
956
- h.listedRaw = mergedRaw;
947
+ h.gatesAt = { at, gates };
957
948
  results.push({
958
949
  server: h.name,
959
950
  prefix: prefixOf(h.name),
@@ -971,7 +962,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
971
962
  });
972
963
  }
973
964
  catch (err) {
974
- h.health.observe?.(classifyMcpFailure(err, { phase: "request" }).kind);
965
+ h.health.observe?.(classifyMcpFailure(err, site).kind);
975
966
  results.push({
976
967
  server: h.name,
977
968
  prefix: prefixOf(h.name),
@@ -979,7 +970,7 @@ export async function materializeMcpTools(specs, principal, onElicit, imageResiz
979
970
  toolCount: h.tools.length,
980
971
  added: [],
981
972
  removed: [],
982
- error: inlineUntrusted(namedMcpFailureText(err), 240),
973
+ error: inlineUntrusted(namingLostSession(err, site, namedMcpFailureText(err)), 240),
983
974
  });
984
975
  }
985
976
  }
@@ -1266,6 +1257,7 @@ function buildResourceTools(resourceServers, isServerRevoked = () => false) {
1266
1257
  }
1267
1258
  const idleMs = mcpIdleTimeoutMs(rs.transportKind);
1268
1259
  const watchdog = armMcpIdleWatchdog(rs.health, idleMs, signal);
1260
+ const site = requestSite(rs.client);
1269
1261
  try {
1270
1262
  const walk = await walkMcpListPages(async (cursor, remainingMs) => {
1271
1263
  const page = await rs.client
@@ -1291,7 +1283,7 @@ function buildResourceTools(resourceServers, isServerRevoked = () => false) {
1291
1283
  throw err;
1292
1284
  const msg = watchdog.idleSignal.reason === IDLE_WATCHDOG_ABORT_REASON
1293
1285
  ? idleWatchdogTripText(idleMs)
1294
- : remoteErrorText(err);
1286
+ : namingLostSession(err, site, remoteErrorText(err));
1295
1287
  errors.push({ server: rs.server, error: msg });
1296
1288
  sections.push(`[${rs.server}] Error: could not list resources: ${inlineUntrusted(msg)}`);
1297
1289
  }
@@ -1345,11 +1337,12 @@ function buildResourceTools(resourceServers, isServerRevoked = () => false) {
1345
1337
  const timeoutMs = mcpToolTimeoutMs();
1346
1338
  const idleMs = mcpIdleTimeoutMs(rs.transportKind);
1347
1339
  const watchdog = armMcpIdleWatchdog(rs.health, idleMs, signal);
1340
+ const site = requestSite(rs.client);
1348
1341
  let res;
1349
1342
  try {
1350
1343
  res = await rs.client
1351
1344
  .readResource({ uri }, { signal: watchdog.combinedSignal, timeout: timeoutMs })
1352
- .catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs, writeEffect: false, signal, attributeServer: true, idle: { signal: watchdog.idleSignal, idleMs }, liveness: rs.health }));
1345
+ .catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs, writeEffect: false, signal, attributeServer: true, site, idle: { signal: watchdog.idleSignal, idleMs }, liveness: rs.health }));
1353
1346
  rs.health.observe?.("completed");
1354
1347
  }
1355
1348
  finally {
@@ -1406,10 +1399,11 @@ function buildResourceTools(resourceServers, isServerRevoked = () => false) {
1406
1399
  const timeoutMs = mcpToolTimeoutMs();
1407
1400
  const idleMs = mcpIdleTimeoutMs(rs.transportKind);
1408
1401
  const watchdog = armMcpIdleWatchdog(rs.health, idleMs, signal);
1402
+ const site = requestSite(rs.client);
1409
1403
  try {
1410
1404
  const ext = rs.client.getServerCapabilities()?.extensions?.[MCP_SKILLS_EXTENSION];
1411
1405
  if (ext?.directoryRead === true) {
1412
- const r = await readDirViaExtension(rs, uri, signal, timeoutMs, watchdog).catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs, writeEffect: false, signal, attributeServer: true, idle: { signal: watchdog.idleSignal, idleMs }, liveness: rs.health }));
1406
+ const r = await readDirViaExtension(rs, uri, signal, timeoutMs, watchdog).catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs, writeEffect: false, signal, attributeServer: true, site, idle: { signal: watchdog.idleSignal, idleMs }, liveness: rs.health }));
1413
1407
  const serverSaid = (detail) => `\nThe server's error text follows as external/untrusted data:\n${delimitUntrusted(`${server} error`, truncateMcpErrorText(detail))}`;
1414
1408
  if (r.kind === "not_found") {
1415
1409
  return {
@@ -1437,7 +1431,7 @@ function buildResourceTools(resourceServers, isServerRevoked = () => false) {
1437
1431
  const walk = await walkMcpListPages(async (cursor, remainingMs) => {
1438
1432
  const page = await rs.client
1439
1433
  .listResources(cursor === undefined ? undefined : { cursor }, { signal: watchdog.combinedSignal, timeout: remainingMs })
1440
- .catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs: remainingMs, writeEffect: false, signal, attributeServer: true, idle: { signal: watchdog.idleSignal, idleMs }, liveness: rs.health }));
1434
+ .catch((err) => rethrowHonestMcpError(err, { server, what, timeoutMs: remainingMs, writeEffect: false, signal, attributeServer: true, site, idle: { signal: watchdog.idleSignal, idleMs }, liveness: rs.health }));
1441
1435
  rs.health.observe?.("completed");
1442
1436
  return { items: page.resources, ...(page.nextCursor !== undefined ? { nextCursor: page.nextCursor } : {}) };
1443
1437
  }, { budgetMs: timeoutMs, onPage: () => watchdog.rearm(), fingerprintOf: listEntryFingerprint, ...(signal !== undefined ? { signal } : {}) });
@@ -1532,7 +1526,8 @@ async function connectServer(spec, principal, onElicit, imageResizer, reminderDi
1532
1526
  const announce = {};
1533
1527
  const startupMs = mcpStartupTimeoutMs(spec);
1534
1528
  const startupOpts = startupMs !== undefined ? { timeout: startupMs } : undefined;
1535
- const dialClient = async () => {
1529
+ const dialClient = async (stop = dialOpts?.signal) => {
1530
+ stop?.throwIfAborted();
1536
1531
  const client = new Client({ name: `sema-core/${spec.name}`, version: "0.1.0" }, { capabilities: elicitOn ? { elicitation: { form: {} } } : {} });
1537
1532
  if (elicitOn) {
1538
1533
  client.setRequestHandler(ElicitRequestSchema, async (request, extra) => {
@@ -1555,12 +1550,17 @@ async function connectServer(spec, principal, onElicit, imageResizer, reminderDi
1555
1550
  });
1556
1551
  }
1557
1552
  const transport = buildTransport(spec, principal, transportKind);
1553
+ const hangUp = () => void client.close().catch(() => undefined);
1554
+ stop?.addEventListener("abort", hangUp, { once: true });
1558
1555
  try {
1559
1556
  await client.connect(transport, startupOpts);
1560
1557
  }
1561
1558
  catch (err) {
1562
1559
  await client.close().catch(() => undefined);
1563
- throw err;
1560
+ throw stop?.aborted === true ? stop.reason : err;
1561
+ }
1562
+ finally {
1563
+ stop?.removeEventListener("abort", hangUp);
1564
1564
  }
1565
1565
  return client;
1566
1566
  };
@@ -1580,10 +1580,13 @@ async function connectServer(spec, principal, onElicit, imageResizer, reminderDi
1580
1580
  lease.onClose(onClosed);
1581
1581
  else
1582
1582
  client.onclose = onClosed;
1583
+ const listing = lease?.listing ?? new ConnectionListing();
1584
+ const at = listing.open();
1583
1585
  const cacheTools = openListing();
1584
- const listed = await listToolsLenient(client, startupOpts);
1585
- const { serverTools, serverAxes, dropped, advisories, accepted } = intakeListedTools(listed, spec, client, health, imageResizer, reminderDisclosure, isServerRevoked, transportKind);
1586
- cacheTools(accepted);
1586
+ const listed = await underStartupBound(startupMs, dialOpts?.signal, (options) => listToolsLenient(client, options));
1587
+ const answered = listing.answer(at, listed.tools, listed.incomplete !== undefined, dialOpts.carried);
1588
+ const { serverTools, serverAxes, gates, dropped, advisories, accepted } = intakeListedTools({ tools: [...answered.view], unproven: answered.unproven }, spec, dialOpts.link, imageResizer, reminderDisclosure, isServerRevoked, transportKind);
1589
+ cacheTools(accepted, answered.kept);
1587
1590
  const caps = client.getServerCapabilities();
1588
1591
  const resourceInfo = caps?.resources
1589
1592
  ? {
@@ -1612,10 +1615,11 @@ async function connectServer(spec, principal, onElicit, imageResizer, reminderDi
1612
1615
  announce,
1613
1616
  tools: serverTools,
1614
1617
  axes: serverAxes,
1618
+ listing,
1619
+ gatesAt: { at, gates },
1615
1620
  dropped,
1616
1621
  schemaAdvisories: advisories,
1617
1622
  ...(listed.toolsCapabilityAbsent === true ? { toolsCapabilityAbsent: true } : {}),
1618
- listedTools: listed.tools,
1619
1623
  ...(instructions ? { instructions } : {}),
1620
1624
  ...(disposed?.defect !== undefined ? { instructionsDefect: disposed.defect } : {}),
1621
1625
  ...(listed.incomplete !== undefined ? { listingIncomplete: listed.incomplete } : {}),
@@ -1627,10 +1631,11 @@ async function connectServer(spec, principal, onElicit, imageResizer, reminderDi
1627
1631
  throw err;
1628
1632
  }
1629
1633
  }
1630
- function intakeListedTools(listed, spec, client, health, imageResizer, reminderDisclosure, isServerRevoked, transportKind) {
1634
+ function intakeListedTools(listed, spec, link, imageResizer, reminderDisclosure, isServerRevoked, transportKind) {
1631
1635
  const declaredContentOrigin = spec.contentOrigin;
1632
1636
  const serverTools = [];
1633
1637
  const serverAxes = [];
1638
+ const gates = new Map();
1634
1639
  const dropped = [];
1635
1640
  const accepted = [];
1636
1641
  const advisories = [];
@@ -1697,6 +1702,9 @@ function intakeListedTools(listed, spec, client, health, imageResizer, reminderD
1697
1702
  if (interaction.advisory !== undefined)
1698
1703
  advisories.push({ tool: inlineUntrusted(t.name), reason: interaction.advisory });
1699
1704
  const mcpAlwaysLoad = mcpToolMeta?.["anthropic/alwaysLoad"] === true;
1705
+ const gateFace = { ...(interaction.declared ? { mcpRequiresUserInteraction: true } : {}), ...(t.annotations?.readOnlyHint === true ? { mcpReadOnlyHint: true } : {}) };
1706
+ const gate = mcpToolGate(remoteName, axis, gateFace, taskRequired);
1707
+ gates.set(namespacedName, listed.unproven?.has(remoteName) === true ? UNPROVEN_GATE : gate);
1700
1708
  const mounted = {
1701
1709
  name: namespacedName,
1702
1710
  description: capMcpToolDescription(effectiveDescription ?? `MCP tool ${inlineUntrusted(sanitizeMcpModelFacingText(remoteName))} from ${spec.name}`),
@@ -1704,8 +1712,8 @@ function intakeListedTools(listed, spec, client, health, imageResizer, reminderD
1704
1712
  parameters: (effectiveInputSchema ?? { type: "object" }),
1705
1713
  ...(mcpMaxResultSizeChars !== undefined ? { mcpMaxResultSizeChars } : {}),
1706
1714
  ...(mcpAlwaysLoad ? { mcpAlwaysLoad: true } : {}),
1707
- ...(interaction.declared ? { mcpRequiresUserInteraction: true } : {}),
1708
- mcpRemoteName: remoteName, ...boundaryFlowFace(spec.toolAxes?.[remoteName]), ...(t.annotations?.readOnlyHint === true ? { mcpReadOnlyHint: true } : {}), permissionSeat: "passthrough",
1715
+ ...gateFace,
1716
+ mcpRemoteName: remoteName, ...boundaryFlowFace(spec.toolAxes?.[remoteName]), permissionSeat: "passthrough",
1709
1717
  execute: async (_toolCallId, params, signal) => {
1710
1718
  const what = `The call to tool ${inlineUntrusted(remoteName)}`;
1711
1719
  if (isServerRevoked?.(spec.name) === true) {
@@ -1724,38 +1732,55 @@ function intakeListedTools(listed, spec, client, health, imageResizer, reminderD
1724
1732
  isError: true,
1725
1733
  };
1726
1734
  }
1727
- if (health.dead)
1728
- throwDeadServer(spec.name, what);
1729
1735
  const timeoutMs = mcpToolTimeoutMs();
1730
1736
  const idleMs = mcpIdleTimeoutMs(idleKindOf(transportKind));
1731
- const watchdog = armMcpIdleWatchdog(health, idleMs, signal);
1732
- let res;
1733
- try {
1734
- res = await client
1735
- .callTool({ name: remoteName, arguments: (params ?? {}) }, LENIENT_CALL_TOOL_RESULT_SCHEMA, {
1736
- signal: watchdog.combinedSignal,
1737
- timeout: timeoutMs,
1738
- resetTimeoutOnProgress: true,
1739
- onprogress: () => watchdog.rearm(),
1740
- maxTotalTimeout: mcpToolTotalTimeoutMs(timeoutMs),
1741
- })
1742
- .catch((err) => rethrowHonestMcpError(err, {
1743
- server: spec.name,
1744
- what,
1745
- timeoutMs,
1746
- writeEffect,
1747
- signal,
1748
- idle: { signal: watchdog.idleSignal, idleMs },
1749
- liveness: health,
1750
- ...(reminderDisclosure !== undefined
1751
- ? { reminder: { mark: reminderDisclosure.mark, outlet: "mcp", ...(reminderDisclosure.counts !== undefined ? { counts: reminderDisclosure.counts } : {}) } }
1752
- : {}),
1753
- }));
1754
- health.observe?.("completed");
1755
- }
1756
- finally {
1757
- watchdog.dispose();
1758
- }
1737
+ const send = async (conn, resent) => {
1738
+ const site = requestSite(conn.client);
1739
+ const watchdog = armMcpIdleWatchdog(conn.health, idleMs, signal);
1740
+ try {
1741
+ const res = await conn.client
1742
+ .callTool({ name: remoteName, arguments: (params ?? {}) }, LENIENT_CALL_TOOL_RESULT_SCHEMA, {
1743
+ signal: watchdog.combinedSignal,
1744
+ timeout: timeoutMs,
1745
+ resetTimeoutOnProgress: true,
1746
+ onprogress: () => watchdog.rearm(),
1747
+ maxTotalTimeout: mcpToolTotalTimeoutMs(timeoutMs),
1748
+ })
1749
+ .catch((err) => {
1750
+ try {
1751
+ return rethrowHonestMcpError(err, {
1752
+ server: spec.name,
1753
+ what,
1754
+ timeoutMs,
1755
+ writeEffect,
1756
+ signal,
1757
+ site,
1758
+ ...(resent ? { resent: true } : {}),
1759
+ idle: { signal: watchdog.idleSignal, idleMs },
1760
+ liveness: conn.health,
1761
+ ...(reminderDisclosure !== undefined
1762
+ ? { reminder: { mark: reminderDisclosure.mark, outlet: "mcp", ...(reminderDisclosure.counts !== undefined ? { counts: reminderDisclosure.counts } : {}) } }
1763
+ : {}),
1764
+ });
1765
+ }
1766
+ catch (failure) {
1767
+ if (sessionGone(err, site) && signal?.aborted !== true)
1768
+ return new Resendable(conn, failure);
1769
+ throw failure;
1770
+ }
1771
+ });
1772
+ if (res instanceof Resendable)
1773
+ return res;
1774
+ conn.health.observe?.("completed");
1775
+ return res;
1776
+ }
1777
+ finally {
1778
+ watchdog.dispose();
1779
+ }
1780
+ };
1781
+ const res = await callThroughLink(link, namespacedName, gate, signal, send, () => throwDeadServer(spec.name, what));
1782
+ if (typeof res === "string")
1783
+ return listingRefusal(res, remoteName, spec.name);
1759
1784
  const mapped = await mapContent(res.content, spec.name, imageResizer);
1760
1785
  if (res.isError === true) {
1761
1786
  const parts = [];
@@ -1815,7 +1840,7 @@ function intakeListedTools(listed, spec, client, health, imageResizer, reminderD
1815
1840
  serverTools.push(declaredContentOrigin !== undefined ? withContentOrigin(mounted, declaredContentOrigin) : mounted);
1816
1841
  accepted.push(t);
1817
1842
  }
1818
- return { serverTools, serverAxes, dropped, advisories, accepted };
1843
+ return { serverTools, serverAxes, gates, dropped, advisories, accepted };
1819
1844
  }
1820
1845
  function asServerWarning(spec, err) {
1821
1846
  const warning = new Error(`mcp: server "${spec.name}" failed to connect — skipped (${namedMcpFailureText(err)})`, { cause: sanitizedCause(err) });
@@ -4,7 +4,7 @@ import type { DocumentContent, ImageContent } from "../internal/llm.js";
4
4
  * Aggregate MEDIA (image) byte budget — the multimodal sibling of {@link AGGREGATE_TOOL_RESULT_BUDGET_CHARS}
5
5
  * (which only counts TEXT, design/64 §17.2). batch-C added image reads (`Read` → `ImageContent`); each is bounded
6
6
  * per-image (`MAX_IMAGE_READ_BYTES`), but N images accumulate ACROSS turns in the context and would otherwise grow
7
- * the request unboundedly (a real gap — CC 2.1.196 added the same cap, `tengu_media_byte_cap`, validating it).
7
+ * the request unboundedly (a real gap — the reference added the same cap, `tengu_media_byte_cap`, validating it).
8
8
  *
9
9
  * This caps the SUM of inline image bytes across the whole message list: while over budget, the OLDEST images are
10
10
  * stripped (replaced with a short text marker) until it fits — recent media (most relevant) is kept. Like the text