@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,252 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+
3
+ export const REQUEST_FRAME_BYTES = 256 * 1024;
4
+ export const DEFAULT_PENDING_CEILING_BYTES = 8 * 1024 * 1024;
5
+ export const MIN_PENDING_CEILING_BYTES = REQUEST_FRAME_BYTES;
6
+
7
+ export type RelayDirection = "downstream->ws" | "ws->downstream";
8
+ export type TransportError = {
9
+ type: "transport_error";
10
+ code: "frame_oversize" | "pending_overflow" | "protocol_error";
11
+ direction?: RelayDirection;
12
+ };
13
+
14
+ export type RelayPair = {
15
+ close(): Promise<void>;
16
+ readonly done: Promise<void>;
17
+ };
18
+
19
+ export class RelayOpenAbortedError extends Error {
20
+ constructor() {
21
+ super("relay_open_aborted");
22
+ this.name = "RelayOpenAbortedError";
23
+ }
24
+ }
25
+
26
+ export type RelayWebSocketEvent = { data?: unknown };
27
+
28
+ export type RelayWebSocket = {
29
+ readonly bufferedAmount: number;
30
+ readonly readyState: number;
31
+ close(): void;
32
+ send(data: string): void;
33
+ addEventListener(type: string, listener: (event: RelayWebSocketEvent) => void, options?: { once?: boolean }): void;
34
+ removeEventListener(type: string, listener: (event: RelayWebSocketEvent) => void): void;
35
+ };
36
+
37
+ export type RelayOptions = {
38
+ url: string;
39
+ token: string;
40
+ pendingCeilingBytes: number;
41
+ downstream: Readable;
42
+ downstreamSink: Writable;
43
+ initialDownstreamBytes?: Buffer;
44
+ onTransportError: (error: TransportError) => void;
45
+ webSocketFactory?: (url: string) => RelayWebSocket;
46
+ signal?: AbortSignal;
47
+ };
48
+
49
+ type QueuedFrame = { bytes: Buffer; accounted: boolean };
50
+
51
+ function upstreamUrl(url: string, token: string): string {
52
+ const endpoint = new URL(url);
53
+ endpoint.pathname = `${endpoint.pathname.replace(/\/$/, "")}/`;
54
+ endpoint.searchParams.set("token", token);
55
+ return endpoint.toString();
56
+ }
57
+
58
+ function waitForDrain(stream: Writable): Promise<void> {
59
+ const { promise, reject, resolve } = Promise.withResolvers<void>();
60
+ const onDrain = (): void => done(resolve);
61
+ const onError = (error: Error): void => done(() => reject(error));
62
+ const done = (callback: () => void): void => {
63
+ stream.removeListener("drain", onDrain);
64
+ stream.removeListener("error", onError);
65
+ callback();
66
+ };
67
+ stream.once("drain", onDrain);
68
+ stream.once("error", onError);
69
+ return promise;
70
+ }
71
+
72
+ const WEBSOCKET_CONNECTING = 0;
73
+ const WEBSOCKET_OPEN = 1;
74
+
75
+ function waitForWebSocketDrain(ws: RelayWebSocket, isClosed: () => boolean): Promise<void> {
76
+ const { promise, resolve } = Promise.withResolvers<void>();
77
+ const poll = (): void => {
78
+ if (isClosed() || ws.readyState !== WEBSOCKET_OPEN || ws.bufferedAmount === 0) {
79
+ resolve();
80
+ return;
81
+ }
82
+ setTimeout(poll, 10);
83
+ };
84
+ poll();
85
+ return promise;
86
+ }
87
+
88
+ /** Starts a dedicated raw-WebSocket relay for exactly one downstream stream. */
89
+ export async function startRelayPair(options: RelayOptions): Promise<RelayPair> {
90
+ if (options.signal?.aborted) throw new RelayOpenAbortedError();
91
+ const ws: RelayWebSocket =
92
+ options.webSocketFactory?.(upstreamUrl(options.url, options.token)) ??
93
+ new WebSocket(upstreamUrl(options.url, options.token));
94
+ const opened = Promise.withResolvers<void>();
95
+ const finished = Promise.withResolvers<void>();
96
+ let closed = false;
97
+ let completed = false;
98
+ let downstreamBuffer = Buffer.alloc(0);
99
+ const toWs: QueuedFrame[] = [];
100
+ const toDownstream: QueuedFrame[] = [];
101
+ let pendingToWs = 0;
102
+ let pendingToDownstream = 0;
103
+ let writingWs = false;
104
+ let writingDownstream = false;
105
+
106
+ const settle = (error?: Error): void => {
107
+ if (completed) return;
108
+ completed = true;
109
+ if (error) finished.reject(error);
110
+ else finished.resolve();
111
+ };
112
+ void finished.promise.catch(() => undefined);
113
+ const detach = (): void => {
114
+ options.downstream.removeListener("data", onData);
115
+ options.downstream.removeListener("end", onEnd);
116
+ options.downstream.removeListener("error", onDownstreamError);
117
+ ws.removeEventListener("message", onMessage);
118
+ ws.removeEventListener("close", onClose);
119
+ ws.removeEventListener("error", onWebSocketError);
120
+ cleanupOpening();
121
+ };
122
+ const close = async (error?: Error): Promise<void> => {
123
+ if (closed) return finished.promise.catch(() => undefined);
124
+ closed = true;
125
+ detach();
126
+ options.downstream.pause();
127
+ if (ws.readyState === WEBSOCKET_OPEN || ws.readyState === WEBSOCKET_CONNECTING) ws.close();
128
+ settle(error);
129
+ return await finished.promise.catch(() => undefined);
130
+ };
131
+ const fail = (transportError: TransportError): void => {
132
+ if (closed) return;
133
+ options.onTransportError(transportError);
134
+ void close(new Error(transportError.code));
135
+ };
136
+ const enqueue = (queue: QueuedFrame[], direction: RelayDirection, bytes: Buffer): void => {
137
+ const isWsDirection = direction === "downstream->ws";
138
+ const active = isWsDirection ? writingWs : writingDownstream;
139
+ const pending = isWsDirection ? pendingToWs : pendingToDownstream;
140
+ if (active && pending + bytes.length > options.pendingCeilingBytes) {
141
+ fail({ type: "transport_error", code: "pending_overflow", direction });
142
+ return;
143
+ }
144
+ queue.push({ bytes, accounted: active });
145
+ if (active) {
146
+ if (isWsDirection) pendingToWs += bytes.length;
147
+ else pendingToDownstream += bytes.length;
148
+ }
149
+ if (isWsDirection) void pumpWs();
150
+ else void pumpDownstream();
151
+ };
152
+ const pumpWs = async (): Promise<void> => {
153
+ if (writingWs || closed) return;
154
+ const frame = toWs.shift();
155
+ if (!frame) return;
156
+ writingWs = true;
157
+ if (frame.accounted) pendingToWs -= frame.bytes.length;
158
+ try {
159
+ if (ws.readyState !== WEBSOCKET_OPEN) throw new Error("upstream_closed");
160
+ ws.send(frame.bytes.toString("utf8"));
161
+ await waitForWebSocketDrain(ws, () => closed);
162
+ } catch (error) {
163
+ await close(error instanceof Error ? error : new Error(String(error)));
164
+ return;
165
+ } finally {
166
+ writingWs = false;
167
+ }
168
+ void pumpWs();
169
+ };
170
+ const pumpDownstream = async (): Promise<void> => {
171
+ if (writingDownstream || closed) return;
172
+ const frame = toDownstream.shift();
173
+ if (!frame) return;
174
+ writingDownstream = true;
175
+ if (frame.accounted) pendingToDownstream -= frame.bytes.length;
176
+ try {
177
+ if (!options.downstreamSink.write(frame.bytes)) await waitForDrain(options.downstreamSink);
178
+ } catch (error) {
179
+ await close(error instanceof Error ? error : new Error(String(error)));
180
+ return;
181
+ } finally {
182
+ writingDownstream = false;
183
+ }
184
+ void pumpDownstream();
185
+ };
186
+ const consumeLines = (chunk: Buffer): void => {
187
+ downstreamBuffer = Buffer.concat([downstreamBuffer, chunk]);
188
+ let newline = downstreamBuffer.indexOf(0x0a);
189
+ while (newline >= 0) {
190
+ const line = downstreamBuffer.subarray(0, newline);
191
+ downstreamBuffer = downstreamBuffer.subarray(newline + 1);
192
+ if (line.length === 0)
193
+ return fail({ type: "transport_error", code: "protocol_error", direction: "downstream->ws" });
194
+ if (line.length > REQUEST_FRAME_BYTES)
195
+ return fail({ type: "transport_error", code: "frame_oversize", direction: "downstream->ws" });
196
+ enqueue(toWs, "downstream->ws", line);
197
+ if (closed) return;
198
+ newline = downstreamBuffer.indexOf(0x0a);
199
+ }
200
+ if (downstreamBuffer.length > REQUEST_FRAME_BYTES)
201
+ fail({ type: "transport_error", code: "frame_oversize", direction: "downstream->ws" });
202
+ };
203
+ const onData = (chunk: Buffer | string): void => consumeLines(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
204
+ const onEnd = (): void => void close();
205
+ const onDownstreamError = (error: Error): void => void close(error);
206
+ const onMessage = (event: RelayWebSocketEvent): void => {
207
+ if (typeof event.data !== "string") {
208
+ fail({ type: "transport_error", code: "protocol_error", direction: "ws->downstream" });
209
+ return;
210
+ }
211
+ enqueue(toDownstream, "ws->downstream", Buffer.concat([Buffer.from(event.data, "utf8"), Buffer.from("\n")]));
212
+ };
213
+ const onClose = (): void => void close();
214
+ const onWebSocketError = (): void => void close(new Error("upstream_error"));
215
+ const onOpen = (): void => {
216
+ cleanupOpening();
217
+ opened.resolve();
218
+ };
219
+ const onOpenError = (): void => {
220
+ cleanupOpening();
221
+ opened.reject(new Error("upstream_error"));
222
+ };
223
+ const onAbort = (): void => {
224
+ const error = new RelayOpenAbortedError();
225
+ cleanupOpening();
226
+ opened.reject(error);
227
+ void close(error);
228
+ };
229
+ const cleanupOpening = (): void => {
230
+ ws.removeEventListener("open", onOpen);
231
+ ws.removeEventListener("error", onOpenError);
232
+ options.signal?.removeEventListener("abort", onAbort);
233
+ };
234
+ ws.addEventListener("open", onOpen, { once: true });
235
+ ws.addEventListener("error", onOpenError, { once: true });
236
+ options.signal?.addEventListener("abort", onAbort, { once: true });
237
+ try {
238
+ await opened.promise;
239
+ } catch (error) {
240
+ await close(error instanceof Error ? error : new Error(String(error)));
241
+ throw error;
242
+ }
243
+ ws.addEventListener("message", onMessage);
244
+ ws.addEventListener("close", onClose);
245
+ ws.addEventListener("error", onWebSocketError);
246
+ options.downstream.on("data", onData);
247
+ options.downstream.once("end", onEnd);
248
+ options.downstream.once("error", onDownstreamError);
249
+ if (options.initialDownstreamBytes?.length) consumeLines(options.initialDownstreamBytes);
250
+ if (!closed) options.downstream.resume();
251
+ return { close: () => close(), done: finished.promise };
252
+ }
@@ -0,0 +1,105 @@
1
+ import { CliParseError } from "@sayknow-cli/utils/cli";
2
+ import type { SdkSessionEndpoint } from "../client/discovery";
3
+ import { listSdkSessionEndpoints } from "../client/discovery";
4
+ import { type SdkEndpointSelectionError, selectLiveEndpoint } from "../client/liveness";
5
+ import { DEFAULT_PENDING_CEILING_BYTES, MIN_PENDING_CEILING_BYTES, startSocketServe, startStdioServe } from "./index";
6
+
7
+ type ServeMode = { kind: "stdio" } | { kind: "socket"; socketPath: string };
8
+
9
+ interface ServeArguments {
10
+ mode: ServeMode;
11
+ sessionId?: string;
12
+ pendingCeiling?: string;
13
+ }
14
+
15
+ function usageError(message: string): never {
16
+ throw new CliParseError(`skc sdk serve: ${message}`);
17
+ }
18
+
19
+ function readFlagValue(argv: string[], index: number, flag: string): string {
20
+ const value = argv[index + 1];
21
+ if (value === undefined || value.startsWith("-")) usageError(`${flag} requires a value`);
22
+ return value;
23
+ }
24
+
25
+ function parseServeArguments(argv: string[]): ServeArguments {
26
+ let stdio = false;
27
+ let socketPath: string | undefined;
28
+ let sessionId: string | undefined;
29
+ let pendingCeiling: string | undefined;
30
+ for (let index = 0; index < argv.length; index++) {
31
+ switch (argv[index]) {
32
+ case "--stdio":
33
+ if (stdio) usageError("--stdio may only be specified once");
34
+ stdio = true;
35
+ break;
36
+ case "--socket":
37
+ if (socketPath !== undefined) usageError("--socket may only be specified once");
38
+ socketPath = readFlagValue(argv, index, "--socket");
39
+ index++;
40
+ break;
41
+ case "--session":
42
+ if (sessionId !== undefined) usageError("--session may only be specified once");
43
+ sessionId = readFlagValue(argv, index, "--session");
44
+ index++;
45
+ break;
46
+ case "--pending-ceiling":
47
+ if (pendingCeiling !== undefined) usageError("--pending-ceiling may only be specified once");
48
+ pendingCeiling = readFlagValue(argv, index, "--pending-ceiling");
49
+ index++;
50
+ break;
51
+ default:
52
+ usageError(`unknown argument: ${argv[index]}`);
53
+ }
54
+ }
55
+ if (stdio === (socketPath !== undefined)) usageError("specify exactly one of --stdio or --socket <path>");
56
+ return { mode: stdio ? { kind: "stdio" } : { kind: "socket", socketPath: socketPath! }, sessionId, pendingCeiling };
57
+ }
58
+
59
+ /** Resolves the pending ceiling with flag > env > default precedence; exported for tests. */
60
+ export function resolveServePendingCeiling(flagValue: string | undefined, envValue: string | undefined): number {
61
+ const value = flagValue ?? envValue;
62
+ if (value === undefined) return DEFAULT_PENDING_CEILING_BYTES;
63
+ if (!/^\d+$/.test(value)) usageError("--pending-ceiling must be a positive integer");
64
+ const ceiling = Number(value);
65
+ if (!Number.isSafeInteger(ceiling) || ceiling < MIN_PENDING_CEILING_BYTES)
66
+ usageError(`--pending-ceiling must be an integer of at least ${MIN_PENDING_CEILING_BYTES}`);
67
+ return ceiling;
68
+ }
69
+
70
+ function isSelectionError(value: SdkSessionEndpoint | SdkEndpointSelectionError): value is SdkEndpointSelectionError {
71
+ return "code" in value;
72
+ }
73
+
74
+ /** Attaches a stdio or Unix-socket relay to one live SDK session endpoint. */
75
+ export async function runSdkServe(argv: string[]): Promise<void> {
76
+ const parsed = parseServeArguments(argv);
77
+ if (parsed.mode.kind === "socket" && process.platform === "win32")
78
+ throw new Error("unsupported_platform: --socket is unavailable on Windows.");
79
+ const pendingCeilingBytes = resolveServePendingCeiling(
80
+ parsed.pendingCeiling,
81
+ process.env.SKC_SDK_SERVE_PENDING_CEILING_BYTES,
82
+ );
83
+ const discovered = await listSdkSessionEndpoints(process.cwd());
84
+ const selected = selectLiveEndpoint(discovered.endpoints, parsed.sessionId);
85
+ if (isSelectionError(selected)) {
86
+ const sessionHint = parsed.sessionId ? ` for session ${parsed.sessionId}` : "; specify --session <id>";
87
+ throw new Error(`${selected.code}${sessionHint}`);
88
+ }
89
+ const options = { url: selected.url, token: selected.token, pendingCeilingBytes };
90
+ const handle =
91
+ parsed.mode.kind === "stdio"
92
+ ? await startStdioServe(options)
93
+ : await startSocketServe({ ...options, socketPath: parsed.mode.socketPath });
94
+ const stop = () => {
95
+ void handle.close();
96
+ };
97
+ process.once("SIGINT", stop);
98
+ process.once("SIGTERM", stop);
99
+ try {
100
+ await handle.done;
101
+ } finally {
102
+ process.removeListener("SIGINT", stop);
103
+ process.removeListener("SIGTERM", stop);
104
+ }
105
+ }
@@ -0,0 +1,197 @@
1
+ import * as fsSync from "node:fs";
2
+ import * as fs from "node:fs/promises";
3
+ import * as net from "node:net";
4
+ import * as path from "node:path";
5
+ import { MAX_UNIX_SOCKET_PATH_BYTES } from "../../harness-control-plane/storage";
6
+ import { authenticatePreface } from "./auth-preface";
7
+ import type { ServeHandle, ServeOptions } from "./index";
8
+ import type { RelayOptions, RelayPair, TransportError } from "./relay";
9
+ import { startRelayPair } from "./relay";
10
+
11
+ function socketFailure(code: string): Error {
12
+ return new Error(code);
13
+ }
14
+
15
+ async function validateSocketPath(socketPath: string): Promise<void> {
16
+ if (process.platform === "win32") throw socketFailure("unsupported_platform");
17
+ if (Buffer.byteLength(socketPath) > MAX_UNIX_SOCKET_PATH_BYTES) throw socketFailure("socket_path_too_long");
18
+ try {
19
+ await fs.lstat(socketPath);
20
+ throw socketFailure("socket_path_in_use");
21
+ } catch (error) {
22
+ if (error instanceof Error && error.message === "socket_path_in_use") throw error;
23
+ const code = error as NodeJS.ErrnoException;
24
+ if (code.code !== "ENOENT") throw error;
25
+ }
26
+ let parent: Awaited<ReturnType<typeof fs.stat>>;
27
+ try {
28
+ parent = await fs.stat(path.dirname(socketPath));
29
+ } catch {
30
+ throw socketFailure("socket_dir_insecure");
31
+ }
32
+ if (!parent.isDirectory() || parent.uid !== process.getuid?.() || (parent.mode & 0o022) !== 0)
33
+ throw socketFailure("socket_dir_insecure");
34
+ }
35
+
36
+ function sendTransportError(socket: net.Socket, error: TransportError): void {
37
+ socket.write(`${JSON.stringify(error)}\n`);
38
+ }
39
+
40
+ /** Serves authenticated SDK JSONL clients over a private Unix-domain socket. */
41
+ export async function startSocketServe(
42
+ options: ServeOptions & { socketPath: string; webSocketFactory?: RelayOptions["webSocketFactory"] },
43
+ ): Promise<ServeHandle> {
44
+ await validateSocketPath(options.socketPath);
45
+ const pairs = new Set<RelayPair>();
46
+ const sockets = new Set<net.Socket>();
47
+ const connectionTasks = new Set<Promise<void>>();
48
+ const dialControllers = new Set<AbortController>();
49
+ let server: net.Server | undefined;
50
+ let ownedIdentity: { dev: number; ino: number } | undefined;
51
+ let closing: Promise<void> | undefined;
52
+ const done = Promise.withResolvers<void>();
53
+ void done.promise.catch(() => undefined);
54
+
55
+ const removeOwnedSocket = async (): Promise<void> => {
56
+ if (!ownedIdentity) return;
57
+ try {
58
+ const current = await fs.lstat(options.socketPath);
59
+ if (current.isSocket() && current.dev === ownedIdentity.dev && current.ino === ownedIdentity.ino)
60
+ await fs.unlink(options.socketPath);
61
+ } catch (error) {
62
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
63
+ }
64
+ };
65
+
66
+ const removeOwnedSocketSync = (): void => {
67
+ if (!ownedIdentity) return;
68
+ try {
69
+ const current = fsSync.lstatSync(options.socketPath);
70
+ if (current.isSocket() && current.dev === ownedIdentity.dev && current.ino === ownedIdentity.ino)
71
+ fsSync.unlinkSync(options.socketPath);
72
+ } catch (error) {
73
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
74
+ }
75
+ };
76
+ const cleanupOnExit = (): void => removeOwnedSocketSync();
77
+ process.once("exit", cleanupOnExit);
78
+ const close = async (fatalError?: Error): Promise<void> => {
79
+ if (closing) return await closing;
80
+ closing = (async () => {
81
+ const errors: Error[] = fatalError ? [fatalError] : [];
82
+ for (const controller of dialControllers) controller.abort();
83
+ for (const socket of sockets) socket.destroy();
84
+ const pairResults = await Promise.allSettled([...pairs].map(pair => pair.close()));
85
+ for (const result of pairResults) {
86
+ if (result.status === "rejected")
87
+ errors.push(result.reason instanceof Error ? result.reason : new Error(String(result.reason)));
88
+ }
89
+ const connectionResults = await Promise.allSettled([...connectionTasks]);
90
+ for (const result of connectionResults) {
91
+ if (result.status === "rejected")
92
+ errors.push(result.reason instanceof Error ? result.reason : new Error(String(result.reason)));
93
+ }
94
+ let displacedPath: string | undefined;
95
+ if (server?.listening && ownedIdentity) {
96
+ try {
97
+ const current = await fs.lstat(options.socketPath).catch(() => undefined);
98
+ if (current && (current.dev !== ownedIdentity.dev || current.ino !== ownedIdentity.ino)) {
99
+ displacedPath = `${options.socketPath}.${process.pid}.closing`;
100
+ await fs.rename(options.socketPath, displacedPath);
101
+ }
102
+ } catch (error) {
103
+ errors.push(error instanceof Error ? error : new Error(String(error)));
104
+ }
105
+ }
106
+ if (server?.listening) {
107
+ try {
108
+ await new Promise<void>((resolve, reject) =>
109
+ server?.close(error => (error ? reject(error) : resolve())),
110
+ );
111
+ } catch (error) {
112
+ errors.push(error instanceof Error ? error : new Error(String(error)));
113
+ }
114
+ }
115
+ if (displacedPath) {
116
+ try {
117
+ await fs.rename(displacedPath, options.socketPath);
118
+ } catch (error) {
119
+ errors.push(error instanceof Error ? error : new Error(String(error)));
120
+ }
121
+ }
122
+ try {
123
+ await removeOwnedSocket();
124
+ } catch (error) {
125
+ errors.push(error instanceof Error ? error : new Error(String(error)));
126
+ }
127
+ process.removeListener("exit", cleanupOnExit);
128
+ if (errors.length > 0) {
129
+ const error = new AggregateError(errors, "socket_serve_close_failed");
130
+ done.reject(error);
131
+ throw error;
132
+ }
133
+ done.resolve();
134
+ })();
135
+ return await closing;
136
+ };
137
+ const handleConnection = async (socket: net.Socket, signal: AbortSignal): Promise<void> => {
138
+ sockets.add(socket);
139
+ socket.once("close", () => sockets.delete(socket));
140
+ try {
141
+ const initialDownstreamBytes = await authenticatePreface(socket, options.token);
142
+ if (closing) {
143
+ socket.destroy();
144
+ return;
145
+ }
146
+ const pair = await startRelayPair({
147
+ ...options,
148
+ signal,
149
+ downstream: socket,
150
+ downstreamSink: socket,
151
+ initialDownstreamBytes,
152
+ onTransportError: error => sendTransportError(socket, error),
153
+ });
154
+ pairs.add(pair);
155
+ try {
156
+ await pair.done.catch(() => undefined);
157
+ } finally {
158
+ pairs.delete(pair);
159
+ socket.end();
160
+ }
161
+ } catch {
162
+ socket.destroy();
163
+ }
164
+ };
165
+ server = net.createServer(socket => {
166
+ if (closing) {
167
+ socket.destroy();
168
+ return;
169
+ }
170
+ const controller = new AbortController();
171
+ dialControllers.add(controller);
172
+ const task = handleConnection(socket, controller.signal).finally(() => {
173
+ dialControllers.delete(controller);
174
+ connectionTasks.delete(task);
175
+ });
176
+ connectionTasks.add(task);
177
+ void task;
178
+ });
179
+ server.on("error", error => void close(error).catch(() => undefined));
180
+ await new Promise<void>((resolve, reject) => {
181
+ server?.once("error", reject);
182
+ server?.listen(options.socketPath, () => resolve());
183
+ });
184
+ const bound = await fs.lstat(options.socketPath);
185
+ if (!bound.isSocket()) {
186
+ await close();
187
+ throw socketFailure("socket_bind_failed");
188
+ }
189
+ ownedIdentity = { dev: bound.dev, ino: bound.ino };
190
+ try {
191
+ await fs.chmod(options.socketPath, 0o600);
192
+ } catch (error) {
193
+ await close();
194
+ throw error;
195
+ }
196
+ return { close, done: done.promise };
197
+ }
@@ -0,0 +1,17 @@
1
+ import type { ServeHandle, ServeOptions } from "./index";
2
+ import { startRelayPair } from "./relay";
3
+
4
+ function writeDiagnostic(value: unknown): void {
5
+ process.stderr.write(`${JSON.stringify(value)}\n`);
6
+ }
7
+
8
+ /** Serves one parent-owned JSONL connection over the process standard streams. */
9
+ export async function startStdioServe(options: ServeOptions): Promise<ServeHandle> {
10
+ const pair = await startRelayPair({
11
+ ...options,
12
+ downstream: process.stdin,
13
+ downstreamSink: process.stdout,
14
+ onTransportError: writeDiagnostic,
15
+ });
16
+ return { close: pair.close, done: pair.done };
17
+ }