@sayknow-cli/coding-agent 0.4.7 → 0.5.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 (428) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/dist/types/capability/index.d.ts +10 -20
  3. package/dist/types/capability/types.d.ts +3 -0
  4. package/dist/types/cli/args.d.ts +0 -2
  5. package/dist/types/cli/read-cli.d.ts +1 -0
  6. package/dist/types/cli.d.ts +9 -1
  7. package/dist/types/commands/daemon.d.ts +2 -2
  8. package/dist/types/commands/deep-interview.d.ts +9 -0
  9. package/dist/types/commands/harness.d.ts +22 -0
  10. package/dist/types/commands/read.d.ts +6 -0
  11. package/dist/types/commands/sdk.d.ts +20 -2
  12. package/dist/types/config/keybindings.d.ts +34 -4
  13. package/dist/types/config/model-profile-contract.d.ts +40 -0
  14. package/dist/types/config/model-profiles.d.ts +2 -3
  15. package/dist/types/config/model-registry.d.ts +5 -0
  16. package/dist/types/config/provider-auth-health.d.ts +14 -0
  17. package/dist/types/config/provider-ranking.d.ts +59 -0
  18. package/dist/types/config/settings-schema.d.ts +156 -10
  19. package/dist/types/config/settings.d.ts +3 -0
  20. package/dist/types/edit/modes/patch.d.ts +13 -0
  21. package/dist/types/edit/path-mutation-lock.d.ts +15 -0
  22. package/dist/types/eval/py/executor.d.ts +1 -1
  23. package/dist/types/eval/py/runner-artifact.d.ts +8 -0
  24. package/dist/types/eval/py/tool-bridge.d.ts +1 -2
  25. package/dist/types/extensibility/extensions/types.d.ts +43 -2
  26. package/dist/types/extensibility/shared-events.d.ts +6 -0
  27. package/dist/types/internal-urls/local-root-gc.d.ts +2 -0
  28. package/dist/types/main.d.ts +33 -2
  29. package/dist/types/memories/index.d.ts +2 -0
  30. package/dist/types/modes/acp/acp-agent.d.ts +22 -9
  31. package/dist/types/modes/components/assistant-message.d.ts +3 -1
  32. package/dist/types/modes/components/btw-panel.d.ts +1 -0
  33. package/dist/types/modes/components/irc-sidebar.d.ts +41 -0
  34. package/dist/types/modes/components/queue-pane.d.ts +8 -0
  35. package/dist/types/modes/components/queued-message-selector.d.ts +6 -0
  36. package/dist/types/modes/components/read-tool-group.d.ts +4 -4
  37. package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
  38. package/dist/types/modes/components/session-observer-overlay.d.ts +4 -0
  39. package/dist/types/modes/components/settings-selector.d.ts +2 -0
  40. package/dist/types/modes/components/tool-execution.d.ts +8 -0
  41. package/dist/types/modes/components/tool-status-header.d.ts +4 -2
  42. package/dist/types/modes/components/welcome.d.ts +5 -0
  43. package/dist/types/modes/controllers/btw-controller.d.ts +5 -0
  44. package/dist/types/modes/controllers/command-controller.d.ts +2 -0
  45. package/dist/types/modes/controllers/event-controller.d.ts +3 -0
  46. package/dist/types/modes/interactive-mode.d.ts +13 -2
  47. package/dist/types/modes/irc-observation-ledger.d.ts +17 -1
  48. package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
  49. package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
  50. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
  51. package/dist/types/modes/types.d.ts +6 -0
  52. package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
  53. package/dist/types/modes/utils/ui-helpers.d.ts +16 -0
  54. package/dist/types/runtime/memory-domain.d.ts +9 -0
  55. package/dist/types/runtime/memory-guard-contract.d.ts +71 -0
  56. package/dist/types/runtime/memory-guard.d.ts +39 -0
  57. package/dist/types/runtime/memory-limit.d.ts +11 -0
  58. package/dist/types/runtime-mcp/content-limits.d.ts +7 -0
  59. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +1 -1
  60. package/dist/types/runtime-mcp/manager.d.ts +9 -1
  61. package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
  62. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
  63. package/dist/types/runtime-mcp/redaction.d.ts +2 -0
  64. package/dist/types/runtime-mcp/smithery-auth.d.ts +5 -0
  65. package/dist/types/sdk/acp/final-text.d.ts +19 -0
  66. package/dist/types/sdk/acp/mcp.d.ts +23 -0
  67. package/dist/types/sdk/broker/broker.d.ts +2 -0
  68. package/dist/types/sdk/broker/lifecycle.d.ts +13 -0
  69. package/dist/types/sdk/bus/chat-daemon-control.d.ts +9 -1
  70. package/dist/types/sdk/bus/config-commands.d.ts +2 -2
  71. package/dist/types/sdk/bus/config.d.ts +2 -0
  72. package/dist/types/sdk/bus/control-drain-lease.d.ts +52 -0
  73. package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
  74. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  75. package/dist/types/sdk/bus/index.d.ts +52 -4
  76. package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +37 -0
  77. package/dist/types/sdk/bus/lifecycle-commands.d.ts +5 -4
  78. package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
  79. package/dist/types/sdk/bus/notification-service.d.ts +34 -0
  80. package/dist/types/sdk/bus/operator-runtime.d.ts +1 -0
  81. package/dist/types/sdk/bus/prompt-reconciliation.d.ts +106 -0
  82. package/dist/types/sdk/bus/rate-limit-pool.d.ts +2 -0
  83. package/dist/types/sdk/bus/reconciliation-store.d.ts +65 -0
  84. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +33 -9
  85. package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
  86. package/dist/types/sdk/bus/telegram-daemon.d.ts +286 -12
  87. package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
  88. package/dist/types/sdk/bus/topic-registry.d.ts +88 -2
  89. package/dist/types/sdk/client/discovery.d.ts +2 -0
  90. package/dist/types/sdk/client/liveness.d.ts +7 -0
  91. package/dist/types/sdk/host/control/operations.d.ts +3 -2
  92. package/dist/types/sdk/host/host.d.ts +3 -1
  93. package/dist/types/sdk/host/query/handlers.d.ts +14 -0
  94. package/dist/types/sdk/index.d.ts +2 -0
  95. package/dist/types/sdk/lifecycle-session.d.ts +10 -1
  96. package/dist/types/sdk/prompt-status.d.ts +88 -0
  97. package/dist/types/sdk/session-directory.d.ts +1 -1
  98. package/dist/types/sdk/session.d.ts +7 -0
  99. package/dist/types/sdk/startup-capability.d.ts +9 -0
  100. package/dist/types/sdk/transport/auth-preface.d.ts +9 -0
  101. package/dist/types/sdk/transport/index.d.ts +12 -0
  102. package/dist/types/sdk/transport/relay.d.ts +43 -0
  103. package/dist/types/sdk/transport/serve-cli.d.ts +4 -0
  104. package/dist/types/sdk/transport/socket.d.ts +7 -0
  105. package/dist/types/sdk/transport/stdio.d.ts +3 -0
  106. package/dist/types/session/agent-session.d.ts +83 -24
  107. package/dist/types/session/btw-contract.d.ts +16 -0
  108. package/dist/types/session/fallback-chain-controller.d.ts +18 -2
  109. package/dist/types/session/internal/managed-session-scope.d.ts +70 -10
  110. package/dist/types/session/internal/managed-session-storage.d.ts +34 -12
  111. package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
  112. package/dist/types/session/memory-guard-checkpoint-participant.d.ts +69 -0
  113. package/dist/types/session/session-manager.d.ts +89 -18
  114. package/dist/types/session/session-storage.d.ts +31 -0
  115. package/dist/types/session/streaming-output.d.ts +60 -5
  116. package/dist/types/setup/credential-import.d.ts +5 -1
  117. package/dist/types/setup/provider-onboarding.d.ts +2 -0
  118. package/dist/types/skc-runtime/deep-interview-runtime.d.ts +1 -1
  119. package/dist/types/skc-runtime/deep-interview-stage.d.ts +37 -0
  120. package/dist/types/skc-runtime/gc-runtime.d.ts +1 -1
  121. package/dist/types/skc-runtime/linux-proc.d.ts +22 -14
  122. package/dist/types/skc-runtime/memory-guard-owner-claims.d.ts +47 -0
  123. package/dist/types/skc-runtime/ralplan-runtime.d.ts +112 -1
  124. package/dist/types/skc-runtime/repository-binding.d.ts +63 -0
  125. package/dist/types/skc-runtime/state-runtime.d.ts +3 -1
  126. package/dist/types/skc-runtime/state-writer.d.ts +5 -1
  127. package/dist/types/skc-runtime/team-launch.d.ts +1 -1
  128. package/dist/types/skc-runtime/team-runtime.d.ts +9 -2
  129. package/dist/types/skc-runtime/team-store.d.ts +12 -0
  130. package/dist/types/skc-runtime/team-worker-memory-guard.d.ts +92 -0
  131. package/dist/types/skc-runtime/tmux-owner-isolation.d.ts +7 -0
  132. package/dist/types/skc-runtime/ultragoal-guard.d.ts +6 -5
  133. package/dist/types/skc-runtime/ultragoal-receipt-freshness.d.ts +28 -0
  134. package/dist/types/skc-runtime/ultragoal-runtime.d.ts +43 -5
  135. package/dist/types/skill-state/workflow-hud.d.ts +3 -0
  136. package/dist/types/ssh/utils.d.ts +1 -0
  137. package/dist/types/task/discovery.d.ts +3 -1
  138. package/dist/types/task/executor.d.ts +6 -0
  139. package/dist/types/task/index.d.ts +3 -1
  140. package/dist/types/task/provider-retry-status.d.ts +13 -0
  141. package/dist/types/task/receipt.d.ts +2 -0
  142. package/dist/types/task/types.d.ts +133 -0
  143. package/dist/types/task/ultragoal-redteam-activation.d.ts +33 -0
  144. package/dist/types/tools/browser/launch.d.ts +7 -0
  145. package/dist/types/tools/fetch.d.ts +22 -9
  146. package/dist/types/tools/image-gen.d.ts +63 -1
  147. package/dist/types/tools/index.d.ts +7 -1
  148. package/dist/types/tools/output-meta.d.ts +21 -1
  149. package/dist/types/tools/read-internals.d.ts +12 -0
  150. package/dist/types/tools/read.d.ts +45 -1
  151. package/dist/types/tools/resource-gc.d.ts +23 -0
  152. package/dist/types/tools/skill-discovery.d.ts +7 -0
  153. package/dist/types/tools/sqlite-reader.d.ts +7 -0
  154. package/dist/types/tools/tool-result.d.ts +7 -1
  155. package/dist/types/utils/prompt-suggestion.d.ts +33 -0
  156. package/dist/types/utils/shell-snapshot.d.ts +12 -0
  157. package/dist/types/web/insane/bridge.d.ts +1 -0
  158. package/dist/types/web/insane/url-guard.d.ts +17 -0
  159. package/package.json +8 -12
  160. package/scripts/benchmark-sticky-viewport-pr1.ts +239 -0
  161. package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
  162. package/scripts/capture-sticky-viewport-showcase.ts +292 -0
  163. package/scripts/compile-args.ts +0 -1
  164. package/scripts/dogfood-repository-binding.ts +209 -0
  165. package/scripts/generate-docs-index.ts +4 -1
  166. package/scripts/generate-sdk-operation-inventory.ts +9 -0
  167. package/scripts/run-test-manifest.ts +6 -4
  168. package/scripts/verify-skc-sdk-canonicalization.ts +122 -18
  169. package/scripts/verify-sticky-viewport-showcase.ts +611 -0
  170. package/src/capability/index.ts +58 -42
  171. package/src/capability/types.ts +3 -0
  172. package/src/cli/args.ts +16 -100
  173. package/src/cli/fast-help.ts +1 -1
  174. package/src/cli/mcp-cli.ts +4 -21
  175. package/src/cli/notify-cli.ts +6 -38
  176. package/src/cli/read-cli.ts +2 -1
  177. package/src/cli/setup-cli.ts +10 -4
  178. package/src/cli.ts +133 -11
  179. package/src/commands/daemon.ts +22 -5
  180. package/src/commands/deep-interview.ts +26 -1
  181. package/src/commands/harness.ts +35 -12
  182. package/src/commands/read.ts +11 -2
  183. package/src/commands/sdk.ts +134 -17
  184. package/src/commands/team.ts +1 -1
  185. package/src/commit/agentic/index.ts +3 -3
  186. package/src/commit/map-reduce/index.ts +2 -2
  187. package/src/config/keybindings.ts +158 -25
  188. package/src/config/model-profile-contract.ts +174 -0
  189. package/src/config/model-profiles.ts +51 -10
  190. package/src/config/model-registry.ts +24 -4
  191. package/src/config/model-resolver.ts +2 -2
  192. package/src/config/provider-auth-health.ts +42 -0
  193. package/src/config/provider-ranking.ts +119 -0
  194. package/src/config/settings-schema.ts +157 -10
  195. package/src/config/settings.ts +254 -108
  196. package/src/coordinator-mcp/model-preset.ts +21 -74
  197. package/src/defaults/skc/skills/deep-interview/SKILL.md +147 -24
  198. package/src/defaults/skc/skills/ralplan/SKILL.md +91 -34
  199. package/src/defaults/skc/skills/team/SKILL.md +1 -1
  200. package/src/defaults/skc/skills/ultragoal/SKILL.md +68 -17
  201. package/src/discovery/ssh.ts +12 -1
  202. package/src/edit/index.ts +3 -6
  203. package/src/edit/modes/patch.ts +38 -1
  204. package/src/edit/modes/replace.ts +42 -0
  205. package/src/edit/path-mutation-lock.ts +112 -0
  206. package/src/eval/js/tool-bridge.ts +1 -1
  207. package/src/eval/py/executor.ts +37 -13
  208. package/src/eval/py/kernel.ts +1 -21
  209. package/src/eval/py/prelude.py +2 -2
  210. package/src/eval/py/runner-artifact.ts +84 -0
  211. package/src/eval/py/tool-bridge.ts +34 -18
  212. package/src/extensibility/extensions/runner.ts +4 -0
  213. package/src/extensibility/extensions/types.ts +42 -4
  214. package/src/extensibility/plugins/marketplace/fetcher.ts +191 -7
  215. package/src/extensibility/shared-events.ts +5 -0
  216. package/src/extensibility/skc-plugins/runtime-adapters.ts +2 -1
  217. package/src/hooks/native-skill-hook.ts +17 -4
  218. package/src/internal-urls/docs-index.generated.ts +17 -16
  219. package/src/internal-urls/local-protocol.ts +87 -25
  220. package/src/internal-urls/local-root-gc.ts +127 -0
  221. package/src/internal-urls/mcp-protocol.ts +73 -11
  222. package/src/main.ts +120 -42
  223. package/src/memories/index.ts +10 -0
  224. package/src/modes/DESIGN.md +52 -0
  225. package/src/modes/acp/acp-agent.ts +647 -83
  226. package/src/modes/acp/acp-event-mapper.ts +106 -12
  227. package/src/modes/acp/acp-mode.ts +39 -3
  228. package/src/modes/action-registry.ts +1 -1
  229. package/src/modes/components/assistant-message.ts +123 -22
  230. package/src/modes/components/btw-panel.ts +82 -16
  231. package/src/modes/components/irc-sidebar.ts +176 -62
  232. package/src/modes/components/model-selector.ts +61 -4
  233. package/src/modes/components/notifications-settings-editor.ts +1 -1
  234. package/src/modes/components/oauth-selector.ts +42 -12
  235. package/src/modes/components/plan-preview-overlay.ts +1 -1
  236. package/src/modes/components/queue-pane.ts +55 -9
  237. package/src/modes/components/queued-message-selector.ts +53 -10
  238. package/src/modes/components/read-tool-group.ts +82 -14
  239. package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
  240. package/src/modes/components/sayknow-pet-widget.ts +20 -8
  241. package/src/modes/components/session-observer-overlay.ts +187 -32
  242. package/src/modes/components/settings-selector.ts +60 -18
  243. package/src/modes/components/tool-execution.ts +138 -27
  244. package/src/modes/components/tool-status-header.ts +26 -8
  245. package/src/modes/components/welcome.ts +50 -23
  246. package/src/modes/controllers/btw-controller.ts +119 -36
  247. package/src/modes/controllers/command-controller.ts +101 -54
  248. package/src/modes/controllers/event-controller.ts +119 -27
  249. package/src/modes/controllers/extension-ui-controller.ts +23 -12
  250. package/src/modes/controllers/input-controller.ts +100 -8
  251. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
  252. package/src/modes/controllers/selector-controller.ts +4 -1
  253. package/src/modes/interactive-mode.ts +201 -60
  254. package/src/modes/irc-observation-ledger.ts +71 -15
  255. package/src/modes/prompt-action-autocomplete.ts +32 -13
  256. package/src/modes/prompt-suggestion-controller.ts +96 -0
  257. package/src/modes/runtime-init.ts +20 -2
  258. package/src/modes/shared/agent-wire/command-dispatch.ts +3 -3
  259. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
  260. package/src/modes/types.ts +6 -0
  261. package/src/modes/utils/hotkeys-markdown.ts +64 -36
  262. package/src/modes/utils/ui-helpers.ts +99 -4
  263. package/src/prompts/agent-fragments/restricted-bash.md +1 -1
  264. package/src/prompts/agents/architect.md +17 -3
  265. package/src/prompts/agents/critic.md +17 -2
  266. package/src/prompts/agents/executor.md +1 -1
  267. package/src/prompts/agents/planner.md +8 -1
  268. package/src/prompts/system/btw-user.md +3 -8
  269. package/src/prompts/system/prompt-suggestion-system.md +32 -0
  270. package/src/prompts/system/system-prompt.md +6 -3
  271. package/src/prompts/tools/cron.md +3 -1
  272. package/src/prompts/tools/read.md +4 -2
  273. package/src/prompts/tools/skill-discovery.md +1 -0
  274. package/src/runtime/memory-domain.ts +66 -0
  275. package/src/runtime/memory-guard-contract.ts +73 -0
  276. package/src/runtime/memory-guard.ts +292 -0
  277. package/src/runtime/memory-limit.ts +50 -0
  278. package/src/runtime-mcp/client.ts +41 -55
  279. package/src/runtime-mcp/content-limits.ts +71 -0
  280. package/src/runtime-mcp/discoverable-tool-metadata.ts +1 -1
  281. package/src/runtime-mcp/json-rpc.ts +28 -11
  282. package/src/runtime-mcp/manager.ts +77 -59
  283. package/src/runtime-mcp/oauth-flow.ts +0 -72
  284. package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
  285. package/src/runtime-mcp/redaction.ts +30 -0
  286. package/src/runtime-mcp/smithery-auth.ts +17 -2
  287. package/src/runtime-mcp/smithery-connect.ts +11 -1
  288. package/src/runtime-mcp/transports/http.ts +63 -22
  289. package/src/sdk/acp/adapter.ts +8 -1
  290. package/src/sdk/acp/final-text.ts +40 -0
  291. package/src/sdk/acp/mcp.ts +26 -0
  292. package/src/sdk/broker/broker.ts +34 -20
  293. package/src/sdk/broker/identity.ts +22 -12
  294. package/src/sdk/broker/lifecycle.ts +293 -43
  295. package/src/sdk/broker/session-index.ts +11 -1
  296. package/src/sdk/bus/chat-daemon-cli.ts +2 -2
  297. package/src/sdk/bus/chat-daemon-control.ts +22 -5
  298. package/src/sdk/bus/config-commands.ts +2 -2
  299. package/src/sdk/bus/config.ts +10 -2
  300. package/src/sdk/bus/control-drain-lease.ts +104 -0
  301. package/src/sdk/bus/conversation-store.ts +40 -6
  302. package/src/sdk/bus/daemon-paths.ts +2 -0
  303. package/src/sdk/bus/index.ts +793 -121
  304. package/src/sdk/bus/kind-aware-reconciliation.ts +242 -0
  305. package/src/sdk/bus/lifecycle-commands.ts +63 -10
  306. package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
  307. package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
  308. package/src/sdk/bus/notification-orchestration.ts +12 -0
  309. package/src/sdk/bus/notification-service.ts +87 -25
  310. package/src/sdk/bus/operator-runtime.ts +31 -5
  311. package/src/sdk/bus/prompt-reconciliation.ts +240 -0
  312. package/src/sdk/bus/rate-limit-pool.ts +8 -0
  313. package/src/sdk/bus/recent-activity.ts +90 -26
  314. package/src/sdk/bus/reconciliation-store.ts +224 -0
  315. package/src/sdk/bus/telegram-cli.ts +6 -13
  316. package/src/sdk/bus/telegram-daemon-cli.ts +8 -3
  317. package/src/sdk/bus/telegram-daemon-contract.ts +34 -9
  318. package/src/sdk/bus/telegram-daemon-control.ts +161 -40
  319. package/src/sdk/bus/telegram-daemon.ts +3649 -593
  320. package/src/sdk/bus/telegram-reference.ts +20 -2
  321. package/src/sdk/bus/topic-registry.ts +451 -12
  322. package/src/sdk/cli/session-cli.ts +15 -2
  323. package/src/sdk/client/discovery.ts +19 -3
  324. package/src/sdk/client/liveness.ts +49 -0
  325. package/src/sdk/host/control/dispatch.ts +8 -2
  326. package/src/sdk/host/control/operations.ts +3 -2
  327. package/src/sdk/host/host.ts +27 -9
  328. package/src/sdk/host/query/handlers.ts +125 -5
  329. package/src/sdk/index.ts +12 -0
  330. package/src/sdk/lifecycle-session.ts +19 -3
  331. package/src/sdk/mcp/server.ts +46 -18
  332. package/src/sdk/prompt-status.ts +97 -0
  333. package/src/sdk/protocol/operation-inventory.generated.json +145 -0
  334. package/src/sdk/protocol/operation-registry.ts +24 -4
  335. package/src/sdk/session-directory.ts +5 -1
  336. package/src/sdk/session.ts +69 -8
  337. package/src/sdk/startup-capability.ts +15 -1
  338. package/src/sdk/transport/auth-preface.ts +82 -0
  339. package/src/sdk/transport/index.ts +18 -0
  340. package/src/sdk/transport/relay.ts +252 -0
  341. package/src/sdk/transport/serve-cli.ts +105 -0
  342. package/src/sdk/transport/socket.ts +197 -0
  343. package/src/sdk/transport/stdio.ts +17 -0
  344. package/src/session/agent-session.ts +1112 -314
  345. package/src/session/blob-store.ts +25 -7
  346. package/src/session/btw-contract.ts +59 -0
  347. package/src/session/fallback-chain-controller.ts +47 -8
  348. package/src/session/internal/managed-session-scope.ts +1426 -259
  349. package/src/session/internal/managed-session-storage.ts +414 -238
  350. package/src/session/internal/native-publish-outcome.ts +271 -0
  351. package/src/session/memory-guard-checkpoint-participant.ts +131 -0
  352. package/src/session/session-manager.ts +2079 -469
  353. package/src/session/session-storage.ts +151 -11
  354. package/src/session/streaming-output.ts +262 -43
  355. package/src/setup/credential-import.ts +28 -3
  356. package/src/setup/provider-onboarding.ts +49 -5
  357. package/src/setup/provider-presets.json +2 -1
  358. package/src/skc-runtime/deep-interview-runtime.ts +303 -36
  359. package/src/skc-runtime/deep-interview-stage.ts +1016 -0
  360. package/src/skc-runtime/deep-interview-state.ts +8 -5
  361. package/src/skc-runtime/gc-render.ts +1 -0
  362. package/src/skc-runtime/gc-runtime.ts +11 -1
  363. package/src/skc-runtime/linux-proc.ts +70 -36
  364. package/src/skc-runtime/managed-owner-supervisor.ts +83 -4
  365. package/src/skc-runtime/memory-guard-owner-claims.ts +389 -0
  366. package/src/skc-runtime/ralplan-runtime.ts +1057 -75
  367. package/src/skc-runtime/repository-binding.ts +267 -0
  368. package/src/skc-runtime/session-state-sidecar.ts +25 -9
  369. package/src/skc-runtime/state-runtime.ts +50 -12
  370. package/src/skc-runtime/state-writer.ts +68 -56
  371. package/src/skc-runtime/team-launch.ts +34 -5
  372. package/src/skc-runtime/team-runtime.ts +41 -1
  373. package/src/skc-runtime/team-store.ts +32 -0
  374. package/src/skc-runtime/team-worker-memory-guard.ts +363 -0
  375. package/src/skc-runtime/team-workers.ts +1 -0
  376. package/src/skc-runtime/tmux-common.ts +13 -2
  377. package/src/skc-runtime/tmux-owner-isolation.ts +20 -1
  378. package/src/skc-runtime/tmux-sessions.ts +45 -6
  379. package/src/skc-runtime/ultragoal-guard.ts +93 -15
  380. package/src/skc-runtime/ultragoal-receipt-freshness.ts +123 -0
  381. package/src/skc-runtime/ultragoal-runtime.ts +469 -38
  382. package/src/skc-runtime/workflow-manifest.generated.json +195 -5
  383. package/src/skc-runtime/workflow-manifest.ts +67 -3
  384. package/src/skill-state/workflow-hud.ts +25 -2
  385. package/src/skill-state/workflow-mutation-guard.ts +104 -12
  386. package/src/slash-commands/builtin-registry.ts +1 -1
  387. package/src/ssh/utils.ts +14 -0
  388. package/src/task/discovery.ts +8 -2
  389. package/src/task/executor.ts +165 -47
  390. package/src/task/index.ts +123 -15
  391. package/src/task/provider-retry-status.ts +88 -0
  392. package/src/task/receipt.ts +3 -0
  393. package/src/task/render.ts +45 -26
  394. package/src/task/skc-command.ts +2 -2
  395. package/src/task/types.ts +55 -10
  396. package/src/task/ultragoal-redteam-activation.ts +75 -0
  397. package/src/tools/ask.ts +1 -1
  398. package/src/tools/ast-edit.ts +2 -2
  399. package/src/tools/bash-allowed-prefixes.ts +45 -1
  400. package/src/tools/bash-pty-selection.ts +2 -2
  401. package/src/tools/browser/launch.ts +41 -6
  402. package/src/tools/browser/screenshot-format.ts +6 -1
  403. package/src/tools/cron.ts +1 -1
  404. package/src/tools/fetch.ts +127 -154
  405. package/src/tools/image-gen.ts +476 -23
  406. package/src/tools/index.ts +7 -1
  407. package/src/tools/output-meta.ts +176 -9
  408. package/src/tools/path-utils.ts +5 -1
  409. package/src/tools/read-internals.ts +44 -0
  410. package/src/tools/read.ts +1544 -310
  411. package/src/tools/resource-gc.ts +665 -125
  412. package/src/tools/skill-discovery.ts +39 -3
  413. package/src/tools/sqlite-reader.ts +63 -5
  414. package/src/tools/subagent-render.ts +74 -16
  415. package/src/tools/subagent.ts +2 -0
  416. package/src/tools/tool-result.ts +14 -1
  417. package/src/utils/edit-mode.ts +2 -2
  418. package/src/utils/pasted-image-path.ts +15 -6
  419. package/src/utils/prompt-suggestion.ts +265 -0
  420. package/src/utils/shell-snapshot.ts +173 -63
  421. package/src/utils/sixel.ts +3 -3
  422. package/src/web/insane/bridge.ts +5 -1
  423. package/src/web/insane/url-guard.ts +140 -27
  424. package/src/web/scrapers/docs-rs.ts +30 -5
  425. package/src/web/scrapers/types.ts +19 -49
  426. package/src/web/scrapers/utils.ts +12 -26
  427. package/src/web/search/providers/codex.ts +1 -1
  428. package/src/workflow/workflow-intent-diff.ts +4 -1
