@gajae-code/coding-agent 0.10.2 → 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 (465) hide show
  1. package/CHANGELOG.md +50 -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/model-selector.d.ts +1 -0
  54. package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
  55. package/dist/types/modes/components/settings-selector.d.ts +6 -4
  56. package/dist/types/modes/components/welcome.d.ts +1 -1
  57. package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
  58. package/dist/types/modes/index.d.ts +0 -4
  59. package/dist/types/modes/interactive-mode.d.ts +3 -0
  60. package/dist/types/modes/runtime-init.d.ts +2 -2
  61. package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
  62. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
  63. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
  64. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
  65. package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
  66. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +169 -30
  67. package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
  68. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
  69. package/dist/types/modes/types.d.ts +5 -0
  70. package/dist/types/runtime-mcp/client.d.ts +2 -0
  71. package/dist/types/runtime-mcp/config.d.ts +5 -1
  72. package/dist/types/runtime-mcp/manager.d.ts +8 -1
  73. package/dist/types/runtime-mcp/types.d.ts +4 -0
  74. package/dist/types/sdk/acp/adapter.d.ts +67 -0
  75. package/dist/types/sdk/acp/index.d.ts +2 -0
  76. package/dist/types/sdk/broker/broker.d.ts +53 -0
  77. package/dist/types/sdk/broker/discovery.d.ts +30 -0
  78. package/dist/types/sdk/broker/ensure.d.ts +33 -0
  79. package/dist/types/sdk/broker/identity.d.ts +3 -0
  80. package/dist/types/sdk/broker/index.d.ts +8 -0
  81. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
  82. package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
  83. package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
  84. package/dist/types/sdk/broker/runtime.d.ts +33 -0
  85. package/dist/types/sdk/broker/session-index.d.ts +57 -0
  86. package/dist/types/sdk/broker/state-version.d.ts +8 -0
  87. package/dist/types/sdk/broker/transport.d.ts +9 -0
  88. package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
  89. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
  90. package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
  91. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
  92. package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
  93. package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
  94. package/dist/types/sdk/bus/config.d.ts +274 -0
  95. package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
  96. package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
  97. package/dist/types/sdk/bus/discord-daemon.d.ts +45 -0
  98. package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
  99. package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
  100. package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
  101. package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
  102. package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
  103. package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
  104. package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
  105. package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
  106. package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
  107. package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
  108. package/dist/types/sdk/bus/session-control.d.ts +83 -0
  109. package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
  110. package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
  111. package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
  112. package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
  113. package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
  114. package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
  115. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
  116. package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
  117. package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +55 -17
  118. package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
  119. package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
  120. package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
  121. package/dist/types/sdk/cli/index.d.ts +1 -0
  122. package/dist/types/sdk/cli/session-cli.d.ts +19 -0
  123. package/dist/types/sdk/client/client.d.ts +1 -0
  124. package/dist/types/sdk/client/discovery.d.ts +27 -0
  125. package/dist/types/sdk/client/index.d.ts +2 -0
  126. package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
  127. package/dist/types/sdk/host/control/index.d.ts +2 -0
  128. package/dist/types/sdk/host/control/operations.d.ts +65 -0
  129. package/dist/types/sdk/host/events.d.ts +35 -0
  130. package/dist/types/sdk/host/host.d.ts +35 -0
  131. package/dist/types/sdk/host/index.d.ts +4 -0
  132. package/dist/types/sdk/host/query/cursor.d.ts +62 -0
  133. package/dist/types/sdk/host/query/handlers.d.ts +70 -0
  134. package/dist/types/sdk/host/query/index.d.ts +3 -0
  135. package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
  136. package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
  137. package/dist/types/sdk/host/types.d.ts +32 -0
  138. package/dist/types/sdk/index.d.ts +6 -0
  139. package/dist/types/sdk/lifecycle-session.d.ts +14 -0
  140. package/dist/types/sdk/mcp/index.d.ts +1 -0
  141. package/dist/types/sdk/mcp/server.d.ts +37 -0
  142. package/dist/types/sdk/models.d.ts +44 -0
  143. package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
  144. package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
  145. package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
  146. package/dist/types/sdk/startup-capability.d.ts +50 -0
  147. package/dist/types/session/agent-session.d.ts +114 -21
  148. package/dist/types/session/default-model-selection.d.ts +25 -0
  149. package/dist/types/session/fallback-chain-controller.d.ts +52 -0
  150. package/dist/types/session/session-manager.d.ts +58 -1
  151. package/dist/types/setup/credential-auto-import.d.ts +36 -6
  152. package/dist/types/tools/ask-answer-registry.d.ts +1 -1
  153. package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
  154. package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
  155. package/dist/types/tools/browser/launch.d.ts +22 -0
  156. package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
  157. package/dist/types/tools/browser/profile-posture.d.ts +37 -0
  158. package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
  159. package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
  160. package/dist/types/tools/browser/registry.d.ts +19 -0
  161. package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
  162. package/dist/types/tools/index.d.ts +8 -8
  163. package/package.json +34 -13
  164. package/scripts/build-sdk-package-smoke.ts +94 -0
  165. package/scripts/capture-notifications-settings-showcase.ts +435 -0
  166. package/scripts/compile-args.ts +4 -2
  167. package/scripts/g005-daemon-smoke.ts +4 -5
  168. package/scripts/g011-daemon-path-smoke.ts +5 -5
  169. package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
  170. package/scripts/generate-sdk-operation-inventory.ts +867 -0
  171. package/scripts/generate-telegram-baseline-manifest.ts +128 -0
  172. package/scripts/manifest-schema.ts +124 -0
  173. package/scripts/run-test-manifest.ts +153 -0
  174. package/scripts/run-threat-model-check.ts +157 -0
  175. package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
  176. package/scripts/verify-gjc-sdk-rename.ts +90 -0
  177. package/src/append-only-mode.ts +1 -1
  178. package/src/cli/args.ts +43 -12
  179. package/src/cli/auth-gateway-cli.ts +2 -2
  180. package/src/cli/config-cli.ts +2 -1
  181. package/src/cli/daemon-cli.ts +63 -14
  182. package/src/cli/list-models.ts +1 -1
  183. package/src/cli/notify-cli.ts +288 -286
  184. package/src/cli.ts +21 -2
  185. package/src/commands/daemon.ts +50 -6
  186. package/src/commands/harness.ts +93 -36
  187. package/src/commands/launch.ts +5 -2
  188. package/src/commands/mcp-serve.ts +19 -12
  189. package/src/commands/notify.ts +36 -3
  190. package/src/commands/sdk.ts +426 -0
  191. package/src/commit/agentic/agent.ts +1 -0
  192. package/src/config/atomic-yaml-patch.ts +423 -0
  193. package/src/config/model-profile-activation.ts +240 -89
  194. package/src/config/model-profiles.ts +18 -15
  195. package/src/config/model-registry.ts +51 -26
  196. package/src/config/model-resolver.ts +138 -51
  197. package/src/config/model-selector-value.ts +50 -0
  198. package/src/config/models-config-schema.ts +14 -21
  199. package/src/config/settings-schema.ts +128 -10
  200. package/src/config/settings.ts +1120 -158
  201. package/src/coordinator-mcp/model-preset.ts +74 -20
  202. package/src/coordinator-mcp/server.ts +1597 -3052
  203. package/src/daemon/builtin.ts +8 -5
  204. package/src/daemon/control-types.ts +1 -1
  205. package/src/defaults/gjc/skills/team/SKILL.md +4 -4
  206. package/src/discovery/mcp-json.ts +327 -20
  207. package/src/extensibility/custom-tools/types.ts +1 -1
  208. package/src/extensibility/extensions/compact-handler.ts +3 -3
  209. package/src/extensibility/extensions/get-commands-handler.ts +2 -2
  210. package/src/extensibility/extensions/loader.ts +69 -5
  211. package/src/extensibility/extensions/runner.ts +112 -4
  212. package/src/extensibility/extensions/types.ts +176 -17
  213. package/src/extensibility/hooks/types.ts +1 -1
  214. package/src/gjc-runtime/launch-tmux.ts +170 -86
  215. package/src/gjc-runtime/psmux-detect.ts +82 -4
  216. package/src/gjc-runtime/session-state-sidecar.ts +41 -31
  217. package/src/gjc-runtime/team-runtime.ts +54 -35
  218. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
  219. package/src/gjc-runtime/tmux-owner-isolation.ts +1 -1
  220. package/src/gjc-runtime/tmux-sessions.ts +19 -13
  221. package/src/gjc-runtime/ultragoal-guard.ts +53 -6
  222. package/src/gjc-runtime/ultragoal-runtime.ts +155 -3
  223. package/src/gjc-runtime/windows-powershell-command.ts +58 -0
  224. package/src/harness-control-plane/operate.ts +9 -9
  225. package/src/harness-control-plane/owner.ts +283 -39
  226. package/src/harness-control-plane/receipts.ts +0 -1
  227. package/src/harness-control-plane/sdk-transport.ts +395 -0
  228. package/src/harness-control-plane/session-transport.ts +94 -0
  229. package/src/harness-control-plane/types.ts +1 -1
  230. package/src/internal-urls/docs-index.generated.ts +34 -35
  231. package/src/lsp/client.ts +42 -6
  232. package/src/lsp/index.ts +2 -2
  233. package/src/main.ts +299 -204
  234. package/src/modes/DESIGN.md +258 -0
  235. package/src/modes/acp/acp-agent.ts +1228 -2612
  236. package/src/modes/acp/acp-client-bridge.ts +2 -20
  237. package/src/modes/acp/acp-event-mapper.ts +18 -2
  238. package/src/modes/acp/acp-mode.ts +8 -36
  239. package/src/modes/acp/permission-mode.ts +25 -0
  240. package/src/modes/acp/startup-options.ts +6 -0
  241. package/src/modes/components/agent-dashboard.ts +37 -17
  242. package/src/modes/components/model-selector.ts +93 -35
  243. package/src/modes/components/notifications-settings-editor.ts +1379 -0
  244. package/src/modes/components/settings-selector.ts +60 -11
  245. package/src/modes/components/welcome.ts +4 -4
  246. package/src/modes/controllers/command-controller.ts +1 -2
  247. package/src/modes/controllers/event-controller.ts +11 -16
  248. package/src/modes/controllers/extension-ui-controller.ts +348 -4
  249. package/src/modes/controllers/selector-controller.ts +674 -36
  250. package/src/modes/index.ts +0 -22
  251. package/src/modes/interactive-mode.ts +59 -20
  252. package/src/modes/runtime-init.ts +298 -6
  253. package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
  254. package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
  255. package/src/modes/shared/agent-wire/event-contract.ts +7 -8
  256. package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
  257. package/src/modes/shared/agent-wire/event-observation.ts +16 -7
  258. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1254 -87
  259. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
  260. package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
  261. package/src/modes/types.ts +5 -0
  262. package/src/runtime-mcp/client.ts +94 -19
  263. package/src/runtime-mcp/config.ts +27 -15
  264. package/src/runtime-mcp/manager.ts +343 -105
  265. package/src/runtime-mcp/oauth-flow.ts +42 -12
  266. package/src/runtime-mcp/transports/http.ts +49 -19
  267. package/src/runtime-mcp/transports/stdio.ts +47 -28
  268. package/src/runtime-mcp/types.ts +10 -0
  269. package/src/sdk/acp/adapter.ts +418 -0
  270. package/src/sdk/acp/index.ts +2 -0
  271. package/src/sdk/broker/broker.ts +642 -0
  272. package/src/sdk/broker/discovery.ts +94 -0
  273. package/src/sdk/broker/ensure.ts +249 -0
  274. package/src/sdk/broker/identity.ts +42 -0
  275. package/src/sdk/broker/index.ts +8 -0
  276. package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
  277. package/src/sdk/broker/internal-source.bunfig.toml +1 -0
  278. package/src/sdk/broker/lifecycle-ledger.ts +286 -0
  279. package/src/sdk/broker/lifecycle.ts +2157 -0
  280. package/src/sdk/broker/process-incarnation.ts +159 -0
  281. package/src/sdk/broker/runtime.ts +164 -0
  282. package/src/sdk/broker/session-index.ts +318 -0
  283. package/src/sdk/broker/state-version.ts +23 -0
  284. package/src/sdk/broker/transport.ts +125 -0
  285. package/src/sdk/bus/chat-command-policy.ts +107 -0
  286. package/src/sdk/bus/chat-daemon-cli.ts +214 -0
  287. package/src/sdk/bus/chat-daemon-control.ts +713 -0
  288. package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
  289. package/src/sdk/bus/chat-effect-journal.ts +353 -0
  290. package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
  291. package/src/sdk/bus/config.ts +433 -0
  292. package/src/sdk/bus/conversation-store.ts +391 -0
  293. package/src/sdk/bus/discord-conversation.ts +85 -0
  294. package/src/sdk/bus/discord-daemon.ts +2275 -0
  295. package/src/sdk/bus/discord-live-provider.ts +521 -0
  296. package/src/sdk/bus/discord-provider.ts +56 -0
  297. package/src/{notifications → sdk/bus}/html-format.ts +2 -2
  298. package/src/sdk/bus/index.ts +3943 -0
  299. package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
  300. package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
  301. package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
  302. package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
  303. package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
  304. package/src/sdk/bus/natives-augment.d.ts +38 -0
  305. package/src/sdk/bus/notification-orchestration.ts +535 -0
  306. package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
  307. package/src/sdk/bus/session-control.ts +333 -0
  308. package/src/sdk/bus/slack-conversation.ts +74 -0
  309. package/src/sdk/bus/slack-daemon.ts +2144 -0
  310. package/src/sdk/bus/slack-live-provider.ts +380 -0
  311. package/src/sdk/bus/slack-provider.ts +76 -0
  312. package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
  313. package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
  314. package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
  315. package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
  316. package/src/{notifications → sdk/bus}/telegram-daemon.ts +502 -54
  317. package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
  318. package/src/sdk/bus/telegram-setup.ts +663 -0
  319. package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
  320. package/src/sdk/cli/index.ts +1 -0
  321. package/src/sdk/cli/session-cli.ts +275 -0
  322. package/src/sdk/client/client.ts +1 -0
  323. package/src/sdk/client/discovery.ts +108 -0
  324. package/src/sdk/client/index.ts +2 -0
  325. package/src/sdk/host/control/dispatch.ts +367 -0
  326. package/src/sdk/host/control/index.ts +10 -0
  327. package/src/sdk/host/control/operations.ts +63 -0
  328. package/src/sdk/host/events.ts +91 -0
  329. package/src/sdk/host/host.ts +296 -0
  330. package/src/sdk/host/index.ts +4 -0
  331. package/src/sdk/host/query/cursor.ts +189 -0
  332. package/src/sdk/host/query/handlers.ts +533 -0
  333. package/src/sdk/host/query/index.ts +3 -0
  334. package/src/sdk/host/query/revision-store.ts +1126 -0
  335. package/src/sdk/host/reverse-leases.ts +325 -0
  336. package/src/sdk/host/types.ts +29 -0
  337. package/src/sdk/index.ts +13 -0
  338. package/src/sdk/lifecycle-session.ts +39 -0
  339. package/src/sdk/mcp/index.ts +1 -0
  340. package/src/sdk/mcp/server.ts +332 -0
  341. package/src/sdk/models.ts +168 -0
  342. package/src/sdk/protocol/adapter-validation.ts +48 -0
  343. package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
  344. package/src/sdk/protocol/operation-registry.ts +312 -0
  345. package/src/{sdk.ts → sdk/session.ts} +339 -212
  346. package/src/sdk/startup-capability.ts +189 -0
  347. package/src/session/agent-session.ts +2453 -894
  348. package/src/session/default-model-selection.ts +93 -0
  349. package/src/session/fallback-chain-controller.ts +136 -0
  350. package/src/session/session-manager.ts +438 -11
  351. package/src/session/session-storage.ts +8 -1
  352. package/src/setup/credential-auto-import.ts +290 -35
  353. package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
  354. package/src/slash-commands/builtin-registry.ts +2 -1
  355. package/src/task/executor.ts +62 -13
  356. package/src/tools/ask-answer-registry.ts +1 -1
  357. package/src/tools/ask.ts +13 -8
  358. package/src/tools/browser/STEALTH-THIRD-PARTY-NOTICES.md +26 -0
  359. package/src/tools/browser/benchmark/detector-report.ts +157 -0
  360. package/src/tools/browser/benchmark/run-suite.ts +39 -0
  361. package/src/tools/browser/launch.ts +124 -19
  362. package/src/tools/browser/profile-discovery.ts +63 -0
  363. package/src/tools/browser/profile-posture.ts +59 -0
  364. package/src/tools/browser/profile-reuse.ts +102 -0
  365. package/src/tools/browser/profile-warmup.ts +109 -0
  366. package/src/tools/browser/registry.ts +87 -11
  367. package/src/tools/browser/tab-protocol.ts +1 -0
  368. package/src/tools/browser/tab-supervisor.ts +1 -0
  369. package/src/tools/browser/tab-worker.ts +1 -1
  370. package/src/tools/browser.ts +6 -0
  371. package/src/tools/index.ts +8 -8
  372. package/src/tools/puppeteer/14_stealth_permissions.txt +57 -0
  373. package/src/tools/puppeteer/15_stealth_webrtc.txt +60 -0
  374. package/src/tools/telegram-send.ts +2 -2
  375. package/vendor/insane-search/MANIFEST.json +2 -1
  376. package/vendor/insane-search/engine/bias_check.py +4 -1
  377. package/vendor/insane-search/engine/phase0.py +26 -1
  378. package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
  379. package/dist/types/modes/bridge/auth.d.ts +0 -12
  380. package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
  381. package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
  382. package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
  383. package/dist/types/modes/bridge/event-stream.d.ts +0 -8
  384. package/dist/types/modes/rpc/host-tools.d.ts +0 -1
  385. package/dist/types/modes/rpc/host-uris.d.ts +0 -1
  386. package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
  387. package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
  388. package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
  389. package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
  390. package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
  391. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
  392. package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
  393. package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
  394. package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
  395. package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
  396. package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
  397. package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
  398. package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
  399. package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
  400. package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
  401. package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
  402. package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
  403. package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
  404. package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
  405. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
  406. package/dist/types/notifications/config.d.ts +0 -132
  407. package/src/harness-control-plane/rpc-adapter.ts +0 -290
  408. package/src/modes/bridge/auth.ts +0 -41
  409. package/src/modes/bridge/bridge-client-bridge.ts +0 -47
  410. package/src/modes/bridge/bridge-mode.ts +0 -675
  411. package/src/modes/bridge/bridge-ui-context.ts +0 -200
  412. package/src/modes/bridge/event-stream.ts +0 -70
  413. package/src/modes/rpc/host-tools.ts +0 -1
  414. package/src/modes/rpc/host-uris.ts +0 -1
  415. package/src/modes/rpc/rpc-client.ts +0 -1166
  416. package/src/modes/rpc/rpc-mode.ts +0 -1029
  417. package/src/modes/rpc/rpc-socket-security.ts +0 -115
  418. package/src/modes/rpc/rpc-types.ts +0 -637
  419. package/src/modes/shared/agent-wire/command-contract.ts +0 -18
  420. package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
  421. package/src/modes/shared/agent-wire/command-validation.ts +0 -181
  422. package/src/modes/shared/agent-wire/handshake.ts +0 -157
  423. package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
  424. package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
  425. package/src/modes/shared/agent-wire/protocol.ts +0 -46
  426. package/src/modes/shared/agent-wire/responses.ts +0 -17
  427. package/src/modes/shared/agent-wire/scopes.ts +0 -94
  428. package/src/modes/shared/agent-wire/session-registry.ts +0 -109
  429. package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
  430. package/src/modes/shared/agent-wire/ui-result.ts +0 -48
  431. package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
  432. package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
  433. package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
  434. package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
  435. package/src/notifications/config.ts +0 -219
  436. package/src/notifications/index.ts +0 -1835
  437. package/src/notifications/telegram-daemon-cli.ts +0 -243
  438. package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
  439. /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
  440. /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
  441. /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
  442. /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
  443. /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
  444. /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
  445. /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
  446. /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
  447. /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
  448. /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
  449. /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
  450. /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
  451. /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
  452. /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
  453. /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
  454. /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
  455. /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
  456. /package/src/{notifications → sdk/bus}/engine.ts +0 -0
  457. /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
  458. /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
  459. /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
  460. /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
  461. /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
  462. /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
  463. /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
  464. /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
  465. /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
