@gajae-code/coding-agent 0.10.1 → 0.11.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 (474) hide show
  1. package/CHANGELOG.md +70 -2
  2. package/dist/types/append-only-mode.d.ts +1 -1
  3. package/dist/types/cli/args.d.ts +4 -5
  4. package/dist/types/cli/daemon-cli.d.ts +12 -1
  5. package/dist/types/cli/notify-cli.d.ts +21 -1
  6. package/dist/types/cli.d.ts +3 -0
  7. package/dist/types/commands/daemon.d.ts +39 -0
  8. package/dist/types/commands/launch.d.ts +3 -0
  9. package/dist/types/commands/notify.d.ts +28 -1
  10. package/dist/types/commands/sdk.d.ts +33 -0
  11. package/dist/types/config/atomic-yaml-patch.d.ts +85 -0
  12. package/dist/types/config/model-profile-activation.d.ts +35 -15
  13. package/dist/types/config/model-profiles.d.ts +11 -5
  14. package/dist/types/config/model-registry.d.ts +3 -3
  15. package/dist/types/config/model-resolver.d.ts +39 -6
  16. package/dist/types/config/model-selector-value.d.ts +17 -0
  17. package/dist/types/config/models-config-schema.d.ts +10 -6
  18. package/dist/types/config/settings-schema.d.ts +133 -6
  19. package/dist/types/config/settings.d.ts +63 -35
  20. package/dist/types/coordinator-mcp/model-preset.d.ts +7 -6
  21. package/dist/types/coordinator-mcp/server.d.ts +6 -49
  22. package/dist/types/daemon/builtin.d.ts +6 -5
  23. package/dist/types/daemon/control-types.d.ts +1 -1
  24. package/dist/types/discovery/mcp-json.d.ts +12 -1
  25. package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
  26. package/dist/types/extensibility/extensions/compact-handler.d.ts +4 -2
  27. package/dist/types/extensibility/extensions/get-commands-handler.d.ts +2 -2
  28. package/dist/types/extensibility/extensions/loader.d.ts +10 -1
  29. package/dist/types/extensibility/extensions/runner.d.ts +3 -3
  30. package/dist/types/extensibility/extensions/types.d.ts +141 -16
  31. package/dist/types/extensibility/hooks/types.d.ts +1 -1
  32. package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -2
  33. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
  34. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
  35. package/dist/types/gjc-runtime/team-runtime.d.ts +3 -4
  36. package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +13 -0
  37. package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -1
  38. package/dist/types/gjc-runtime/windows-powershell-command.d.ts +9 -0
  39. package/dist/types/harness-control-plane/operate.d.ts +5 -5
  40. package/dist/types/harness-control-plane/owner.d.ts +18 -4
  41. package/dist/types/harness-control-plane/receipts.d.ts +0 -1
  42. package/dist/types/harness-control-plane/sdk-transport.d.ts +43 -0
  43. package/dist/types/harness-control-plane/session-transport.d.ts +48 -0
  44. package/dist/types/harness-control-plane/types.d.ts +3 -4
  45. package/dist/types/lsp/index.d.ts +1 -1
  46. package/dist/types/main.d.ts +32 -38
  47. package/dist/types/modes/acp/acp-agent.d.ts +224 -53
  48. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -0
  49. package/dist/types/modes/acp/acp-mode.d.ts +6 -15
  50. package/dist/types/modes/acp/permission-mode.d.ts +4 -0
  51. package/dist/types/modes/acp/startup-options.d.ts +6 -0
  52. package/dist/types/modes/components/agent-dashboard.d.ts +3 -0
  53. package/dist/types/modes/components/gajae-pet-widget.d.ts +1 -0
  54. package/dist/types/modes/components/model-selector.d.ts +1 -0
  55. package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
  56. package/dist/types/modes/components/pet-capability.d.ts +37 -0
  57. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  58. package/dist/types/modes/components/settings-selector.d.ts +15 -4
  59. package/dist/types/modes/components/welcome.d.ts +1 -1
  60. package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
  61. package/dist/types/modes/index.d.ts +0 -4
  62. package/dist/types/modes/interactive-mode.d.ts +5 -1
  63. package/dist/types/modes/runtime-init.d.ts +2 -2
  64. package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
  65. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
  66. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
  67. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
  68. package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
  69. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +169 -30
  70. package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
  71. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
  72. package/dist/types/modes/types.d.ts +16 -1
  73. package/dist/types/runtime-mcp/client.d.ts +2 -0
  74. package/dist/types/runtime-mcp/config.d.ts +5 -1
  75. package/dist/types/runtime-mcp/manager.d.ts +8 -1
  76. package/dist/types/runtime-mcp/types.d.ts +4 -0
  77. package/dist/types/sdk/acp/adapter.d.ts +67 -0
  78. package/dist/types/sdk/acp/index.d.ts +2 -0
  79. package/dist/types/sdk/broker/broker.d.ts +53 -0
  80. package/dist/types/sdk/broker/discovery.d.ts +30 -0
  81. package/dist/types/sdk/broker/ensure.d.ts +33 -0
  82. package/dist/types/sdk/broker/identity.d.ts +3 -0
  83. package/dist/types/sdk/broker/index.d.ts +8 -0
  84. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
  85. package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
  86. package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
  87. package/dist/types/sdk/broker/runtime.d.ts +33 -0
  88. package/dist/types/sdk/broker/session-index.d.ts +57 -0
  89. package/dist/types/sdk/broker/state-version.d.ts +8 -0
  90. package/dist/types/sdk/broker/transport.d.ts +9 -0
  91. package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
  92. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
  93. package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
  94. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
  95. package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
  96. package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
  97. package/dist/types/sdk/bus/config.d.ts +274 -0
  98. package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
  99. package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
  100. package/dist/types/sdk/bus/discord-daemon.d.ts +45 -0
  101. package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
  102. package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
  103. package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
  104. package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
  105. package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
  106. package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
  107. package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
  108. package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
  109. package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
  110. package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
  111. package/dist/types/sdk/bus/session-control.d.ts +83 -0
  112. package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
  113. package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
  114. package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
  115. package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
  116. package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
  117. package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
  118. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
  119. package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
  120. package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +55 -17
  121. package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
  122. package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
  123. package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
  124. package/dist/types/sdk/cli/index.d.ts +1 -0
  125. package/dist/types/sdk/cli/session-cli.d.ts +19 -0
  126. package/dist/types/sdk/client/client.d.ts +1 -0
  127. package/dist/types/sdk/client/discovery.d.ts +27 -0
  128. package/dist/types/sdk/client/index.d.ts +2 -0
  129. package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
  130. package/dist/types/sdk/host/control/index.d.ts +2 -0
  131. package/dist/types/sdk/host/control/operations.d.ts +65 -0
  132. package/dist/types/sdk/host/events.d.ts +35 -0
  133. package/dist/types/sdk/host/host.d.ts +35 -0
  134. package/dist/types/sdk/host/index.d.ts +4 -0
  135. package/dist/types/sdk/host/query/cursor.d.ts +62 -0
  136. package/dist/types/sdk/host/query/handlers.d.ts +70 -0
  137. package/dist/types/sdk/host/query/index.d.ts +3 -0
  138. package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
  139. package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
  140. package/dist/types/sdk/host/types.d.ts +32 -0
  141. package/dist/types/sdk/index.d.ts +6 -0
  142. package/dist/types/sdk/lifecycle-session.d.ts +14 -0
  143. package/dist/types/sdk/mcp/index.d.ts +1 -0
  144. package/dist/types/sdk/mcp/server.d.ts +37 -0
  145. package/dist/types/sdk/models.d.ts +44 -0
  146. package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
  147. package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
  148. package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
  149. package/dist/types/sdk/startup-capability.d.ts +50 -0
  150. package/dist/types/session/agent-session.d.ts +114 -21
  151. package/dist/types/session/default-model-selection.d.ts +25 -0
  152. package/dist/types/session/fallback-chain-controller.d.ts +52 -0
  153. package/dist/types/session/session-manager.d.ts +58 -1
  154. package/dist/types/setup/credential-auto-import.d.ts +36 -6
  155. package/dist/types/tools/ask-answer-registry.d.ts +1 -1
  156. package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
  157. package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
  158. package/dist/types/tools/browser/launch.d.ts +22 -0
  159. package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
  160. package/dist/types/tools/browser/profile-posture.d.ts +37 -0
  161. package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
  162. package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
  163. package/dist/types/tools/browser/registry.d.ts +19 -0
  164. package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
  165. package/dist/types/tools/index.d.ts +8 -8
  166. package/package.json +34 -13
  167. package/scripts/build-sdk-package-smoke.ts +94 -0
  168. package/scripts/capture-notifications-settings-showcase.ts +435 -0
  169. package/scripts/compile-args.ts +4 -2
  170. package/scripts/g005-daemon-smoke.ts +4 -5
  171. package/scripts/g011-daemon-path-smoke.ts +5 -5
  172. package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
  173. package/scripts/generate-sdk-operation-inventory.ts +867 -0
  174. package/scripts/generate-telegram-baseline-manifest.ts +128 -0
  175. package/scripts/manifest-schema.ts +124 -0
  176. package/scripts/run-test-manifest.ts +153 -0
  177. package/scripts/run-threat-model-check.ts +157 -0
  178. package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
  179. package/scripts/verify-gjc-sdk-rename.ts +90 -0
  180. package/src/append-only-mode.ts +1 -1
  181. package/src/cli/args.ts +43 -12
  182. package/src/cli/auth-gateway-cli.ts +2 -2
  183. package/src/cli/config-cli.ts +2 -1
  184. package/src/cli/daemon-cli.ts +63 -14
  185. package/src/cli/list-models.ts +1 -1
  186. package/src/cli/notify-cli.ts +288 -286
  187. package/src/cli.ts +21 -2
  188. package/src/commands/daemon.ts +50 -6
  189. package/src/commands/harness.ts +93 -36
  190. package/src/commands/launch.ts +5 -2
  191. package/src/commands/mcp-serve.ts +19 -12
  192. package/src/commands/notify.ts +36 -3
  193. package/src/commands/sdk.ts +426 -0
  194. package/src/commit/agentic/agent.ts +1 -0
  195. package/src/config/atomic-yaml-patch.ts +423 -0
  196. package/src/config/model-profile-activation.ts +240 -89
  197. package/src/config/model-profiles.ts +37 -34
  198. package/src/config/model-registry.ts +51 -26
  199. package/src/config/model-resolver.ts +138 -51
  200. package/src/config/model-selector-value.ts +50 -0
  201. package/src/config/models-config-schema.ts +14 -21
  202. package/src/config/settings-schema.ts +128 -10
  203. package/src/config/settings.ts +1120 -158
  204. package/src/coordinator-mcp/model-preset.ts +74 -20
  205. package/src/coordinator-mcp/server.ts +1597 -3052
  206. package/src/daemon/builtin.ts +8 -5
  207. package/src/daemon/control-types.ts +1 -1
  208. package/src/defaults/gjc/skills/team/SKILL.md +4 -4
  209. package/src/discovery/mcp-json.ts +327 -20
  210. package/src/extensibility/custom-tools/types.ts +1 -1
  211. package/src/extensibility/extensions/compact-handler.ts +3 -3
  212. package/src/extensibility/extensions/get-commands-handler.ts +2 -2
  213. package/src/extensibility/extensions/loader.ts +69 -5
  214. package/src/extensibility/extensions/runner.ts +112 -4
  215. package/src/extensibility/extensions/types.ts +176 -17
  216. package/src/extensibility/hooks/types.ts +1 -1
  217. package/src/extensibility/slash-commands.ts +3 -3
  218. package/src/gjc-runtime/launch-tmux.ts +170 -86
  219. package/src/gjc-runtime/psmux-detect.ts +82 -4
  220. package/src/gjc-runtime/session-state-sidecar.ts +41 -31
  221. package/src/gjc-runtime/team-runtime.ts +54 -35
  222. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
  223. package/src/gjc-runtime/tmux-owner-isolation.ts +5 -2
  224. package/src/gjc-runtime/tmux-sessions.ts +19 -13
  225. package/src/gjc-runtime/ultragoal-guard.ts +53 -6
  226. package/src/gjc-runtime/ultragoal-runtime.ts +155 -3
  227. package/src/gjc-runtime/windows-powershell-command.ts +58 -0
  228. package/src/harness-control-plane/operate.ts +9 -9
  229. package/src/harness-control-plane/owner.ts +283 -39
  230. package/src/harness-control-plane/receipts.ts +0 -1
  231. package/src/harness-control-plane/sdk-transport.ts +395 -0
  232. package/src/harness-control-plane/session-transport.ts +94 -0
  233. package/src/harness-control-plane/types.ts +1 -1
  234. package/src/internal-urls/docs-index.generated.ts +36 -37
  235. package/src/lsp/client.ts +42 -6
  236. package/src/lsp/index.ts +2 -2
  237. package/src/main.ts +299 -204
  238. package/src/modes/DESIGN.md +258 -0
  239. package/src/modes/acp/acp-agent.ts +1228 -2612
  240. package/src/modes/acp/acp-client-bridge.ts +2 -20
  241. package/src/modes/acp/acp-event-mapper.ts +18 -2
  242. package/src/modes/acp/acp-mode.ts +8 -36
  243. package/src/modes/acp/permission-mode.ts +25 -0
  244. package/src/modes/acp/startup-options.ts +6 -0
  245. package/src/modes/components/agent-dashboard.ts +37 -17
  246. package/src/modes/components/custom-editor.ts +1 -0
  247. package/src/modes/components/gajae-pet-widget.ts +169 -20
  248. package/src/modes/components/model-selector.ts +93 -35
  249. package/src/modes/components/notifications-settings-editor.ts +1379 -0
  250. package/src/modes/components/pet-capability.ts +115 -0
  251. package/src/modes/components/pet-selector.ts +4 -6
  252. package/src/modes/components/settings-selector.ts +86 -11
  253. package/src/modes/components/welcome.ts +4 -4
  254. package/src/modes/controllers/command-controller.ts +1 -2
  255. package/src/modes/controllers/event-controller.ts +11 -16
  256. package/src/modes/controllers/extension-ui-controller.ts +357 -12
  257. package/src/modes/controllers/input-controller.ts +43 -13
  258. package/src/modes/controllers/selector-controller.ts +671 -44
  259. package/src/modes/index.ts +0 -22
  260. package/src/modes/interactive-mode.ts +108 -24
  261. package/src/modes/runtime-init.ts +298 -6
  262. package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
  263. package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
  264. package/src/modes/shared/agent-wire/event-contract.ts +7 -8
  265. package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
  266. package/src/modes/shared/agent-wire/event-observation.ts +16 -7
  267. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1254 -87
  268. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
  269. package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
  270. package/src/modes/types.ts +16 -1
  271. package/src/runtime-mcp/client.ts +94 -19
  272. package/src/runtime-mcp/config.ts +27 -15
  273. package/src/runtime-mcp/manager.ts +343 -105
  274. package/src/runtime-mcp/oauth-flow.ts +42 -12
  275. package/src/runtime-mcp/transports/http.ts +49 -19
  276. package/src/runtime-mcp/transports/stdio.ts +47 -28
  277. package/src/runtime-mcp/types.ts +10 -0
  278. package/src/sdk/acp/adapter.ts +418 -0
  279. package/src/sdk/acp/index.ts +2 -0
  280. package/src/sdk/broker/broker.ts +642 -0
  281. package/src/sdk/broker/discovery.ts +94 -0
  282. package/src/sdk/broker/ensure.ts +249 -0
  283. package/src/sdk/broker/identity.ts +42 -0
  284. package/src/sdk/broker/index.ts +8 -0
  285. package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
  286. package/src/sdk/broker/internal-source.bunfig.toml +1 -0
  287. package/src/sdk/broker/lifecycle-ledger.ts +286 -0
  288. package/src/sdk/broker/lifecycle.ts +2157 -0
  289. package/src/sdk/broker/process-incarnation.ts +159 -0
  290. package/src/sdk/broker/runtime.ts +164 -0
  291. package/src/sdk/broker/session-index.ts +318 -0
  292. package/src/sdk/broker/state-version.ts +23 -0
  293. package/src/sdk/broker/transport.ts +125 -0
  294. package/src/sdk/bus/chat-command-policy.ts +107 -0
  295. package/src/sdk/bus/chat-daemon-cli.ts +214 -0
  296. package/src/sdk/bus/chat-daemon-control.ts +713 -0
  297. package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
  298. package/src/sdk/bus/chat-effect-journal.ts +353 -0
  299. package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
  300. package/src/sdk/bus/config.ts +433 -0
  301. package/src/sdk/bus/conversation-store.ts +391 -0
  302. package/src/sdk/bus/discord-conversation.ts +85 -0
  303. package/src/sdk/bus/discord-daemon.ts +2275 -0
  304. package/src/sdk/bus/discord-live-provider.ts +521 -0
  305. package/src/sdk/bus/discord-provider.ts +56 -0
  306. package/src/{notifications → sdk/bus}/html-format.ts +2 -2
  307. package/src/sdk/bus/index.ts +3943 -0
  308. package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
  309. package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
  310. package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
  311. package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
  312. package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
  313. package/src/sdk/bus/natives-augment.d.ts +38 -0
  314. package/src/sdk/bus/notification-orchestration.ts +535 -0
  315. package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
  316. package/src/sdk/bus/session-control.ts +333 -0
  317. package/src/sdk/bus/slack-conversation.ts +74 -0
  318. package/src/sdk/bus/slack-daemon.ts +2144 -0
  319. package/src/sdk/bus/slack-live-provider.ts +380 -0
  320. package/src/sdk/bus/slack-provider.ts +76 -0
  321. package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
  322. package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
  323. package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
  324. package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
  325. package/src/{notifications → sdk/bus}/telegram-daemon.ts +502 -54
  326. package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
  327. package/src/sdk/bus/telegram-setup.ts +663 -0
  328. package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
  329. package/src/sdk/cli/index.ts +1 -0
  330. package/src/sdk/cli/session-cli.ts +275 -0
  331. package/src/sdk/client/client.ts +1 -0
  332. package/src/sdk/client/discovery.ts +108 -0
  333. package/src/sdk/client/index.ts +2 -0
  334. package/src/sdk/host/control/dispatch.ts +367 -0
  335. package/src/sdk/host/control/index.ts +10 -0
  336. package/src/sdk/host/control/operations.ts +63 -0
  337. package/src/sdk/host/events.ts +91 -0
  338. package/src/sdk/host/host.ts +296 -0
  339. package/src/sdk/host/index.ts +4 -0
  340. package/src/sdk/host/query/cursor.ts +189 -0
  341. package/src/sdk/host/query/handlers.ts +533 -0
  342. package/src/sdk/host/query/index.ts +3 -0
  343. package/src/sdk/host/query/revision-store.ts +1126 -0
  344. package/src/sdk/host/reverse-leases.ts +325 -0
  345. package/src/sdk/host/types.ts +29 -0
  346. package/src/sdk/index.ts +13 -0
  347. package/src/sdk/lifecycle-session.ts +39 -0
  348. package/src/sdk/mcp/index.ts +1 -0
  349. package/src/sdk/mcp/server.ts +332 -0
  350. package/src/sdk/models.ts +168 -0
  351. package/src/sdk/protocol/adapter-validation.ts +48 -0
  352. package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
  353. package/src/sdk/protocol/operation-registry.ts +312 -0
  354. package/src/{sdk.ts → sdk/session.ts} +339 -212
  355. package/src/sdk/startup-capability.ts +189 -0
  356. package/src/session/agent-session.ts +2470 -866
  357. package/src/session/default-model-selection.ts +93 -0
  358. package/src/session/fallback-chain-controller.ts +136 -0
  359. package/src/session/session-manager.ts +447 -13
  360. package/src/session/session-storage.ts +8 -1
  361. package/src/setup/credential-auto-import.ts +290 -35
  362. package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
  363. package/src/slash-commands/builtin-registry.ts +33 -30
  364. package/src/task/executor.ts +62 -13
  365. package/src/tools/ask-answer-registry.ts +1 -1
  366. package/src/tools/ask.ts +13 -8
  367. package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
  368. package/src/tools/browser/benchmark/detector-report.ts +157 -0
  369. package/src/tools/browser/benchmark/run-suite.ts +39 -0
  370. package/src/tools/browser/launch.ts +124 -19
  371. package/src/tools/browser/profile-discovery.ts +63 -0
  372. package/src/tools/browser/profile-posture.ts +59 -0
  373. package/src/tools/browser/profile-reuse.ts +102 -0
  374. package/src/tools/browser/profile-warmup.ts +109 -0
  375. package/src/tools/browser/registry.ts +87 -11
  376. package/src/tools/browser/tab-protocol.ts +1 -0
  377. package/src/tools/browser/tab-supervisor.ts +1 -0
  378. package/src/tools/browser/tab-worker.ts +1 -1
  379. package/src/tools/browser.ts +6 -0
  380. package/src/tools/index.ts +8 -8
  381. package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
  382. package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
  383. package/src/tools/telegram-send.ts +2 -2
  384. package/vendor/insane-search/MANIFEST.json +2 -1
  385. package/vendor/insane-search/engine/bias_check.py +4 -1
  386. package/vendor/insane-search/engine/phase0.py +26 -1
  387. package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
  388. package/dist/types/modes/bridge/auth.d.ts +0 -12
  389. package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
  390. package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
  391. package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
  392. package/dist/types/modes/bridge/event-stream.d.ts +0 -8
  393. package/dist/types/modes/rpc/host-tools.d.ts +0 -1
  394. package/dist/types/modes/rpc/host-uris.d.ts +0 -1
  395. package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
  396. package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
  397. package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
  398. package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
  399. package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
  400. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
  401. package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
  402. package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
  403. package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
  404. package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
  405. package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
  406. package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
  407. package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
  408. package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
  409. package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
  410. package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
  411. package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
  412. package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
  413. package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
  414. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
  415. package/dist/types/notifications/config.d.ts +0 -132
  416. package/src/harness-control-plane/rpc-adapter.ts +0 -290
  417. package/src/modes/bridge/auth.ts +0 -41
  418. package/src/modes/bridge/bridge-client-bridge.ts +0 -47
  419. package/src/modes/bridge/bridge-mode.ts +0 -675
  420. package/src/modes/bridge/bridge-ui-context.ts +0 -200
  421. package/src/modes/bridge/event-stream.ts +0 -70
  422. package/src/modes/rpc/host-tools.ts +0 -1
  423. package/src/modes/rpc/host-uris.ts +0 -1
  424. package/src/modes/rpc/rpc-client.ts +0 -1166
  425. package/src/modes/rpc/rpc-mode.ts +0 -1029
  426. package/src/modes/rpc/rpc-socket-security.ts +0 -115
  427. package/src/modes/rpc/rpc-types.ts +0 -637
  428. package/src/modes/shared/agent-wire/command-contract.ts +0 -18
  429. package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
  430. package/src/modes/shared/agent-wire/command-validation.ts +0 -181
  431. package/src/modes/shared/agent-wire/handshake.ts +0 -157
  432. package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
  433. package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
  434. package/src/modes/shared/agent-wire/protocol.ts +0 -46
  435. package/src/modes/shared/agent-wire/responses.ts +0 -17
  436. package/src/modes/shared/agent-wire/scopes.ts +0 -94
  437. package/src/modes/shared/agent-wire/session-registry.ts +0 -109
  438. package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
  439. package/src/modes/shared/agent-wire/ui-result.ts +0 -48
  440. package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
  441. package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
  442. package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
  443. package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
  444. package/src/notifications/config.ts +0 -219
  445. package/src/notifications/index.ts +0 -1835
  446. package/src/notifications/telegram-daemon-cli.ts +0 -243
  447. package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
  448. /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
  449. /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
  450. /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
  451. /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
  452. /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
  453. /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
  454. /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
  455. /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
  456. /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
  457. /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
  458. /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
  459. /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
  460. /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
  461. /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
  462. /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
  463. /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
  464. /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
  465. /package/src/{notifications → sdk/bus}/engine.ts +0 -0
  466. /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
  467. /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
  468. /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
  469. /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
  470. /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
  471. /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
  472. /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
  473. /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
  474. /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