@@ -0,0 +1,292 @@
1
+ import type { Settings } from "../config/settings";
2
+ import type {
3
+ MemoryGuardActionTarget,
4
+ MemoryGuardDecision,
5
+ MemoryGuardDomainSnapshot,
6
+ MemoryGuardPolicy,
7
+ MemoryGuardSchedulerState,
8
+ } from "./memory-guard-contract";
9
+
10
+ const BYTES_PER_MB = 1024 * 1024;
11
+ const DEFAULT_CHECK_INTERVAL_MS = 30_000;
12
+
13
+ type ScheduleOwner = { generation: number; token: number };
14
+ type DeferredSchedule = { generation: number; deadline: number };
15
+ type WorkOwner = { generation: number; source: "timer" | "external" };
16
+
17
+ export interface MemoryGuardHostRegistration {
18
+ ownerId: string;
19
+ intervalMs: number;
20
+ }
21
+
22
+ export interface MemoryGuardHostOptions {
23
+ run: () => Promise<void>;
24
+ logDebug?: (message: string, meta?: Record<string, unknown>) => void;
25
+ schedulerNow?: () => number;
26
+ }
27
+
28
+ function normalizePositiveIntervalMs(intervalMs: number): number {
29
+ if (!Number.isFinite(intervalMs) || intervalMs <= 0) throw new Error("invalid_interval_ms");
30
+ return intervalMs;
31
+ }
32
+
33
+ function toRatio(percent: number): number {
34
+ return percent / 100;
35
+ }
36
+
37
+ function mbToBytes(value: number): number {
38
+ return Math.round(value * BYTES_PER_MB);
39
+ }
40
+
41
+ function sortTargets(left: MemoryGuardActionTarget, right: MemoryGuardActionTarget): number {
42
+ if (left.excessBytes !== right.excessBytes) return right.excessBytes - left.excessBytes;
43
+ if (left.kind !== right.kind) return left.kind === "host" ? -1 : 1;
44
+ if (left.kind === "worker" && right.kind === "worker") return left.workerId.localeCompare(right.workerId);
45
+ return 0;
46
+ }
47
+
48
+ export function resolveMemoryGuardPolicy(settings: Settings): MemoryGuardPolicy {
49
+ const configuredPolicyLimitMb = settings.get("memoryGuard.policyLimitMb");
50
+ return {
51
+ enabled: settings.get("memoryGuard.enabled"),
52
+ checkIntervalMs: settings.get("memoryGuard.checkIntervalMs"),
53
+ gcThresholdRatio: toRatio(settings.get("memoryGuard.gcThresholdPercent")),
54
+ restartThresholdRatio: toRatio(settings.get("memoryGuard.restartThresholdPercent")),
55
+ restartThresholdWindowMs: settings.get("memoryGuard.restartThresholdWindowMs"),
56
+ cooldownMs: settings.get("memoryGuard.cooldownMs"),
57
+ parentReserveBytes: mbToBytes(settings.get("memoryGuard.parentReserveMb")),
58
+ policyLimitBytes: configuredPolicyLimitMb > 0 ? mbToBytes(configuredPolicyLimitMb) : null,
59
+ };
60
+ }
61
+
62
+ export function chooseMemoryGuardAction(input: {
63
+ domain: MemoryGuardDomainSnapshot;
64
+ hostSupported: boolean;
65
+ workerSupported: (workerId: string) => boolean;
66
+ }): MemoryGuardDecision {
67
+ const candidates: MemoryGuardActionTarget[] = [];
68
+ if (input.hostSupported && input.domain.hostExcessBytes > 0) {
69
+ candidates.push({ kind: "host", excessBytes: input.domain.hostExcessBytes });
70
+ }
71
+ for (const worker of input.domain.workers) {
72
+ if (!worker.accepted || worker.excessBytes <= 0 || !input.workerSupported(worker.workerId)) continue;
73
+ candidates.push({ kind: "worker", workerId: worker.workerId, excessBytes: worker.excessBytes });
74
+ }
75
+ if (candidates.length > 0) {
76
+ candidates.sort(sortTargets);
77
+ return { kind: "execute", target: candidates[0]! };
78
+ }
79
+ const unsupportedExcessExists =
80
+ input.domain.hostExcessBytes > 0 ||
81
+ input.domain.workers.some(worker => worker.accepted && worker.excessBytes > 0);
82
+ if (unsupportedExcessExists) return { kind: "noop", reason: "memory_guard_action_noop_unsupported" };
83
+ return { kind: "noop", reason: "memory_guard_action_noop_unmanaged_or_within_allowance" };
84
+ }
85
+
86
+ export function revalidateMemoryGuardAction(
87
+ previous: Extract<MemoryGuardDecision, { kind: "execute" }>,
88
+ revalidated: MemoryGuardDecision,
89
+ ): MemoryGuardDecision {
90
+ if (revalidated.kind !== "execute")
91
+ return { kind: "revalidated_out", reason: "memory_guard_action_revalidated_out" };
92
+ if (previous.target.kind !== revalidated.target.kind)
93
+ return { kind: "revalidated_out", reason: "memory_guard_action_revalidated_out" };
94
+ if (
95
+ previous.target.kind === "worker" &&
96
+ revalidated.target.kind === "worker" &&
97
+ previous.target.workerId !== revalidated.target.workerId
98
+ ) {
99
+ return { kind: "revalidated_out", reason: "memory_guard_action_revalidated_out" };
100
+ }
101
+ return revalidated;
102
+ }
103
+
104
+ export class MemoryGuardHost {
105
+ #run: () => Promise<void>;
106
+ #logDebug: (message: string, meta?: Record<string, unknown>) => void;
107
+ #defaultSchedulerNow: () => number;
108
+ #schedulerNow: () => number;
109
+ #registrations = new Map<string, { intervalMs: number; nextDueMs: number }>();
110
+ #pendingTimer: NodeJS.Timeout | null = null;
111
+ #pendingDeadline: number | null = null;
112
+ #pendingOwner: ScheduleOwner | null = null;
113
+ #deferredSchedule: DeferredSchedule | null = null;
114
+ #inProgressOwner: WorkOwner | null = null;
115
+ #stopped = false;
116
+ #generation = 0;
117
+ #nextTimerToken = 0;
118
+
119
+ constructor(options: MemoryGuardHostOptions) {
120
+ this.#run = options.run;
121
+ this.#logDebug = options.logDebug ?? (() => undefined);
122
+ this.#defaultSchedulerNow = options.schedulerNow ?? (() => performance.now());
123
+ this.#schedulerNow = this.#defaultSchedulerNow;
124
+ }
125
+
126
+ register(registration: MemoryGuardHostRegistration): () => void {
127
+ const intervalMs = normalizePositiveIntervalMs(registration.intervalMs);
128
+ const now = this.#schedulerNow();
129
+ const existing = this.#registrations.get(registration.ownerId);
130
+ const isNewRegistration = !existing;
131
+ const nextDueMs = existing ? Math.min(existing.nextDueMs, now + intervalMs) : now + intervalMs;
132
+ this.#registrations.set(registration.ownerId, { intervalMs, nextDueMs });
133
+ this.#stopped = false;
134
+ if (isNewRegistration) {
135
+ const deadline = nextDueMs;
136
+ if (
137
+ this.#inProgressOwner?.generation === this.#generation &&
138
+ (this.#pendingDeadline === null || this.#pendingDeadline <= deadline)
139
+ ) {
140
+ this.#deferSchedule(this.#generation, deadline);
141
+ } else {
142
+ this.#requestSchedule(deadline);
143
+ }
144
+ }
145
+ let unregistered = false;
146
+ return () => {
147
+ if (unregistered) return;
148
+ unregistered = true;
149
+ this.#registrations.delete(registration.ownerId);
150
+ if (this.#registrations.size === 0) this.#stop();
151
+ };
152
+ }
153
+
154
+ updateInterval(ownerId: string, intervalMs: number): void {
155
+ const existing = this.#registrations.get(ownerId);
156
+ if (!existing) return;
157
+ const normalized = normalizePositiveIntervalMs(intervalMs);
158
+ if (existing.intervalMs === normalized) return;
159
+ const now = this.#schedulerNow();
160
+ const nextDueMs = now + normalized;
161
+ this.#registrations.set(ownerId, { intervalMs: normalized, nextDueMs });
162
+ if (this.#inProgressOwner) return;
163
+ const nextDeadline = this.#nextDeadlineMs();
164
+ if (this.#pendingDeadline !== null && nextDeadline > this.#pendingDeadline) {
165
+ this.#clearPendingSchedule();
166
+ }
167
+ this.#requestSchedule(nextDeadline);
168
+ }
169
+
170
+ async runTick(generation = this.#generation, source: WorkOwner["source"] = "external"): Promise<void> {
171
+ if (this.#inProgressOwner || this.#registrations.size === 0) return;
172
+ const owner: WorkOwner = { generation, source };
173
+ this.#inProgressOwner = owner;
174
+ try {
175
+ await this.#run();
176
+ } catch (error) {
177
+ this.#logDebug("memory guard tick failed", { error: error instanceof Error ? error.message : String(error) });
178
+ } finally {
179
+ if (this.#inProgressOwner === owner) this.#inProgressOwner = null;
180
+ this.#advanceDueTimes();
181
+ this.#reconcileCurrentSchedule();
182
+ }
183
+ }
184
+
185
+ async runTimerCallbackForTest(owner: { generation: number; token: number }, deadline: number): Promise<void> {
186
+ await this.#handleTimerCallback(owner, deadline);
187
+ }
188
+
189
+ setSchedulerNowForTest(now: () => number): void {
190
+ this.#schedulerNow = now;
191
+ }
192
+
193
+ getStateForTest(): MemoryGuardSchedulerState {
194
+ return {
195
+ timerActive: this.#pendingTimer !== null,
196
+ registrationCount: this.#registrations.size,
197
+ inProgress: this.#inProgressOwner !== null,
198
+ generation: this.#generation,
199
+ pendingDeadline: this.#pendingDeadline,
200
+ pendingOwner: this.#pendingOwner ? { ...this.#pendingOwner } : null,
201
+ deferredDeadline: this.#deferredSchedule?.deadline ?? null,
202
+ deferredGeneration: this.#deferredSchedule?.generation ?? null,
203
+ activeGeneration: this.#inProgressOwner?.generation ?? null,
204
+ };
205
+ }
206
+
207
+ resetForTest(): void {
208
+ this.#stop();
209
+ this.#registrations.clear();
210
+ this.#inProgressOwner = null;
211
+ this.#deferredSchedule = null;
212
+ this.#nextTimerToken = 0;
213
+ this.#schedulerNow = this.#defaultSchedulerNow;
214
+ }
215
+
216
+ #nextDeadlineMs(): number {
217
+ let minDue = Number.POSITIVE_INFINITY;
218
+ for (const reg of this.#registrations.values()) {
219
+ minDue = Math.min(minDue, reg.nextDueMs);
220
+ }
221
+ return Number.isFinite(minDue) ? minDue : this.#schedulerNow() + DEFAULT_CHECK_INTERVAL_MS;
222
+ }
223
+
224
+ #advanceDueTimes(): void {
225
+ const now = this.#schedulerNow();
226
+ for (const reg of this.#registrations.values()) {
227
+ if (reg.nextDueMs <= now) {
228
+ reg.nextDueMs = now + reg.intervalMs;
229
+ }
230
+ }
231
+ }
232
+
233
+ #clearPendingSchedule(): void {
234
+ if (this.#pendingTimer) clearTimeout(this.#pendingTimer);
235
+ this.#pendingTimer = null;
236
+ this.#pendingDeadline = null;
237
+ this.#pendingOwner = null;
238
+ }
239
+
240
+ #requestSchedule(deadline: number): void {
241
+ if (this.#stopped || this.#registrations.size === 0) return;
242
+ if (this.#pendingDeadline !== null && this.#pendingDeadline <= deadline) return;
243
+ this.#clearPendingSchedule();
244
+ const owner = { generation: this.#generation, token: ++this.#nextTimerToken };
245
+ this.#pendingDeadline = deadline;
246
+ this.#pendingOwner = owner;
247
+ this.#pendingTimer = setTimeout(
248
+ () => {
249
+ void this.#handleTimerCallback(owner, deadline);
250
+ },
251
+ Math.max(0, deadline - this.#schedulerNow()),
252
+ );
253
+ this.#pendingTimer.unref?.();
254
+ }
255
+
256
+ #deferSchedule(generation: number, deadline: number): void {
257
+ if (generation !== this.#generation) return;
258
+ if (this.#deferredSchedule?.generation === generation) {
259
+ this.#deferredSchedule.deadline = Math.min(this.#deferredSchedule.deadline, deadline);
260
+ return;
261
+ }
262
+ this.#deferredSchedule = { generation, deadline };
263
+ }
264
+
265
+ async #handleTimerCallback(owner: ScheduleOwner, deadline: number): Promise<void> {
266
+ if (this.#pendingOwner?.generation !== owner.generation || this.#pendingOwner.token !== owner.token) return;
267
+ this.#pendingTimer = null;
268
+ this.#pendingDeadline = null;
269
+ this.#pendingOwner = null;
270
+ if (this.#inProgressOwner) {
271
+ this.#deferSchedule(owner.generation, deadline);
272
+ return;
273
+ }
274
+ await this.runTick(owner.generation, "timer");
275
+ }
276
+
277
+ #reconcileCurrentSchedule(): void {
278
+ if (this.#stopped || this.#registrations.size === 0 || this.#inProgressOwner) return;
279
+ const normalDeadline = this.#nextDeadlineMs();
280
+ const deferredDeadline =
281
+ this.#deferredSchedule?.generation === this.#generation ? this.#deferredSchedule.deadline : null;
282
+ if (deferredDeadline !== null) this.#deferredSchedule = null;
283
+ this.#requestSchedule(deferredDeadline === null ? normalDeadline : Math.min(deferredDeadline, normalDeadline));
284
+ }
285
+
286
+ #stop(): void {
287
+ this.#stopped = true;
288
+ this.#generation++;
289
+ this.#clearPendingSchedule();
290
+ this.#deferredSchedule = null;
291
+ }
292
+ }
@@ -0,0 +1,50 @@
1
+ export interface EffectiveMemoryLimitInput {
2
+ hardCapBytes?: number | null;
3
+ policyLimitBytes?: number | null;
4
+ }
5
+
6
+ export interface EffectiveMemoryLimit {
7
+ hardCapBytes: number | null;
8
+ policyLimitBytes: number | null;
9
+ effectiveBytes: number | null;
10
+ source: "none" | "hard_cap" | "policy_limit" | "hard_cap_and_policy_limit";
11
+ }
12
+
13
+ function normalizePositiveByteCount(value: number | null | undefined): number | null {
14
+ return Number.isSafeInteger(value) && typeof value === "number" && value > 0 ? value : null;
15
+ }
16
+
17
+ export function resolveEffectiveMemoryLimit(input: EffectiveMemoryLimitInput): EffectiveMemoryLimit {
18
+ const hardCapBytes = normalizePositiveByteCount(input.hardCapBytes);
19
+ const policyLimitBytes = normalizePositiveByteCount(input.policyLimitBytes);
20
+ if (hardCapBytes !== null && policyLimitBytes !== null) {
21
+ return {
22
+ hardCapBytes,
23
+ policyLimitBytes,
24
+ effectiveBytes: Math.min(hardCapBytes, policyLimitBytes),
25
+ source: "hard_cap_and_policy_limit",
26
+ };
27
+ }
28
+ if (hardCapBytes !== null) {
29
+ return {
30
+ hardCapBytes,
31
+ policyLimitBytes: null,
32
+ effectiveBytes: hardCapBytes,
33
+ source: "hard_cap",
34
+ };
35
+ }
36
+ if (policyLimitBytes !== null) {
37
+ return {
38
+ hardCapBytes: null,
39
+ policyLimitBytes,
40
+ effectiveBytes: policyLimitBytes,
41
+ source: "policy_limit",
42
+ };
43
+ }
44
+ return {
45
+ hardCapBytes: null,
46
+ policyLimitBytes: null,
47
+ effectiveBytes: null,
48
+ source: "none",
49
+ };
50
+ }
@@ -15,15 +15,12 @@ import type {
15
15
  MCPInitializeParams,
16
16
  MCPInitializeResult,
17
17
  MCPPrompt,
18
- MCPPromptsListResult,
19
18
  MCPRequestOptions,
20
19
  MCPResource,
21
20
  MCPResourceReadParams,
22
21
  MCPResourceReadResult,
23
22
  MCPResourceSubscribeParams,
24
- MCPResourcesListResult,
25
23
  MCPResourceTemplate,
26
- MCPResourceTemplatesListResult,
27
24
  MCPServerCapabilities,
28
25
  MCPServerConfig,
29
26
  MCPServerConnection,
@@ -43,6 +40,9 @@ const PROTOCOL_VERSION = "2025-03-26";
43
40
  /** Default connection timeout in ms */
44
41
  const CONNECTION_TIMEOUT_MS = 30_000;
45
42
 
43
+ const MAX_PAGINATION_PAGES = 100,
44
+ MAX_PAGINATION_ITEMS = 10_000;
45
+
46
46
  /** Client info sent during initialization */
47
47
  const CLIENT_INFO = {
48
48
  name: "skc-coding-agent",
@@ -109,6 +109,40 @@ function decodeToolsListResult(value: unknown): MCPToolsListResult {
109
109
  return value.nextCursor === undefined ? { tools } : { tools, nextCursor: value.nextCursor };
110
110
  }
111
111
 
112
+ async function collectPaginated<T>(
113
+ connection: MCPServerConnection,
114
+ options: MCPRequestOptions | undefined,
115
+ method: string,
116
+ itemKey: string,
117
+ items: T[],
118
+ decode?: (value: unknown) => unknown,
119
+ ): Promise<void> {
120
+ const seenCursors = new Set<string>();
121
+ const failure = (detail: string) => new MCPExpectedFailure(new Error(`MCP ${method} pagination ${detail}`));
122
+ let cursor: string | undefined;
123
+ for (let page = 1; page <= MAX_PAGINATION_PAGES; page++) {
124
+ const value = await connection.transport.request<unknown>(method, cursor ? { cursor } : {}, options);
125
+ const result = decode ? decode(value) : value;
126
+ if (
127
+ !isRecord(result) ||
128
+ !Array.isArray(result[itemKey]) ||
129
+ (result.nextCursor !== undefined && typeof result.nextCursor !== "string")
130
+ )
131
+ throw new MCPExpectedFailure();
132
+ const nextCursor = result.nextCursor as string | undefined;
133
+ if (nextCursor && seenCursors.has(nextCursor)) throw failure("repeated a cursor");
134
+ const pageItems = result[itemKey] as T[];
135
+ const itemCount = items.length + pageItems.length;
136
+ if (itemCount > MAX_PAGINATION_ITEMS || (itemCount === MAX_PAGINATION_ITEMS && nextCursor))
137
+ throw failure("did not complete within the 10000-item budget");
138
+ items.push(...pageItems);
139
+ if (!nextCursor) return;
140
+ if (page === MAX_PAGINATION_PAGES) throw failure("did not complete within the 100-page budget");
141
+ seenCursors.add(nextCursor);
142
+ cursor = nextCursor;
143
+ }
144
+ }
145
+
112
146
  /**
113
147
  * Default handler for standard MCP server-to-client requests.
114
148
  * Handles `ping` and `roots/list`; rejects unknown methods with -32601.
@@ -292,18 +326,7 @@ export async function listTools(
292
326
  }
293
327
 
294
328
  const allTools: MCPToolDefinition[] = [];
295
- let cursor: string | undefined;
296
-
297
- do {
298
- const params: Record<string, unknown> = {};
299
- if (cursor) {
300
- params.cursor = cursor;
301
- }
302
-
303
- const result = decodeToolsListResult(await connection.transport.request<unknown>("tools/list", params, options));
304
- allTools.push(...result.tools);
305
- cursor = result.nextCursor;
306
- } while (cursor);
329
+ await collectPaginated(connection, options, "tools/list", "tools", allTools, decodeToolsListResult);
307
330
 
308
331
  // Cache tools
309
332
  connection.tools = allTools;
@@ -362,18 +385,7 @@ export async function listResources(
362
385
  }
363
386
 
364
387
  const allResources: MCPResource[] = [];
365
- let cursor: string | undefined;
366
-
367
- do {
368
- const params: Record<string, unknown> = {};
369
- if (cursor) {
370
- params.cursor = cursor;
371
- }
372
-
373
- const result = await connection.transport.request<MCPResourcesListResult>("resources/list", params, options);
374
- allResources.push(...result.resources);
375
- cursor = result.nextCursor;
376
- } while (cursor);
388
+ await collectPaginated(connection, options, "resources/list", "resources", allResources);
377
389
 
378
390
  connection.resources = allResources;
379
391
  return allResources;
@@ -395,22 +407,7 @@ export async function listResourceTemplates(
395
407
  }
396
408
 
397
409
  const allTemplates: MCPResourceTemplate[] = [];
398
- let cursor: string | undefined;
399
-
400
- do {
401
- const params: Record<string, unknown> = {};
402
- if (cursor) {
403
- params.cursor = cursor;
404
- }
405
-
406
- const result = await connection.transport.request<MCPResourceTemplatesListResult>(
407
- "resources/templates/list",
408
- params,
409
- options,
410
- );
411
- allTemplates.push(...result.resourceTemplates);
412
- cursor = result.nextCursor;
413
- } while (cursor);
410
+ await collectPaginated(connection, options, "resources/templates/list", "resourceTemplates", allTemplates);
414
411
 
415
412
  connection.resourceTemplates = allTemplates;
416
413
  return allTemplates;
@@ -514,18 +511,7 @@ export async function listPrompts(
514
511
  }
515
512
 
516
513
  const allPrompts: MCPPrompt[] = [];
517
- let cursor: string | undefined;
518
-
519
- do {
520
- const params: Record<string, unknown> = {};
521
- if (cursor) {
522
- params.cursor = cursor;
523
- }
524
-
525
- const result = await connection.transport.request<MCPPromptsListResult>("prompts/list", params, options);
526
- allPrompts.push(...result.prompts);
527
- cursor = result.nextCursor;
528
- } while (cursor);
514
+ await collectPaginated(connection, options, "prompts/list", "prompts", allPrompts);
529
515
 
530
516
  connection.prompts = allPrompts;
531
517
  return allPrompts;
@@ -0,0 +1,71 @@
1
+ export const MCP_MAX_CONTENT_BYTES = 16 * 1024 * 1024;
2
+ export const MCP_MAX_ERROR_BYTES = 16 * 1024;
3
+ export const MCP_MAX_SSE_REQUEST_MESSAGES = 1024;
4
+ export const MCP_MAX_SSE_BATCH_MESSAGES = 1024;
5
+ export const MCP_HTTP_TIMEOUT_MS = 30_000;
6
+
7
+ function ignoreCancellation(cancel: () => Promise<unknown>): void {
8
+ try {
9
+ void cancel().catch(() => {});
10
+ } catch {}
11
+ }
12
+
13
+ export function cancelMCPStream(stream: ReadableStream<Uint8Array> | null): void {
14
+ if (stream) ignoreCancellation(() => stream.cancel());
15
+ }
16
+
17
+ type ByteReader = Pick<ReadableStreamDefaultReader<Uint8Array>, "read">;
18
+
19
+ async function readChunk(reader: ByteReader, signal?: AbortSignal) {
20
+ if (!signal) return reader.read();
21
+ signal.throwIfAborted();
22
+ const aborted = Promise.withResolvers<never>();
23
+ const onAbort = () => aborted.reject(signal.reason);
24
+ signal.addEventListener("abort", onAbort, { once: true });
25
+ try {
26
+ return await Promise.race([reader.read(), aborted.promise]);
27
+ } finally {
28
+ signal.removeEventListener("abort", onAbort);
29
+ }
30
+ }
31
+
32
+ export async function readMCPResponseText(response: Response, limit: number, truncate = false, signal?: AbortSignal) {
33
+ const declared = response.headers.get("Content-Length");
34
+ if (!truncate && declared && /^\d+$/.test(declared) && Number(declared) > limit) {
35
+ cancelMCPStream(response.body);
36
+ throw new Error("MCP response exceeds size limit");
37
+ }
38
+ if (!response.body) return "";
39
+ const reader = response.body.getReader();
40
+ let buffer = new Uint8Array(Math.min(limit, 64 * 1024));
41
+ let total = 0;
42
+ try {
43
+ while (true) {
44
+ const { value, done } = await readChunk(reader, signal);
45
+ if (done) break;
46
+ if (value.length === 0) continue;
47
+ const remaining = limit - total;
48
+ if (value.length > remaining) {
49
+ if (truncate && remaining > 0) buffer.set(value.subarray(0, remaining), total);
50
+ ignoreCancellation(() => reader.cancel());
51
+ if (!truncate) throw new Error("MCP response exceeds size limit");
52
+ return `${new TextDecoder().decode(buffer)}\u2026`;
53
+ }
54
+ if (total + value.length > buffer.length) {
55
+ const grown = new Uint8Array(Math.min(limit, Math.max(total + value.length, buffer.length * 2)));
56
+ grown.set(buffer);
57
+ buffer = grown;
58
+ }
59
+ buffer.set(value, total);
60
+ total += value.length;
61
+ }
62
+ return new TextDecoder().decode(buffer.subarray(0, total));
63
+ } catch (error) {
64
+ ignoreCancellation(() => reader.cancel());
65
+ throw error;
66
+ } finally {
67
+ try {
68
+ reader.releaseLock();
69
+ } catch {}
70
+ }
71
+ }
@@ -7,10 +7,10 @@
7
7
  * the new symbols under the legacy names so existing call sites still compile.
8
8
  */
9
9
  export type {
10
+ DiscoverableTool as DiscoverableMCPTool,
10
11
  DiscoverableToolSearchDocument as DiscoverableMCPSearchDocument,
11
12
  DiscoverableToolSearchIndex as DiscoverableMCPSearchIndex,
12
13
  DiscoverableToolSearchResult as DiscoverableMCPSearchResult,
13
- DiscoverableTool as DiscoverableMCPTool,
14
14
  DiscoverableToolServerSummary as DiscoverableMCPToolServerSummary,
15
15
  DiscoverableToolSummary as DiscoverableMCPToolSummary,
16
16
  } from "../tool-discovery/tool-index";
@@ -5,6 +5,8 @@
5
5
  * without maintaining persistent connections.
6
6
  */
7
7
  import { logger } from "@sayknow-cli/utils";
8
+ import { cancelMCPStream, MCP_HTTP_TIMEOUT_MS, MCP_MAX_CONTENT_BYTES, readMCPResponseText } from "./content-limits";
9
+ import { redactMCPDiagnosticValue, redactMCPEndpoint } from "./redaction";
8
10
 
9
11
  /** Parse SSE response format (lines starting with "data: ") */
10
12
  export function parseSSE(text: string): unknown {
@@ -25,6 +27,15 @@ export function parseSSE(text: string): unknown {
25
27
  }
26
28
  }
27
29
 
30
+ async function translateMCPTimeout<T>(operation: Promise<T>, signal: AbortSignal): Promise<T> {
31
+ try {
32
+ return await operation;
33
+ } catch (error) {
34
+ if (signal.aborted && error === signal.reason) throw new Error("MCP request timed out");
35
+ throw error;
36
+ }
37
+ }
38
+
28
39
  /** JSON-RPC 2.0 response structure */
29
40
  export interface JsonRpcResponse<T = unknown> {
30
41
  jsonrpc: "2.0";
@@ -57,26 +68,32 @@ export async function callMCP<T = unknown>(
57
68
  params: params ?? {},
58
69
  };
59
70
 
60
- const response = await fetch(url, {
61
- method: "POST",
62
- headers: {
63
- "Content-Type": "application/json",
64
- Accept: "application/json, text/event-stream",
65
- },
66
- body: JSON.stringify(body),
67
- });
71
+ const signal = AbortSignal.timeout(MCP_HTTP_TIMEOUT_MS);
72
+ const response = await translateMCPTimeout(
73
+ fetch(url, {
74
+ method: "POST",
75
+ headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
76
+ body: JSON.stringify(body),
77
+ signal,
78
+ }),
79
+ signal,
80
+ );
68
81
 
69
82
  if (!response.ok) {
83
+ cancelMCPStream(response.body);
70
84
  const errorMsg = `MCP request failed: ${response.status} ${response.statusText}`;
71
- logger.error(errorMsg, { url, method, params });
85
+ logger.error(errorMsg, { url: redactMCPEndpoint(url), method, params: redactMCPDiagnosticValue(params) });
72
86
  throw new Error(errorMsg);
73
87
  }
74
88
 
75
- const text = await response.text();
89
+ const text = await translateMCPTimeout(readMCPResponseText(response, MCP_MAX_CONTENT_BYTES, false, signal), signal);
76
90
  const result = parseSSE(text) as JsonRpcResponse<T> | null;
77
91
 
78
92
  if (!result) {
79
- logger.error("Failed to parse MCP response", { url, method, responseText: text.slice(0, 500) });
93
+ logger.error("Failed to parse MCP response", {
94
+ url: redactMCPEndpoint(url),
95
+ method,
96
+ });
80
97
  throw new Error("Failed to parse MCP response");
81
98
  }
82
99