@@ -0,0 +1,642 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import * as fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { resolveResumableSession, SessionManager } from "../../session/session-manager";
5
+ import {
6
+ BROKER_HEARTBEAT_TTL_MS,
7
+ type BrokerDiscovery,
8
+ brokerDiscoveryPath,
9
+ brokerProcessIncarnation,
10
+ isPidAlive,
11
+ newBrokerToken,
12
+ readBrokerDiscovery,
13
+ redactBrokerDiscovery,
14
+ writeBrokerDiscovery,
15
+ } from "./discovery";
16
+ import { deriveIdempotencyIdentity } from "./identity";
17
+ import { executeLifecycle, isCanonicalSessionId } from "./lifecycle";
18
+
19
+ import {
20
+ type LifecycleDurableEffectsReceipt,
21
+ LifecycleLedger,
22
+ type LifecycleStartupFailureReceipt,
23
+ } from "./lifecycle-ledger";
24
+ import { type IndexedSession, SessionIndex } from "./session-index";
25
+ import { BrokerTransport } from "./transport";
26
+
27
+ export interface BrokerSettings {
28
+ agentDir: string;
29
+ packageGeneration?: string;
30
+ port?: number;
31
+ heartbeatTtlMs?: number;
32
+ }
33
+ export type BrokerErrorCode =
34
+ | "idempotency_conflict"
35
+ | "terminal_uncertain"
36
+ | "broker_restarting"
37
+ | "unavailable"
38
+ | "endpoint_stale"
39
+ | "resource_gone"
40
+ | "invalid_input"
41
+ | "spawn_failed"
42
+ | "readiness_timeout"
43
+ | "close_refused"
44
+ | "not_found"
45
+ | "live_session"
46
+ | "cleanup_pending"
47
+ | (string & {});
48
+
49
+ export type BrokerCleanupEvidence = {
50
+ phase: "artifacts" | "transcript" | "metadata";
51
+ artifactsIdentity?: { dev: string; ino: string };
52
+ transcriptIdentity?: { dev: string; ino: string };
53
+ };
54
+ export type BrokerResponse =
55
+ | { ok: true; result?: unknown; indexSeq?: number }
56
+ | {
57
+ ok: false;
58
+ error: {
59
+ code: BrokerErrorCode;
60
+ message: string;
61
+ endpoint?: "unavailable";
62
+ cleanup?: BrokerCleanupEvidence;
63
+ };
64
+ indexSeq?: number;
65
+ durableEffects?: LifecycleDurableEffectsReceipt;
66
+ startupFailure?: LifecycleStartupFailureReceipt;
67
+ };
68
+ const error = (code: BrokerErrorCode, message: string): BrokerResponse => ({ ok: false, error: { code, message } });
69
+
70
+ type InputNormalization = { input: Record<string, unknown> } | BrokerResponse;
71
+
72
+ function isBrokerResponse(value: InputNormalization): value is BrokerResponse {
73
+ return "ok" in value;
74
+ }
75
+
76
+ function normalizeAliasedString(
77
+ input: Record<string, unknown>,
78
+ canonical: string,
79
+ aliases: readonly string[],
80
+ normalize = (value: string) => value,
81
+ ): { value: string | undefined; error?: string } {
82
+ const supplied = [canonical, ...aliases].filter(name => input[name] !== undefined).map(name => input[name]);
83
+ if (supplied.length === 0) return { value: undefined };
84
+ if (supplied.some(value => typeof value !== "string" || value.length === 0))
85
+ return { value: undefined, error: `${canonical} must be a non-empty string` };
86
+ const values = supplied.map(value => normalize(value as string));
87
+ if (values.some(value => value !== values[0])) return { value: undefined, error: `${canonical} aliases conflict` };
88
+ return { value: values[0] };
89
+ }
90
+
91
+ function normalizeBrokerInput(operation: string, input: Record<string, unknown>): InputNormalization {
92
+ const normalized: Record<string, unknown> = { ...input };
93
+ const session = normalizeAliasedString(input, "sessionId", ["id"]);
94
+ if (session.error) return error("invalid_input", session.error);
95
+ if (session.value !== undefined) {
96
+ if (!isCanonicalSessionId(session.value))
97
+ return error("invalid_input", "sessionId must be a canonical safe identifier");
98
+ normalized.sessionId = session.value;
99
+ delete normalized.id;
100
+ }
101
+ const source = normalizeAliasedString(input, "sourceSessionId", ["sourceId"]);
102
+ if (source.error) return error("invalid_input", source.error);
103
+ if (source.value !== undefined) {
104
+ if (!isCanonicalSessionId(source.value))
105
+ return error("invalid_input", "sourceSessionId must be a canonical safe identifier");
106
+ normalized.sourceSessionId = source.value;
107
+ delete normalized.sourceId;
108
+ }
109
+
110
+ if (operation === "session.list") {
111
+ const resolved = input.resolveSessionId;
112
+ if (resolved !== undefined && (typeof resolved !== "string" || !isCanonicalSessionId(resolved)))
113
+ return error("invalid_input", "resolveSessionId must be a canonical safe identifier");
114
+ return { input: normalized };
115
+ }
116
+ if (
117
+ operation !== "session.create" &&
118
+ operation !== "session.fork" &&
119
+ operation !== "session.resume" &&
120
+ operation !== "session.close" &&
121
+ operation !== "session.delete"
122
+ )
123
+ return { input: normalized };
124
+
125
+ const target =
126
+ typeof input.target === "object" && input.target !== null && !Array.isArray(input.target)
127
+ ? (input.target as Record<string, unknown>)
128
+ : undefined;
129
+ const cwd = normalizeAliasedString(
130
+ { cwd: input.cwd, path: input.path, targetPath: target?.path },
131
+ "cwd",
132
+ ["path", "targetPath"],
133
+ value => path.resolve(value),
134
+ );
135
+ if (cwd.error) return error("invalid_input", cwd.error);
136
+ if (cwd.value !== undefined) {
137
+ normalized.cwd = cwd.value;
138
+ delete normalized.path;
139
+ }
140
+ const stateRoot = normalizeAliasedString(
141
+ { stateRoot: input.stateRoot, targetStateRoot: target?.stateRoot },
142
+ "stateRoot",
143
+ ["targetStateRoot"],
144
+ value => path.resolve(value),
145
+ );
146
+ if (stateRoot.error) return error("invalid_input", stateRoot.error);
147
+ if (stateRoot.value !== undefined && (!cwd.value || stateRoot.value !== path.join(cwd.value, ".gjc", "state")))
148
+ return error("invalid_input", "stateRoot must be the default .gjc/state for cwd.");
149
+ if (cwd.value !== undefined) normalized.stateRoot = path.join(cwd.value, ".gjc", "state");
150
+ else if (stateRoot.value !== undefined) return error("invalid_input", "stateRoot requires cwd.");
151
+
152
+ if (target) {
153
+ const normalizedTarget = { ...target };
154
+ delete normalizedTarget.path;
155
+ delete normalizedTarget.stateRoot;
156
+ if (Object.keys(normalizedTarget).length > 0) normalized.target = normalizedTarget;
157
+ else delete normalized.target;
158
+ }
159
+ return { input: normalized };
160
+ }
161
+ function canonicalJson(value: unknown): string {
162
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
163
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
164
+ const record = value as Record<string, unknown>;
165
+ return `{${Object.keys(record)
166
+ .sort()
167
+ .map(key => `${JSON.stringify(key)}:${canonicalJson(record[key])}`)
168
+ .join(",")}}`;
169
+ }
170
+
171
+ type EndpointAuthority = { endpointGeneration?: number; endpointIncarnation?: string };
172
+ function endpointIncarnation(
173
+ record: Pick<IndexedSession, "endpointGeneration" | "endpointMtimeMs" | "pid">,
174
+ sessionId: string,
175
+ ): string | undefined {
176
+ if (
177
+ !Number.isSafeInteger(record.endpointGeneration) ||
178
+ record.endpointGeneration <= 0 ||
179
+ !Number.isSafeInteger(record.pid) ||
180
+ record.pid <= 0 ||
181
+ typeof record.endpointMtimeMs !== "number" ||
182
+ !Number.isFinite(record.endpointMtimeMs) ||
183
+ record.endpointMtimeMs <= 0
184
+ )
185
+ return undefined;
186
+ return createHash("sha256")
187
+ .update(
188
+ canonicalJson({
189
+ endpointGeneration: record.endpointGeneration,
190
+ endpointMtimeMs: record.endpointMtimeMs,
191
+ pid: record.pid,
192
+ sessionId,
193
+ }),
194
+ )
195
+ .digest("hex");
196
+ }
197
+ function expectedEndpointAuthority(input: Record<string, unknown>): EndpointAuthority | BrokerResponse {
198
+ const endpointGeneration = input.endpointGeneration;
199
+ const endpointIncarnation = input.endpointIncarnation;
200
+ if (
201
+ endpointGeneration !== undefined &&
202
+ (typeof endpointGeneration !== "number" || !Number.isSafeInteger(endpointGeneration) || endpointGeneration <= 0)
203
+ )
204
+ return error("invalid_input", "endpointGeneration must be a positive safe integer");
205
+ if (
206
+ endpointIncarnation !== undefined &&
207
+ (typeof endpointIncarnation !== "string" || !/^[a-f0-9]{64}$/.test(endpointIncarnation))
208
+ )
209
+ return error("invalid_input", "endpointIncarnation must be a SHA-256 hash");
210
+ if (endpointIncarnation !== undefined && endpointGeneration === undefined)
211
+ return error("invalid_input", "endpointIncarnation requires endpointGeneration");
212
+ return { endpointGeneration, endpointIncarnation };
213
+ }
214
+ function matchesEndpointAuthority(record: IndexedSession, authority: EndpointAuthority): boolean {
215
+ return (
216
+ (authority.endpointGeneration === undefined || authority.endpointGeneration === record.endpointGeneration) &&
217
+ (authority.endpointIncarnation === undefined ||
218
+ authority.endpointIncarnation === endpointIncarnation(record, record.sessionId))
219
+ );
220
+ }
221
+ function sameEndpointRecord(expected: IndexedSession, current: IndexedSession): boolean {
222
+ return (
223
+ current.live &&
224
+ current.endpointGeneration === expected.endpointGeneration &&
225
+ current.pid === expected.pid &&
226
+ current.endpointMtimeMs === expected.endpointMtimeMs &&
227
+ path.resolve(current.locator.repo) === path.resolve(expected.locator.repo) &&
228
+ path.resolve(current.locator.stateRoot) === path.resolve(expected.locator.stateRoot)
229
+ );
230
+ }
231
+
232
+ function lifecycleTarget(operation: string, input: Record<string, unknown>): unknown {
233
+ const target = input.target as Record<string, unknown> | undefined;
234
+ const string = (...values: unknown[]): string | undefined =>
235
+ values.find((value): value is string => typeof value === "string" && value.length > 0);
236
+ const explicitRoot = string(input.stateRoot, target?.stateRoot);
237
+ const root =
238
+ explicitRoot ??
239
+ (() => {
240
+ const cwd = string(input.cwd, input.path, target?.path);
241
+ return cwd ? path.join(cwd, ".gjc", "state") : undefined;
242
+ })();
243
+ const id = string(input.sessionId, input.id);
244
+ switch (operation) {
245
+ case "session.create":
246
+ return { root };
247
+ case "session.fork":
248
+ return {
249
+ root,
250
+ sourceSessionId: string(input.sourceSessionId, input.sourceId),
251
+ sourceSessionPath: string(input.sourceSessionPath, input.sourcePath, input.sessionPath),
252
+ };
253
+ case "session.resume":
254
+ case "session.close":
255
+ case "session.delete":
256
+ return { sessionId: id };
257
+ default:
258
+ return { operation, root, sessionId: id };
259
+ }
260
+ }
261
+
262
+ const BROKER_LOCK_RECORD = "owner.json";
263
+ const BROKER_LOCK_STARTUP_WAIT_MS = 1_000;
264
+ const BROKER_LOCK_RETRY_MS = 10;
265
+
266
+ type BrokerLockSnapshot = {
267
+ ownerId?: string;
268
+ pid: number;
269
+ identity: string;
270
+ };
271
+
272
+ const terminalPersistenceHooksForTest = new WeakMap<Broker, () => void>();
273
+
274
+ export class Broker {
275
+ readonly settings: Required<BrokerSettings>;
276
+ readonly index: SessionIndex;
277
+ readonly ledger: LifecycleLedger;
278
+ discovery: BrokerDiscovery | null = null;
279
+ #lock: string;
280
+ #owner = randomBytes(12).toString("hex");
281
+ #chains = new Map<string, Promise<void>>();
282
+ #stopping = false;
283
+ #transport: BrokerTransport | null = null;
284
+ #heartbeatTimer: ReturnType<typeof setInterval> | null = null;
285
+ #heartbeatWrite: Promise<void> = Promise.resolve();
286
+ constructor(settings: BrokerSettings) {
287
+ this.settings = { packageGeneration: "unknown", port: 0, heartbeatTtlMs: BROKER_HEARTBEAT_TTL_MS, ...settings };
288
+ this.index = new SessionIndex(settings.agentDir);
289
+ this.ledger = new LifecycleLedger(settings.agentDir);
290
+ this.#lock = path.join(settings.agentDir, "sdk", "broker.lock");
291
+ }
292
+ #lockRecordPath(): string {
293
+ return path.join(this.#lock, BROKER_LOCK_RECORD);
294
+ }
295
+ #lockSnapshot(raw: string): BrokerLockSnapshot {
296
+ try {
297
+ const lock = JSON.parse(raw) as { ownerId?: unknown; pid?: unknown };
298
+ if (
299
+ typeof lock.ownerId === "string" &&
300
+ lock.ownerId.length > 0 &&
301
+ typeof lock.pid === "number" &&
302
+ Number.isInteger(lock.pid) &&
303
+ lock.pid > 0
304
+ )
305
+ return { ownerId: lock.ownerId, pid: lock.pid, identity: `owner:${lock.ownerId}` };
306
+ } catch {}
307
+ return { pid: 0, identity: `contents:${createHash("sha256").update(raw).digest("hex")}` };
308
+ }
309
+ async #readLock(): Promise<BrokerLockSnapshot | null> {
310
+ try {
311
+ return this.#lockSnapshot(await fs.readFile(this.#lockRecordPath(), "utf8"));
312
+ } catch (e) {
313
+ const code = (e as NodeJS.ErrnoException).code;
314
+ if (code === "ENOTDIR") {
315
+ try {
316
+ return this.#lockSnapshot(await fs.readFile(this.#lock, "utf8"));
317
+ } catch (legacyError) {
318
+ if ((legacyError as NodeJS.ErrnoException).code === "ENOENT") return null;
319
+ throw legacyError;
320
+ }
321
+ }
322
+ if (code !== "ENOENT") throw e;
323
+ }
324
+ try {
325
+ const lock = await fs.stat(this.#lock);
326
+ return lock.isDirectory() ? { pid: 0, identity: `directory:${lock.dev}:${lock.ino}` } : null;
327
+ } catch (e) {
328
+ if ((e as NodeJS.ErrnoException).code === "ENOENT") return null;
329
+ throw e;
330
+ }
331
+ }
332
+ async #createLock(): Promise<void> {
333
+ await fs.mkdir(this.#lock, { mode: 0o700 });
334
+ try {
335
+ await fs.writeFile(
336
+ this.#lockRecordPath(),
337
+ JSON.stringify({ version: 1, ownerId: this.#owner, pid: process.pid, acquiredAt: Date.now() }),
338
+ { flag: "wx", mode: 0o600 },
339
+ );
340
+ } catch (e) {
341
+ try {
342
+ await fs.rmdir(this.#lock);
343
+ } catch {}
344
+ throw e;
345
+ }
346
+ }
347
+ async #waitForBrokerDiscovery(): Promise<BrokerDiscovery | null> {
348
+ const deadline = Date.now() + BROKER_LOCK_STARTUP_WAIT_MS;
349
+ while (Date.now() < deadline) {
350
+ const live = await readBrokerDiscovery(this.settings.agentDir, this.settings.heartbeatTtlMs);
351
+ if (live) return live;
352
+ await Bun.sleep(BROKER_LOCK_RETRY_MS);
353
+ }
354
+ return readBrokerDiscovery(this.settings.agentDir, this.settings.heartbeatTtlMs);
355
+ }
356
+ async #reclaimStaleLock(snapshot: BrokerLockSnapshot): Promise<void> {
357
+ const current = await this.#readLock();
358
+ if (!current || current.identity !== snapshot.identity || (current.pid > 0 && isPidAlive(current.pid))) return;
359
+
360
+ // Keep the tombstone: its immutable-owner-derived pathname prevents a contender
361
+ // holding an old snapshot from renaming a newly-created directory lock.
362
+ const tombstone = path.join(
363
+ path.dirname(this.#lock),
364
+ `.broker.lock.stale-${createHash("sha256").update(snapshot.identity).digest("hex")}`,
365
+ );
366
+ try {
367
+ await fs.rename(this.#lock, tombstone);
368
+ } catch (e) {
369
+ const code = (e as NodeJS.ErrnoException).code;
370
+ if (["ENOENT", "EEXIST", "ENOTEMPTY", "EISDIR", "ENOTDIR"].includes(code ?? "")) return;
371
+ if (code === "EPERM") {
372
+ try {
373
+ await fs.lstat(tombstone);
374
+ return;
375
+ } catch (statError) {
376
+ if ((statError as NodeJS.ErrnoException).code !== "ENOENT") throw statError;
377
+ }
378
+ }
379
+ throw e;
380
+ }
381
+ }
382
+ async start(): Promise<BrokerDiscovery> {
383
+ this.#stopping = false;
384
+ await Promise.all([
385
+ this.index.assertSupportedStateVersions(),
386
+ this.ledger.assertSupportedStateVersions(),
387
+ readBrokerDiscovery(this.settings.agentDir),
388
+ ]);
389
+ await fs.mkdir(path.dirname(this.#lock), { recursive: true, mode: 0o700 });
390
+ for (;;) {
391
+ try {
392
+ await this.#createLock();
393
+ break;
394
+ } catch (e) {
395
+ if ((e as NodeJS.ErrnoException).code !== "EEXIST") throw e;
396
+ }
397
+
398
+ const live = await readBrokerDiscovery(this.settings.agentDir, this.settings.heartbeatTtlMs);
399
+ if (live) {
400
+ this.discovery = live;
401
+ return live;
402
+ }
403
+ const snapshot = await this.#readLock();
404
+ if (!snapshot) continue;
405
+ if (snapshot.pid > 0 && isPidAlive(snapshot.pid)) {
406
+ const starting = await this.#waitForBrokerDiscovery();
407
+ if (starting) {
408
+ this.discovery = starting;
409
+ return starting;
410
+ }
411
+ const current = await this.#readLock();
412
+ if (current && current.identity === snapshot.identity && current.pid > 0 && isPidAlive(current.pid))
413
+ throw new Error("Broker lock is held by a live owner");
414
+ continue;
415
+ }
416
+ await this.#reclaimStaleLock(snapshot);
417
+ }
418
+ await this.index.open();
419
+ await this.ledger.open();
420
+ const now = Date.now();
421
+ const incarnation = brokerProcessIncarnation(process.pid);
422
+ if (!incarnation) throw new Error("Broker process incarnation is unavailable.");
423
+ const token = newBrokerToken();
424
+ this.#transport = new BrokerTransport(this, token, this.settings.port);
425
+ const port = await this.#transport.start();
426
+ this.discovery = {
427
+ version: 1,
428
+ protocolVersion: 3,
429
+ packageGeneration: this.settings.packageGeneration,
430
+ ownerId: this.#owner,
431
+ pid: process.pid,
432
+ incarnation,
433
+ host: "127.0.0.1",
434
+ port,
435
+ url: `ws://127.0.0.1:${port}`,
436
+ token,
437
+ startedAt: now,
438
+ heartbeatAt: now,
439
+ };
440
+ await writeBrokerDiscovery(this.settings.agentDir, this.discovery);
441
+ this.#heartbeatTimer = setInterval(
442
+ () => void this.heartbeat(),
443
+ Math.max(1, Math.floor(this.settings.heartbeatTtlMs / 3)),
444
+ );
445
+ return this.discovery;
446
+ }
447
+ get ownsDiscovery(): boolean {
448
+ return this.discovery?.ownerId === this.#owner;
449
+ }
450
+ status(): ReturnType<typeof redactBrokerDiscovery> | null {
451
+ return this.discovery ? redactBrokerDiscovery(this.discovery) : null;
452
+ }
453
+ async heartbeat(): Promise<void> {
454
+ if (!this.discovery || this.discovery.ownerId !== this.#owner) return;
455
+ this.discovery = { ...this.discovery, heartbeatAt: Date.now() };
456
+ const discovery = this.discovery;
457
+ this.#heartbeatWrite = this.#heartbeatWrite.then(() => writeBrokerDiscovery(this.settings.agentDir, discovery));
458
+ await this.#heartbeatWrite;
459
+ }
460
+ async stop(): Promise<void> {
461
+ this.#stopping = true;
462
+ if (this.#heartbeatTimer) {
463
+ clearInterval(this.#heartbeatTimer);
464
+ this.#heartbeatTimer = null;
465
+ }
466
+ await this.#heartbeatWrite;
467
+ await Promise.allSettled(this.#chains.values());
468
+ await this.#transport?.stop();
469
+ this.#transport = null;
470
+ if (this.discovery?.ownerId === this.#owner) {
471
+ try {
472
+ const disk = JSON.parse(await fs.readFile(brokerDiscoveryPath(this.settings.agentDir), "utf8")) as {
473
+ ownerId?: string;
474
+ };
475
+ if (disk.ownerId === this.#owner) await fs.unlink(brokerDiscoveryPath(this.settings.agentDir));
476
+ } catch (e) {
477
+ if ((e as NodeJS.ErrnoException).code !== "ENOENT") throw e;
478
+ }
479
+ try {
480
+ const lock = await this.#readLock();
481
+ if (lock?.ownerId === this.#owner) {
482
+ await fs.unlink(this.#lockRecordPath());
483
+ await fs.rmdir(this.#lock);
484
+ }
485
+ } catch (e) {
486
+ if ((e as NodeJS.ErrnoException).code !== "ENOENT") throw e;
487
+ }
488
+ }
489
+ this.discovery = null;
490
+ }
491
+ async #endpoint(input: Record<string, unknown>): Promise<BrokerResponse> {
492
+ const sessionId = input.sessionId;
493
+ if (typeof sessionId !== "string" || !isCanonicalSessionId(sessionId))
494
+ return error("invalid_input", "sessionId must be a canonical safe identifier");
495
+ const authority = expectedEndpointAuthority(input);
496
+ if ("ok" in authority) return authority;
497
+ await this.index.refresh();
498
+ const record = this.index.listSessions().sessions.find(session => session.sessionId === sessionId);
499
+ if (!record) return error("resource_gone", "session is not indexed");
500
+ if (!record.live || !matchesEndpointAuthority(record, authority))
501
+ return error("endpoint_stale", "session endpoint is stale");
502
+ return this.#readEndpoint(record, authority);
503
+ }
504
+ async #readEndpoint(record: IndexedSession, authority: EndpointAuthority): Promise<BrokerResponse> {
505
+ if (!isCanonicalSessionId(record.sessionId))
506
+ return error("invalid_input", "indexed sessionId is not a canonical safe identifier");
507
+
508
+ try {
509
+ const endpointPath = path.join(record.locator.stateRoot, "sdk", `${record.sessionId}.json`);
510
+ const [source, metadata] = await Promise.all([fs.readFile(endpointPath, "utf8"), fs.stat(endpointPath)]);
511
+ const endpoint = JSON.parse(source) as Record<string, unknown>;
512
+ if (
513
+ endpoint.sessionId !== record.sessionId ||
514
+ endpoint.pid !== record.pid ||
515
+ endpoint.stale === true ||
516
+ record.endpointMtimeMs === undefined ||
517
+ metadata.mtimeMs !== record.endpointMtimeMs
518
+ )
519
+ return error("endpoint_stale", "session endpoint is stale");
520
+ await this.index.refresh();
521
+ const current = this.index.listSessions().sessions.find(session => session.sessionId === record.sessionId);
522
+ if (!current || !sameEndpointRecord(record, current) || !matchesEndpointAuthority(current, authority))
523
+ return error("endpoint_stale", "session endpoint is stale");
524
+ return { ok: true, result: endpoint };
525
+ } catch (e) {
526
+ if ((e as NodeJS.ErrnoException).code === "ENOENT")
527
+ return error("resource_gone", "session endpoint record is gone");
528
+ throw e;
529
+ }
530
+ }
531
+ async handleRequest(
532
+ operation: string,
533
+ input: Record<string, unknown>,
534
+ idempotencyKey?: string,
535
+ ): Promise<BrokerResponse> {
536
+ if (this.#stopping) return error("broker_restarting", "broker is stopping");
537
+ const normalization = normalizeBrokerInput(operation, input);
538
+ if (isBrokerResponse(normalization)) return normalization;
539
+ input = normalization.input;
540
+ if (operation === "session.list") {
541
+ await this.index.refresh();
542
+ const result = this.index.listSessions();
543
+ const resolveSessionId = typeof input.resolveSessionId === "string" ? input.resolveSessionId : undefined;
544
+ const cwd = typeof input.cwd === "string" ? input.cwd : undefined;
545
+ if (resolveSessionId && cwd) {
546
+ const sessionDir = SessionManager.getDefaultSessionDir(cwd, this.settings.agentDir);
547
+ const match = await resolveResumableSession(resolveSessionId, cwd, sessionDir);
548
+ return {
549
+ ok: true,
550
+ result: {
551
+ ...result,
552
+ savedSession:
553
+ match && match.session.id === resolveSessionId
554
+ ? { id: match.session.id, path: match.session.path }
555
+ : undefined,
556
+ },
557
+ indexSeq: result.indexSeq,
558
+ };
559
+ }
560
+ return { ok: true, result, indexSeq: result.indexSeq };
561
+ }
562
+ if (operation === "session.get_endpoint") return this.#endpoint(input);
563
+ if (!idempotencyKey) return error("invalid_input", "idempotencyKey is required for lifecycle operations");
564
+ const target = createHash("sha256")
565
+ .update(canonicalJson(lifecycleTarget(operation, input)))
566
+ .digest("hex");
567
+ const identity = await deriveIdempotencyIdentity(this.settings.agentDir, operation, idempotencyKey, target);
568
+ const requestHash = createHash("sha256").update(canonicalJson({ operation, input })).digest("hex");
569
+ const prev = this.#chains.get(target) ?? Promise.resolve();
570
+ let release!: () => void;
571
+ const current = new Promise<void>(resolve => (release = resolve));
572
+ this.#chains.set(
573
+ target,
574
+ prev.then(() => current),
575
+ );
576
+ await prev;
577
+ try {
578
+ const begun = await this.ledger.begin(identity, requestHash);
579
+ if (begun.kind === "replay") return begun.entry.response as BrokerResponse;
580
+ if (begun.kind === "idempotency_conflict")
581
+ return error("idempotency_conflict", "idempotency key was used with a different request");
582
+ if (begun.kind === "terminal_uncertain")
583
+ return begun.entry.response
584
+ ? (begun.entry.response as BrokerResponse)
585
+ : error("terminal_uncertain", "prior lifecycle operation outcome is uncertain");
586
+ if (begun.kind === "in_progress") return error("broker_restarting", "lifecycle operation is in progress");
587
+ const outcome = await executeLifecycle(this, operation, input, identity);
588
+ const response = outcome.response;
589
+ await this.ledger.transition(
590
+ identity,
591
+ response.ok
592
+ ? "terminal_ok"
593
+ : response.error.code === "terminal_uncertain"
594
+ ? "terminal_uncertain"
595
+ : "terminal_error",
596
+ {
597
+ resultSessionId:
598
+ response.ok && typeof (response.result as { sessionId?: unknown } | undefined)?.sessionId === "string"
599
+ ? (response.result as { sessionId: string }).sessionId
600
+ : undefined,
601
+ response,
602
+ responseDigest: createHash("sha256").update(canonicalJson(response)).digest("hex"),
603
+ ...(outcome.durableEffects ? { durableEffects: outcome.durableEffects } : {}),
604
+ ...(outcome.startupFailure ? { startupFailure: outcome.startupFailure } : {}),
605
+ },
606
+ );
607
+ const reopenedLedger = await new LifecycleLedger(this.settings.agentDir).open();
608
+ const persisted = reopenedLedger.get(identity);
609
+ const expectedResponseDigest = createHash("sha256").update(canonicalJson(response)).digest("hex");
610
+ const persistenceVerified =
611
+ persisted?.responseDigest === expectedResponseDigest &&
612
+ canonicalJson(persisted.response) === canonicalJson(response) &&
613
+ canonicalJson(persisted.durableEffects) === canonicalJson(outcome.durableEffects) &&
614
+ canonicalJson(persisted.startupFailure) === canonicalJson(outcome.startupFailure);
615
+ if (!persistenceVerified) {
616
+ const uncertain = error(
617
+ "terminal_uncertain",
618
+ "Lifecycle terminal evidence could not be verified after persistence; retained artifacts require reconciliation.",
619
+ );
620
+ await this.ledger.transition(identity, "terminal_uncertain", {
621
+ response: uncertain,
622
+ responseDigest: createHash("sha256").update(canonicalJson(uncertain)).digest("hex"),
623
+ ...(outcome.durableEffects ? { durableEffects: outcome.durableEffects } : {}),
624
+ ...(outcome.startupFailure ? { startupFailure: outcome.startupFailure } : {}),
625
+ });
626
+ return uncertain;
627
+ }
628
+ terminalPersistenceHooksForTest.get(this)?.();
629
+ await outcome.deferredArtifactCleanup?.();
630
+ return response;
631
+ } finally {
632
+ release();
633
+ if (this.#chains.get(target) === current) this.#chains.delete(target);
634
+ }
635
+ }
636
+ }
637
+
638
+ /** Test-only hook for simulating a process crash after terminal persistence verification. */
639
+ export function setTerminalPersistenceHookForTest(broker: Broker, hook: (() => void) | undefined): void {
640
+ if (hook) terminalPersistenceHooksForTest.set(broker, hook);
641
+ else terminalPersistenceHooksForTest.delete(broker);
642
+ }