@@ -0,0 +1,94 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import * as fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { processIncarnation } from "./process-incarnation";
5
+ import { assertSupportedStateVersion, SDK_STATE_VERSION } from "./state-version";
6
+
7
+ export const BROKER_HEARTBEAT_TTL_MS = 15_000;
8
+ export interface BrokerDiscovery {
9
+ version: typeof SDK_STATE_VERSION;
10
+ protocolVersion: 3;
11
+ packageGeneration: string;
12
+ ownerId: string;
13
+ pid: number;
14
+ incarnation: string;
15
+ host: "127.0.0.1";
16
+ port: number;
17
+ url: string;
18
+ token: string;
19
+ startedAt: number;
20
+ heartbeatAt: number;
21
+ }
22
+ export type RedactedBrokerDiscovery = Omit<BrokerDiscovery, "token"> & { token: "[redacted]" };
23
+ export type BrokerDiscoveryWrite = Omit<BrokerDiscovery, "incarnation"> & { incarnation?: string };
24
+ export const brokerDiscoveryPath = (agentDir: string) => path.join(agentDir, "sdk", "broker.json");
25
+ export const newBrokerToken = () => randomBytes(32).toString("hex");
26
+ export const brokerProcessIncarnation = processIncarnation;
27
+ export function isPidAlive(pid: number): boolean {
28
+ try {
29
+ process.kill(pid, 0);
30
+ return true;
31
+ } catch (e) {
32
+ return (e as NodeJS.ErrnoException).code === "EPERM";
33
+ }
34
+ }
35
+ async function syncFile(file: string): Promise<void> {
36
+ const handle = await fs.open(file, "r");
37
+ try {
38
+ await handle.sync();
39
+ } finally {
40
+ await handle.close();
41
+ }
42
+ }
43
+ export async function writeBrokerDiscovery(agentDir: string, discovery: BrokerDiscoveryWrite): Promise<void> {
44
+ const incarnation = discovery.incarnation ?? brokerProcessIncarnation(discovery.pid);
45
+ if (!incarnation) throw new Error(`Broker process incarnation is unavailable for pid ${discovery.pid}.`);
46
+ const record: BrokerDiscovery = { ...discovery, incarnation };
47
+ const file = brokerDiscoveryPath(agentDir);
48
+ const dir = path.dirname(file);
49
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
50
+ await fs.chmod(dir, 0o700);
51
+ const temp = `${file}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
52
+ try {
53
+ await fs.writeFile(temp, `${JSON.stringify(record)}\n`, { mode: 0o600 });
54
+ await fs.chmod(temp, 0o600);
55
+ await syncFile(temp);
56
+ await fs.rename(temp, file);
57
+ await syncFile(dir);
58
+ } finally {
59
+ await fs.rm(temp, { force: true });
60
+ }
61
+ }
62
+ export async function readBrokerDiscovery(
63
+ agentDir: string,
64
+ ttlMs = BROKER_HEARTBEAT_TTL_MS,
65
+ ): Promise<BrokerDiscovery | null> {
66
+ try {
67
+ const raw: unknown = JSON.parse(await fs.readFile(brokerDiscoveryPath(agentDir), "utf8"));
68
+ if (!raw || typeof raw !== "object") return null;
69
+ assertSupportedStateVersion(brokerDiscoveryPath(agentDir), raw);
70
+ const d = raw as BrokerDiscovery;
71
+ if (
72
+ d.version !== SDK_STATE_VERSION ||
73
+ d.protocolVersion !== 3 ||
74
+ d.host !== "127.0.0.1" ||
75
+ !d.token ||
76
+ !Number.isSafeInteger(d.pid) ||
77
+ d.pid <= 0 ||
78
+ typeof d.incarnation !== "string" ||
79
+ d.incarnation.length === 0 ||
80
+ !Number.isFinite(d.heartbeatAt)
81
+ )
82
+ return null;
83
+ if (!isPidAlive(d.pid)) return null;
84
+ const incarnation = brokerProcessIncarnation(d.pid);
85
+ if (!incarnation || incarnation !== d.incarnation || Date.now() - d.heartbeatAt > ttlMs) return null;
86
+ return d;
87
+ } catch (e) {
88
+ if ((e as NodeJS.ErrnoException).code === "ENOENT" || e instanceof SyntaxError) return null;
89
+ throw e;
90
+ }
91
+ }
92
+ export function redactBrokerDiscovery(discovery: BrokerDiscovery): RedactedBrokerDiscovery {
93
+ return { ...discovery, token: "[redacted]" };
94
+ }
@@ -0,0 +1,249 @@
1
+ import { type ChildProcess, spawn } from "node:child_process";
2
+ import { type BrokerDiscovery, brokerProcessIncarnation, readBrokerDiscovery } from "./discovery";
3
+ import { resolveSdkInternalSpawnCommand, type SdkInternalSpawnCommand } from "./runtime";
4
+ export interface EnsureBrokerSettings {
5
+ agentDir: string;
6
+ heartbeatTtlMs?: number;
7
+ /**
8
+ * Environment for the spawned detached broker. Defaults to `process.env`; tests
9
+ * that pre-start an isolated broker pass the same sanitized child env so the
10
+ * broker and the child that attaches to it share one owned root.
11
+ */
12
+ env?: NodeJS.ProcessEnv;
13
+ }
14
+
15
+ const DISCOVERY_TIMEOUT_MS = 10_000;
16
+ // Bounded grace windows for reaping a spawned broker on failure, mirroring the
17
+ // owned-process teardown convention (SIGTERM -> grace -> SIGKILL -> hard cap).
18
+ const REAP_GRACEFUL_MS = 2_000;
19
+ const REAP_SIGKILL_CAP_MS = 2_000;
20
+ interface BrokerOwner {
21
+ stop(): Promise<void>;
22
+ canReuse(discovery: BrokerDiscovery | null): boolean;
23
+ markReady(discovery: BrokerDiscovery): boolean;
24
+ }
25
+ const owners = new Map<string, BrokerOwner>();
26
+ const ensureInFlight = new Map<string, Promise<BrokerDiscovery>>();
27
+ const reapErrorGuards = new WeakSet<ChildProcess>();
28
+ interface ReapTiming {
29
+ gracefulMs: number;
30
+ killVerifyMs: number;
31
+ }
32
+ const DEFAULT_REAP_TIMING: ReapTiming = {
33
+ gracefulMs: REAP_GRACEFUL_MS,
34
+ killVerifyMs: REAP_SIGKILL_CAP_MS,
35
+ };
36
+ const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
37
+
38
+ /**
39
+ * Terminate and reap a detached broker this process spawned, targeting the exact
40
+ * owned {@link ChildProcess} (never by name). SIGTERM escalates to SIGKILL after
41
+ * a bounded grace window; a child still alive after SIGKILL is surfaced rather
42
+ * than silently orphaned. Reaping is idempotent once the child has exited.
43
+ *
44
+ * Termination is proven only by an observed exit — an `exit`/`close` event or a
45
+ * non-null `exitCode`/`signalCode`. A still-live child can emit `error` during
46
+ * teardown (e.g. a transient signal-delivery failure); that is diagnostic only
47
+ * and never counts as exit, so the escalation cannot be skipped mid-shutdown.
48
+ */
49
+ async function reapSpawnedBroker(child: ChildProcess, timing: ReapTiming = DEFAULT_REAP_TIMING): Promise<void> {
50
+ // A spawn failure (e.g. ENOENT) never created a kernel process: pid is
51
+ // undefined and there is nothing to signal or await. The `error` event is the
52
+ // only signal and is diagnostic here — termination trivially holds, so do not
53
+ // run out the TERM/KILL windows or report a stuck child that never existed.
54
+ if (child.pid === undefined) return;
55
+ // Reaping owns repeated teardown diagnostics too. Keep exactly one error
56
+ // listener for the retained child so a later signal-delivery error cannot
57
+ // become an unhandled EventEmitter error after the spawn listener is consumed.
58
+ if (!reapErrorGuards.has(child)) {
59
+ child.on("error", () => {});
60
+ reapErrorGuards.add(child);
61
+ }
62
+
63
+ // Awaits an authoritative exit signal, never a transient `error`. Resolves on
64
+ // an `exit`/`close` event or when the codes are already set; the caller
65
+ // re-checks the codes after the race, so resolution alone is never proof.
66
+ const awaitVerifiedExit = (): Promise<void> => {
67
+ const { promise, resolve } = Promise.withResolvers<void>();
68
+ if (child.exitCode !== null || child.signalCode !== null) resolve();
69
+ else {
70
+ child.once("exit", () => resolve());
71
+ child.once("close", () => resolve());
72
+ }
73
+ return promise;
74
+ };
75
+ // Observed exit is authoritative: only non-null exit/signal codes prove the
76
+ // child is gone, regardless of which event (if any) resolved the wait.
77
+ const hasExited = (): boolean => child.exitCode !== null || child.signalCode !== null;
78
+ const signal = (sig: NodeJS.Signals): void => {
79
+ if (hasExited()) return;
80
+ try {
81
+ child.kill(sig);
82
+ } catch {
83
+ // already exited between the liveness check and the kill
84
+ }
85
+ };
86
+ if (hasExited()) return;
87
+ signal("SIGTERM");
88
+ await Promise.race([awaitVerifiedExit(), sleep(timing.gracefulMs)]);
89
+ if (hasExited()) return;
90
+ signal("SIGKILL");
91
+ await Promise.race([awaitVerifiedExit(), sleep(timing.killVerifyMs)]);
92
+ if (hasExited()) return;
93
+ // SIGKILL is uninterruptible; a child still alive past this bounded wait is a
94
+ // kernel-level stuck state. Surface it rather than silently orphaning the spawn.
95
+ throw new Error(`Detached SDK broker (pid ${child.pid}) did not exit after SIGKILL during reap.`);
96
+ }
97
+
98
+ function registerBrokerOwner(
99
+ agentDir: string,
100
+ child: ChildProcess,
101
+ timing: ReapTiming = DEFAULT_REAP_TIMING,
102
+ ): BrokerOwner {
103
+ const incarnation = child.pid === undefined ? undefined : brokerProcessIncarnation(child.pid);
104
+ let state: "starting" | "ready" | "cleanup-unverified" = "starting";
105
+ const matches = (discovery: BrokerDiscovery | null): boolean =>
106
+ Boolean(
107
+ discovery &&
108
+ child.pid !== undefined &&
109
+ incarnation &&
110
+ discovery.pid === child.pid &&
111
+ discovery.incarnation === incarnation,
112
+ );
113
+ const owner: BrokerOwner = {
114
+ async stop(): Promise<void> {
115
+ try {
116
+ await reapSpawnedBroker(child, timing);
117
+ } catch (error) {
118
+ state = "cleanup-unverified";
119
+ throw error;
120
+ }
121
+ if (owners.get(agentDir) === owner) owners.delete(agentDir);
122
+ },
123
+ canReuse(discovery): boolean {
124
+ return state === "ready" && matches(discovery);
125
+ },
126
+ markReady(discovery): boolean {
127
+ if (!matches(discovery)) return false;
128
+ state = "ready";
129
+ return true;
130
+ },
131
+ };
132
+ owners.set(agentDir, owner);
133
+ return owner;
134
+ }
135
+ function brokerSpawnEnvironment(command: SdkInternalSpawnCommand, override?: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
136
+ const environment = { ...(override ?? command.env) };
137
+ delete environment.BUN_OPTIONS;
138
+ if (command.kind === "bun-source") {
139
+ delete environment.PI_COMPILED;
140
+ delete environment.GJC_COMPILED;
141
+ }
142
+ return environment;
143
+ }
144
+
145
+ async function ensureBrokerOnce(settings: EnsureBrokerSettings): Promise<BrokerDiscovery> {
146
+ const priorOwner = owners.get(settings.agentDir);
147
+ const existing = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
148
+ if (priorOwner) {
149
+ // A retained cleanup failure fences every discovery record. Only a ready
150
+ // record bound to this exact child incarnation may be reused.
151
+ if (priorOwner.canReuse(existing)) return existing!;
152
+ await priorOwner.stop();
153
+ const discoveredAfterCleanup = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
154
+ if (discoveredAfterCleanup) return discoveredAfterCleanup;
155
+ } else if (existing) {
156
+ return existing;
157
+ }
158
+
159
+ const command = resolveSdkInternalSpawnCommand("broker-internal");
160
+ const child = spawn(command.file, [...command.args, "--agent-dir", settings.agentDir], {
161
+ detached: true,
162
+ stdio: "ignore",
163
+ env: brokerSpawnEnvironment(command, settings.env),
164
+ ...(command.kind === "bun-source" ? { cwd: command.cwd } : {}),
165
+ });
166
+ child.unref();
167
+ let spawnError: Error | undefined;
168
+ child.once("error", error => {
169
+ spawnError = error;
170
+ });
171
+ const owner = registerBrokerOwner(settings.agentDir, child);
172
+ const deadline = Date.now() + DISCOVERY_TIMEOUT_MS;
173
+ let discoveryError: unknown;
174
+ while (Date.now() < deadline) {
175
+ // Fail fast: if the spawn failed or the child already exited, discovery can
176
+ // never succeed — stop (a no-op once dead) and surface the failure instead
177
+ // of running out the discovery timeout with an orphaned child.
178
+ if (spawnError || child.exitCode !== null || child.signalCode !== null) break;
179
+ try {
180
+ const discovered = await readBrokerDiscovery(settings.agentDir, settings.heartbeatTtlMs);
181
+ if (discovered) {
182
+ if (owner.markReady(discovered)) return discovered;
183
+ await owner.stop();
184
+ return discovered;
185
+ }
186
+ } catch (error) {
187
+ // A transient read failure (e.g. a half-written record) must not orphan the
188
+ // child; remember it and keep polling. It is surfaced if discovery fails.
189
+ discoveryError = error;
190
+ }
191
+ await sleep(50);
192
+ }
193
+ // Discovery failed (timeout, early child exit, spawn error, or unreadable
194
+ // discovery): the spawned broker never became reachable. Terminate and reap it
195
+ // so the failure cannot leave a detached orphan behind, then surface why.
196
+ const exitedBeforeDiscovery = child.exitCode !== null || child.signalCode !== null;
197
+ const failure = spawnError
198
+ ? new Error(`Failed to spawn detached SDK broker: ${spawnError.message}`)
199
+ : exitedBeforeDiscovery
200
+ ? new Error(
201
+ `Detached SDK broker exited before discovery (code=${child.exitCode}, signal=${child.signalCode}).`,
202
+ )
203
+ : discoveryError
204
+ ? discoveryError
205
+ : new Error("Timed out waiting for detached SDK broker discovery.");
206
+ try {
207
+ await owner.stop();
208
+ } catch (cleanupError) {
209
+ throw new AggregateError([failure, cleanupError], "SDK broker discovery and spawned broker cleanup both failed.");
210
+ }
211
+ throw failure;
212
+ }
213
+
214
+ /** Starts the detached broker entrypoint when discovery has no live owner. */
215
+ export function ensureBroker(settings: EnsureBrokerSettings): Promise<BrokerDiscovery> {
216
+ const existing = ensureInFlight.get(settings.agentDir);
217
+ if (existing) return existing;
218
+ const pending = ensureBrokerOnce(settings);
219
+ ensureInFlight.set(settings.agentDir, pending);
220
+ const clear = (): void => {
221
+ if (ensureInFlight.get(settings.agentDir) === pending) ensureInFlight.delete(settings.agentDir);
222
+ };
223
+ void pending.then(clear, clear);
224
+ return pending;
225
+ }
226
+
227
+ /** Test hook: returns a stop handle for the detached broker this process spawned. */
228
+ export function brokerOwnerForTest(agentDir: string): BrokerOwner | undefined {
229
+ return owners.get(agentDir);
230
+ }
231
+ /** Test hook: drives the detached-broker reap on a controllable child surface. */
232
+ export function reapSpawnedBrokerForTest(child: ChildProcess, timing: ReapTiming = DEFAULT_REAP_TIMING): Promise<void> {
233
+ return reapSpawnedBroker(child, timing);
234
+ }
235
+ /** Test hook: resolves the complete broker environment without spawning. */
236
+ export function brokerSpawnEnvironmentForTest(
237
+ command: SdkInternalSpawnCommand,
238
+ override?: NodeJS.ProcessEnv,
239
+ ): NodeJS.ProcessEnv {
240
+ return brokerSpawnEnvironment(command, override);
241
+ }
242
+ /** Test hook: installs an exact controllable owner to exercise replacement fencing. */
243
+ export function registerBrokerOwnerForTest(
244
+ agentDir: string,
245
+ child: ChildProcess,
246
+ timing: ReapTiming = DEFAULT_REAP_TIMING,
247
+ ): BrokerOwner {
248
+ return registerBrokerOwner(agentDir, child, timing);
249
+ }
@@ -0,0 +1,42 @@
1
+ import { createHmac, randomBytes } from "node:crypto";
2
+ import * as fs from "node:fs/promises";
3
+ import path from "node:path";
4
+
5
+ const MODE = 0o600;
6
+
7
+ export function brokerIdentityPath(agentDir: string): string {
8
+ return path.join(agentDir, "sdk", "broker.identity");
9
+ }
10
+
11
+ export async function getBrokerIdentityKey(agentDir: string): Promise<string> {
12
+ const file = brokerIdentityPath(agentDir);
13
+ await fs.mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
14
+ try {
15
+ const key = (await fs.readFile(file, "utf8")).trim();
16
+ if (/^[0-9a-f]{64}$/i.test(key)) return key;
17
+ throw new Error(`Invalid broker identity key at ${file}`);
18
+ } catch (error) {
19
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
20
+ const key = randomBytes(32).toString("hex");
21
+ try {
22
+ await fs.writeFile(file, `${key}\n`, { encoding: "utf8", mode: MODE, flag: "wx" });
23
+ await fs.chmod(file, MODE);
24
+ return key;
25
+ } catch (writeError) {
26
+ if ((writeError as NodeJS.ErrnoException).code !== "EEXIST") throw writeError;
27
+ return getBrokerIdentityKey(agentDir);
28
+ }
29
+ }
30
+ }
31
+
32
+ export async function deriveIdempotencyIdentity(
33
+ agentDir: string,
34
+ operation: string,
35
+ callerKey: string,
36
+ canonicalTargetHash: string,
37
+ ): Promise<string> {
38
+ const key = await getBrokerIdentityKey(agentDir);
39
+ return createHmac("sha256", Buffer.from(key, "hex"))
40
+ .update(`3|${operation}|${callerKey}|${canonicalTargetHash}`)
41
+ .digest("hex");
42
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./broker";
2
+ export * from "./discovery";
3
+ export * from "./ensure";
4
+ export * from "./identity";
5
+ export * from "./lifecycle";
6
+ export * from "./lifecycle-ledger";
7
+ export * from "./session-index";
8
+ export * from "./transport";
@@ -0,0 +1 @@
1
+ gjc-sdk-broker-internal-source-marker-2178
@@ -0,0 +1 @@
1
+ # Product-owned empty Bun configuration for isolated SDK-internal source children.
@@ -0,0 +1,286 @@
1
+ import { createHash } from "node:crypto";
2
+ import * as fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import type { SdkStartupFailure, SdkStartupRollbackResult } from "../startup-capability";
5
+ import { assertSupportedStateVersion, SDK_STATE_VERSION } from "./state-version";
6
+
7
+ export type LifecycleState =
8
+ | "accepted"
9
+ | "effect_started"
10
+ | "awaiting_ready"
11
+ | "terminal_ok"
12
+ | "terminal_error"
13
+ | "terminal_uncertain";
14
+ export interface LifecycleWorktreeIntent {
15
+ repoRoot: string;
16
+ worktreePath: string;
17
+ detached: boolean;
18
+ baseRef: string;
19
+ branchName?: string;
20
+ }
21
+
22
+ export interface LifecycleEffectIntent {
23
+ sessionId: string;
24
+ stateRoot: string;
25
+ childOwnershipEstablished?: boolean;
26
+ worktree?: LifecycleWorktreeIntent;
27
+ }
28
+
29
+ /** Durable lifecycle effects retained for exact replay; never implies rollback authority. */
30
+ export interface LifecycleCleanupProof {
31
+ processExited: true;
32
+ endpointRemoved: true;
33
+ hostUnregistered:
34
+ | { state: "unregistered"; indexSeq: number; lifecycleRequestId?: string }
35
+ | { state: "not_registered" };
36
+ rollback: {
37
+ endpointGeneration: number | null;
38
+ fenced: true;
39
+ runtimeRemoved: true;
40
+ hostStopped: true;
41
+ brokerRegistrationReleased: true;
42
+ };
43
+ }
44
+
45
+ export interface LifecycleStartupFailureReceipt extends SdkStartupFailure {
46
+ artifactDigest: string;
47
+ rollback: SdkStartupRollbackResult;
48
+ cleanupProof?: LifecycleCleanupProof;
49
+ }
50
+
51
+ export interface LifecycleDurableEffectsReceipt {
52
+ worktree?: {
53
+ cwdDigest: string;
54
+ created: boolean;
55
+ reused: boolean;
56
+ branchDigest?: string;
57
+ };
58
+ transcript?: {
59
+ identityDigest: string;
60
+ contentDigest: string;
61
+ };
62
+ startup?: LifecycleStartupFailureReceipt;
63
+ digest?: string;
64
+ }
65
+
66
+ export interface LifecycleLedgerEntry {
67
+ version: typeof SDK_STATE_VERSION;
68
+ identity: string;
69
+ requestHash: string;
70
+ state: LifecycleState;
71
+ intendedSessionId?: string;
72
+ resultSessionId?: string;
73
+ effectMarker?: string;
74
+ effectIntent?: LifecycleEffectIntent;
75
+ durableEffects?: LifecycleDurableEffectsReceipt;
76
+ startupFailure?: LifecycleStartupFailureReceipt;
77
+
78
+ endpointGeneration?: number;
79
+ responseDigest?: string;
80
+ response?: unknown;
81
+ ts: number;
82
+ }
83
+ export type BeginResult =
84
+ | { kind: "new"; entry: LifecycleLedgerEntry }
85
+ | { kind: "replay"; entry: LifecycleLedgerEntry }
86
+ | { kind: "idempotency_conflict" }
87
+ | { kind: "terminal_uncertain"; entry: LifecycleLedgerEntry }
88
+ | { kind: "in_progress"; entry: LifecycleLedgerEntry };
89
+ const terminal = (s: LifecycleState) => s === "terminal_ok" || s === "terminal_error";
90
+ function canonicalJson(value: unknown): string {
91
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
92
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
93
+ const record = value as Record<string, unknown>;
94
+ return `{${Object.keys(record)
95
+ .sort()
96
+ .map(key => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)
97
+ .join(",")}}`;
98
+ }
99
+
100
+ function hasValidTerminalDigests(entry: LifecycleLedgerEntry): boolean {
101
+ if (!terminal(entry.state) && entry.state !== "terminal_uncertain") return true;
102
+ if (
103
+ (entry.state !== "terminal_uncertain" || entry.response !== undefined) &&
104
+ (entry.response === undefined ||
105
+ typeof entry.responseDigest !== "string" ||
106
+ entry.responseDigest !== createHash("sha256").update(canonicalJson(entry.response)).digest("hex"))
107
+ )
108
+ return false;
109
+ if (!entry.durableEffects) return true;
110
+ const { digest, ...body } = entry.durableEffects;
111
+ return typeof digest === "string" && digest === createHash("sha256").update(canonicalJson(body)).digest("hex");
112
+ }
113
+ export class LifecycleLedger {
114
+ #file: string;
115
+ #corruptFile: string;
116
+ #entries: LifecycleLedgerEntry[] = [];
117
+ #byIdentity = new Map<string, LifecycleLedgerEntry>();
118
+ #warnings: string[] = [];
119
+ constructor(agentDir: string) {
120
+ this.#file = path.join(agentDir, "sdk", "lifecycle-ledger.jsonl");
121
+ this.#corruptFile = `${this.#file}.corrupt`;
122
+ }
123
+ async open(): Promise<this> {
124
+ await this.assertSupportedStateVersions();
125
+ await fs.mkdir(path.dirname(this.#file), { recursive: true, mode: 0o700 });
126
+ this.#entries = [];
127
+ const quarantinedTerminal = new Set<string>();
128
+ this.#byIdentity.clear();
129
+ this.#warnings = [];
130
+ const uncertainAfterCorruption = new Set<string>();
131
+ let tornTail = false;
132
+ try {
133
+ const source = await fs.readFile(this.#file, "utf8");
134
+ tornTail = source.length > 0 && !source.endsWith("\n");
135
+ const lines = source.split("\n");
136
+ for (const line of lines) {
137
+ if (!line) continue;
138
+ try {
139
+ const e = JSON.parse(line) as LifecycleLedgerEntry;
140
+ assertSupportedStateVersion(this.#file, e);
141
+ if (!e.identity || !e.requestHash || !e.state) throw new Error("invalid ledger entry");
142
+ if (!hasValidTerminalDigests(e)) {
143
+ await this.#quarantine(line);
144
+ const {
145
+ response: _response,
146
+ responseDigest: _responseDigest,
147
+ durableEffects: _durableEffects,
148
+ ...uncertain
149
+ } = e;
150
+ const quarantined = { ...uncertain, state: "terminal_uncertain" as const, ts: Date.now() };
151
+ this.#entries.push(quarantined);
152
+ this.#byIdentity.set(quarantined.identity, quarantined);
153
+ quarantinedTerminal.add(quarantined.identity);
154
+ continue;
155
+ }
156
+ this.#entries.push(e);
157
+ this.#byIdentity.set(e.identity, e);
158
+ uncertainAfterCorruption.delete(e.identity);
159
+ } catch {
160
+ for (const [identity, latest] of this.#byIdentity) {
161
+ if (!terminal(latest.state) && latest.state !== "terminal_uncertain")
162
+ uncertainAfterCorruption.add(identity);
163
+ }
164
+ await this.#quarantine(line);
165
+ }
166
+ }
167
+ } catch (e) {
168
+ if ((e as NodeJS.ErrnoException).code !== "ENOENT") throw e;
169
+ }
170
+ if (tornTail) await this.#sealTornTail();
171
+ for (const identity of quarantinedTerminal) {
172
+ const entry = this.#byIdentity.get(identity);
173
+ if (entry) await this.#append(entry);
174
+ }
175
+ for (const identity of uncertainAfterCorruption) {
176
+ const entry = this.#byIdentity.get(identity);
177
+ if (entry && !terminal(entry.state) && entry.state !== "terminal_uncertain") {
178
+ await this.#append({ ...entry, state: "terminal_uncertain", ts: Date.now() });
179
+ }
180
+ }
181
+ // Effects may have completed after the last durable marker; do not retry them after a restart.
182
+ for (const entry of [...this.#byIdentity.values()]) {
183
+ if (entry.state === "effect_started" || entry.state === "awaiting_ready")
184
+ await this.#append({ ...entry, state: "terminal_uncertain", ts: Date.now() });
185
+ }
186
+ return this;
187
+ }
188
+ async #sealTornTail(): Promise<void> {
189
+ const h = await fs.open(this.#file, "a", 0o600);
190
+ try {
191
+ await h.writeFile("\n");
192
+ await h.sync();
193
+ } finally {
194
+ await h.close();
195
+ }
196
+ }
197
+ async #quarantine(line: string): Promise<void> {
198
+ const h = await fs.open(this.#corruptFile, "a", 0o600);
199
+ try {
200
+ await h.writeFile(`${line}\n`);
201
+ await h.sync();
202
+ } finally {
203
+ await h.close();
204
+ }
205
+ this.#warnings.push("Malformed lifecycle ledger entry quarantined");
206
+ }
207
+ get warnings(): readonly string[] {
208
+ return this.#warnings;
209
+ }
210
+ async #append(entry: LifecycleLedgerEntry): Promise<LifecycleLedgerEntry> {
211
+ const h = await fs.open(this.#file, "a", 0o600);
212
+ try {
213
+ await h.writeFile(`${JSON.stringify(entry)}\n`);
214
+ await h.sync();
215
+ } finally {
216
+ await h.close();
217
+ }
218
+ this.#entries.push(entry);
219
+ this.#byIdentity.set(entry.identity, entry);
220
+ return entry;
221
+ }
222
+ async begin(identity: string, requestHash: string): Promise<BeginResult> {
223
+ const prior = this.#byIdentity.get(identity);
224
+ if (!prior)
225
+ return {
226
+ kind: "new",
227
+ entry: await this.#append({
228
+ version: SDK_STATE_VERSION,
229
+ identity,
230
+ requestHash,
231
+ state: "accepted",
232
+ ts: Date.now(),
233
+ }),
234
+ };
235
+ if (prior.requestHash !== requestHash) return { kind: "idempotency_conflict" };
236
+ if (terminal(prior.state)) return { kind: "replay", entry: prior };
237
+ if (prior.state === "terminal_uncertain") return { kind: "terminal_uncertain", entry: prior };
238
+ // An accepted row has no durable side effect. Target serialization makes retrying it safe.
239
+ if (prior.state === "accepted") return { kind: "new", entry: prior };
240
+ return { kind: "in_progress", entry: prior };
241
+ }
242
+ async transition(
243
+ identity: string,
244
+ state: LifecycleState,
245
+ fields: Omit<Partial<LifecycleLedgerEntry>, "identity" | "requestHash" | "state" | "ts"> = {},
246
+ ): Promise<LifecycleLedgerEntry> {
247
+ const previous = this.#byIdentity.get(identity);
248
+ if (!previous) throw new Error("Unknown lifecycle identity");
249
+ const next = { ...previous, ...fields, state, ts: Date.now() };
250
+ if (
251
+ (terminal(state) || state === "terminal_uncertain") &&
252
+ next.response !== undefined &&
253
+ next.responseDigest === undefined
254
+ )
255
+ next.responseDigest = createHash("sha256").update(canonicalJson(next.response)).digest("hex");
256
+ if (next.durableEffects && next.durableEffects.digest === undefined) {
257
+ const { digest: _digest, ...body } = next.durableEffects;
258
+ next.durableEffects = {
259
+ ...body,
260
+ digest: createHash("sha256").update(canonicalJson(body)).digest("hex"),
261
+ };
262
+ }
263
+ return this.#append(next);
264
+ }
265
+ async assertSupportedStateVersions(): Promise<void> {
266
+ let source: string;
267
+ try {
268
+ source = await fs.readFile(this.#file, "utf8");
269
+ } catch (error) {
270
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
271
+ throw error;
272
+ }
273
+ for (const line of source.split("\n")) {
274
+ if (!line) continue;
275
+ try {
276
+ assertSupportedStateVersion(this.#file, JSON.parse(line));
277
+ } catch (error) {
278
+ if (error instanceof Error && "code" in error && error.code === "unsupported_state_version") throw error;
279
+ }
280
+ }
281
+ }
282
+
283
+ get(identity: string): LifecycleLedgerEntry | undefined {
284
+ return this.#byIdentity.get(identity);
285
+ }
286
+ }