@gajae-code/coding-agent 0.10.2 → 0.11.1

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 (476) hide show
  1. package/CHANGELOG.md +58 -2
  2. package/dist/types/append-only-mode.d.ts +1 -1
  3. package/dist/types/async/job-manager.d.ts +46 -7
  4. package/dist/types/cli/args.d.ts +4 -5
  5. package/dist/types/cli/daemon-cli.d.ts +12 -1
  6. package/dist/types/cli/notify-cli.d.ts +21 -1
  7. package/dist/types/cli.d.ts +3 -0
  8. package/dist/types/commands/daemon.d.ts +39 -0
  9. package/dist/types/commands/launch.d.ts +3 -0
  10. package/dist/types/commands/notify.d.ts +28 -1
  11. package/dist/types/commands/sdk.d.ts +33 -0
  12. package/dist/types/config/atomic-yaml-patch.d.ts +85 -0
  13. package/dist/types/config/model-profile-activation.d.ts +35 -15
  14. package/dist/types/config/model-profiles.d.ts +11 -5
  15. package/dist/types/config/model-registry.d.ts +3 -3
  16. package/dist/types/config/model-resolver.d.ts +39 -6
  17. package/dist/types/config/model-selector-value.d.ts +17 -0
  18. package/dist/types/config/models-config-schema.d.ts +10 -6
  19. package/dist/types/config/settings-schema.d.ts +133 -6
  20. package/dist/types/config/settings.d.ts +63 -35
  21. package/dist/types/coordinator-mcp/model-preset.d.ts +7 -6
  22. package/dist/types/coordinator-mcp/server.d.ts +6 -49
  23. package/dist/types/daemon/builtin.d.ts +6 -5
  24. package/dist/types/daemon/control-types.d.ts +1 -1
  25. package/dist/types/discovery/mcp-json.d.ts +12 -1
  26. package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
  27. package/dist/types/extensibility/extensions/compact-handler.d.ts +4 -2
  28. package/dist/types/extensibility/extensions/get-commands-handler.d.ts +2 -2
  29. package/dist/types/extensibility/extensions/loader.d.ts +10 -1
  30. package/dist/types/extensibility/extensions/runner.d.ts +3 -3
  31. package/dist/types/extensibility/extensions/types.d.ts +141 -16
  32. package/dist/types/extensibility/hooks/types.d.ts +1 -1
  33. package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -2
  34. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -0
  35. package/dist/types/gjc-runtime/session-state-sidecar.d.ts +3 -0
  36. package/dist/types/gjc-runtime/team-runtime.d.ts +3 -4
  37. package/dist/types/gjc-runtime/tmux-owner-isolation-cli.d.ts +13 -0
  38. package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -1
  39. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +11 -0
  40. package/dist/types/gjc-runtime/windows-powershell-command.d.ts +9 -0
  41. package/dist/types/harness-control-plane/operate.d.ts +5 -5
  42. package/dist/types/harness-control-plane/owner.d.ts +18 -4
  43. package/dist/types/harness-control-plane/receipts.d.ts +0 -1
  44. package/dist/types/harness-control-plane/sdk-transport.d.ts +43 -0
  45. package/dist/types/harness-control-plane/session-transport.d.ts +48 -0
  46. package/dist/types/harness-control-plane/types.d.ts +3 -4
  47. package/dist/types/lsp/index.d.ts +1 -1
  48. package/dist/types/main.d.ts +32 -38
  49. package/dist/types/modes/acp/acp-agent.d.ts +224 -53
  50. package/dist/types/modes/acp/acp-client-bridge.d.ts +1 -0
  51. package/dist/types/modes/acp/acp-mode.d.ts +6 -15
  52. package/dist/types/modes/acp/permission-mode.d.ts +4 -0
  53. package/dist/types/modes/acp/startup-options.d.ts +6 -0
  54. package/dist/types/modes/components/agent-dashboard.d.ts +3 -0
  55. package/dist/types/modes/components/model-selector.d.ts +1 -0
  56. package/dist/types/modes/components/notifications-settings-editor.d.ts +135 -0
  57. package/dist/types/modes/components/settings-selector.d.ts +6 -4
  58. package/dist/types/modes/components/welcome.d.ts +1 -1
  59. package/dist/types/modes/controllers/command-controller.d.ts +2 -2
  60. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -0
  61. package/dist/types/modes/controllers/selector-controller.d.ts +35 -1
  62. package/dist/types/modes/index.d.ts +0 -4
  63. package/dist/types/modes/interactive-mode.d.ts +5 -2
  64. package/dist/types/modes/runtime-init.d.ts +2 -2
  65. package/dist/types/modes/shared/agent-wire/approval-gate.d.ts +6 -9
  66. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -6
  67. package/dist/types/modes/shared/agent-wire/event-contract.d.ts +6 -6
  68. package/dist/types/modes/shared/agent-wire/event-envelope.d.ts +4 -9
  69. package/dist/types/modes/shared/agent-wire/event-observation.d.ts +1 -1
  70. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +170 -30
  71. package/dist/types/modes/shared/agent-wire/workflow-gate-schema.d.ts +7 -7
  72. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +105 -0
  73. package/dist/types/modes/types.d.ts +7 -2
  74. package/dist/types/runtime-mcp/client.d.ts +2 -0
  75. package/dist/types/runtime-mcp/config.d.ts +5 -1
  76. package/dist/types/runtime-mcp/manager.d.ts +8 -1
  77. package/dist/types/runtime-mcp/types.d.ts +4 -0
  78. package/dist/types/sdk/acp/adapter.d.ts +67 -0
  79. package/dist/types/sdk/acp/index.d.ts +2 -0
  80. package/dist/types/sdk/broker/broker.d.ts +53 -0
  81. package/dist/types/sdk/broker/discovery.d.ts +30 -0
  82. package/dist/types/sdk/broker/ensure.d.ts +42 -0
  83. package/dist/types/sdk/broker/identity.d.ts +3 -0
  84. package/dist/types/sdk/broker/index.d.ts +8 -0
  85. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +95 -0
  86. package/dist/types/sdk/broker/lifecycle.d.ts +85 -0
  87. package/dist/types/sdk/broker/process-incarnation.d.ts +14 -0
  88. package/dist/types/sdk/broker/runtime.d.ts +33 -0
  89. package/dist/types/sdk/broker/session-index.d.ts +57 -0
  90. package/dist/types/sdk/broker/state-version.d.ts +8 -0
  91. package/dist/types/sdk/broker/transport.d.ts +9 -0
  92. package/dist/types/sdk/bus/chat-command-policy.d.ts +49 -0
  93. package/dist/types/sdk/bus/chat-daemon-cli.d.ts +21 -0
  94. package/dist/types/sdk/bus/chat-daemon-control.d.ts +111 -0
  95. package/dist/types/sdk/bus/chat-daemon-runtime.d.ts +77 -0
  96. package/dist/types/sdk/bus/chat-effect-journal.d.ts +84 -0
  97. package/dist/types/{notifications → sdk/bus}/config-commands.d.ts +7 -2
  98. package/dist/types/sdk/bus/config.d.ts +274 -0
  99. package/dist/types/sdk/bus/conversation-store.d.ts +72 -0
  100. package/dist/types/sdk/bus/discord-conversation.d.ts +58 -0
  101. package/dist/types/sdk/bus/discord-daemon.d.ts +50 -0
  102. package/dist/types/sdk/bus/discord-live-provider.d.ts +70 -0
  103. package/dist/types/sdk/bus/discord-provider.d.ts +85 -0
  104. package/dist/types/{notifications → sdk/bus}/html-format.d.ts +2 -2
  105. package/dist/types/{notifications → sdk/bus}/index.d.ts +113 -13
  106. package/dist/types/{notifications → sdk/bus}/lifecycle-control-runtime.d.ts +4 -2
  107. package/dist/types/{notifications → sdk/bus}/lifecycle-orchestrator.d.ts +18 -21
  108. package/dist/types/{notifications → sdk/bus}/managed-daemon.d.ts +1 -1
  109. package/dist/types/sdk/bus/native-runtime-compatibility.d.ts +13 -0
  110. package/dist/types/sdk/bus/notification-orchestration.d.ts +170 -0
  111. package/dist/types/{notifications → sdk/bus}/notification-service.d.ts +7 -1
  112. package/dist/types/sdk/bus/session-control.d.ts +83 -0
  113. package/dist/types/sdk/bus/slack-conversation.d.ts +56 -0
  114. package/dist/types/sdk/bus/slack-daemon.d.ts +60 -0
  115. package/dist/types/sdk/bus/slack-live-provider.d.ts +59 -0
  116. package/dist/types/sdk/bus/slack-provider.d.ts +55 -0
  117. package/dist/types/{notifications → sdk/bus}/telegram-cli.d.ts +4 -4
  118. package/dist/types/{notifications → sdk/bus}/telegram-daemon-cli.d.ts +14 -5
  119. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +12 -0
  120. package/dist/types/{notifications → sdk/bus}/telegram-daemon-control.d.ts +2 -2
  121. package/dist/types/{notifications → sdk/bus}/telegram-daemon.d.ts +78 -17
  122. package/dist/types/{notifications → sdk/bus}/telegram-reference.d.ts +2 -2
  123. package/dist/types/sdk/bus/telegram-setup.d.ts +128 -0
  124. package/dist/types/{notifications → sdk/bus}/topic-registry.d.ts +11 -0
  125. package/dist/types/sdk/cli/index.d.ts +1 -0
  126. package/dist/types/sdk/cli/session-cli.d.ts +19 -0
  127. package/dist/types/sdk/client/client.d.ts +1 -0
  128. package/dist/types/sdk/client/discovery.d.ts +27 -0
  129. package/dist/types/sdk/client/index.d.ts +2 -0
  130. package/dist/types/sdk/host/control/dispatch.d.ts +34 -0
  131. package/dist/types/sdk/host/control/index.d.ts +2 -0
  132. package/dist/types/sdk/host/control/operations.d.ts +65 -0
  133. package/dist/types/sdk/host/events.d.ts +35 -0
  134. package/dist/types/sdk/host/host.d.ts +35 -0
  135. package/dist/types/sdk/host/index.d.ts +4 -0
  136. package/dist/types/sdk/host/query/cursor.d.ts +62 -0
  137. package/dist/types/sdk/host/query/handlers.d.ts +70 -0
  138. package/dist/types/sdk/host/query/index.d.ts +3 -0
  139. package/dist/types/sdk/host/query/revision-store.d.ts +60 -0
  140. package/dist/types/sdk/host/reverse-leases.d.ts +45 -0
  141. package/dist/types/sdk/host/types.d.ts +32 -0
  142. package/dist/types/sdk/index.d.ts +6 -0
  143. package/dist/types/sdk/lifecycle-session.d.ts +14 -0
  144. package/dist/types/sdk/mcp/index.d.ts +1 -0
  145. package/dist/types/sdk/mcp/server.d.ts +37 -0
  146. package/dist/types/sdk/models.d.ts +44 -0
  147. package/dist/types/sdk/protocol/adapter-validation.d.ts +14 -0
  148. package/dist/types/sdk/protocol/operation-registry.d.ts +24 -0
  149. package/dist/types/{sdk.d.ts → sdk/session.d.ts} +44 -38
  150. package/dist/types/sdk/startup-capability.d.ts +50 -0
  151. package/dist/types/session/agent-session.d.ts +114 -21
  152. package/dist/types/session/default-model-selection.d.ts +25 -0
  153. package/dist/types/session/fallback-chain-controller.d.ts +52 -0
  154. package/dist/types/session/session-manager.d.ts +58 -1
  155. package/dist/types/setup/credential-auto-import.d.ts +36 -6
  156. package/dist/types/tools/ask-answer-registry.d.ts +1 -1
  157. package/dist/types/tools/browser/benchmark/detector-report.d.ts +63 -0
  158. package/dist/types/tools/browser/benchmark/run-suite.d.ts +18 -0
  159. package/dist/types/tools/browser/launch.d.ts +22 -0
  160. package/dist/types/tools/browser/profile-discovery.d.ts +31 -0
  161. package/dist/types/tools/browser/profile-posture.d.ts +37 -0
  162. package/dist/types/tools/browser/profile-reuse.d.ts +39 -0
  163. package/dist/types/tools/browser/profile-warmup.d.ts +39 -0
  164. package/dist/types/tools/browser/registry.d.ts +19 -0
  165. package/dist/types/tools/browser/tab-protocol.d.ts +4 -0
  166. package/dist/types/tools/index.d.ts +8 -8
  167. package/dist/types/tools/subagent-render.d.ts +1 -0
  168. package/dist/types/tools/subagent.d.ts +6 -1
  169. package/package.json +34 -13
  170. package/scripts/build-sdk-package-smoke.ts +99 -0
  171. package/scripts/capture-notifications-settings-showcase.ts +435 -0
  172. package/scripts/compile-args.ts +4 -2
  173. package/scripts/g005-daemon-smoke.ts +4 -5
  174. package/scripts/g011-daemon-path-smoke.ts +5 -5
  175. package/scripts/generate-sdk-adapter-parity-manifest.ts +163 -0
  176. package/scripts/generate-sdk-operation-inventory.ts +867 -0
  177. package/scripts/generate-telegram-baseline-manifest.ts +128 -0
  178. package/scripts/manifest-schema.ts +124 -0
  179. package/scripts/run-test-manifest.ts +153 -0
  180. package/scripts/run-threat-model-check.ts +157 -0
  181. package/scripts/verify-gjc-sdk-canonicalization.ts +3467 -0
  182. package/scripts/verify-gjc-sdk-rename.ts +90 -0
  183. package/src/append-only-mode.ts +1 -1
  184. package/src/async/job-manager.ts +406 -63
  185. package/src/cli/args.ts +43 -12
  186. package/src/cli/auth-gateway-cli.ts +2 -2
  187. package/src/cli/config-cli.ts +2 -1
  188. package/src/cli/daemon-cli.ts +63 -14
  189. package/src/cli/list-models.ts +1 -1
  190. package/src/cli/notify-cli.ts +288 -286
  191. package/src/cli.ts +21 -2
  192. package/src/commands/daemon.ts +50 -6
  193. package/src/commands/harness.ts +93 -36
  194. package/src/commands/launch.ts +5 -2
  195. package/src/commands/mcp-serve.ts +19 -12
  196. package/src/commands/notify.ts +36 -3
  197. package/src/commands/sdk.ts +426 -0
  198. package/src/commit/agentic/agent.ts +1 -0
  199. package/src/config/atomic-yaml-patch.ts +423 -0
  200. package/src/config/model-profile-activation.ts +240 -89
  201. package/src/config/model-profiles.ts +18 -15
  202. package/src/config/model-registry.ts +51 -26
  203. package/src/config/model-resolver.ts +138 -51
  204. package/src/config/model-selector-value.ts +50 -0
  205. package/src/config/models-config-schema.ts +14 -21
  206. package/src/config/settings-schema.ts +128 -10
  207. package/src/config/settings.ts +1120 -158
  208. package/src/coordinator-mcp/model-preset.ts +74 -20
  209. package/src/coordinator-mcp/server.ts +1597 -3052
  210. package/src/daemon/builtin.ts +8 -5
  211. package/src/daemon/control-types.ts +1 -1
  212. package/src/defaults/gjc/skills/team/SKILL.md +4 -4
  213. package/src/discovery/mcp-json.ts +327 -20
  214. package/src/extensibility/custom-tools/types.ts +1 -1
  215. package/src/extensibility/extensions/compact-handler.ts +3 -3
  216. package/src/extensibility/extensions/get-commands-handler.ts +2 -2
  217. package/src/extensibility/extensions/loader.ts +69 -5
  218. package/src/extensibility/extensions/runner.ts +112 -4
  219. package/src/extensibility/extensions/types.ts +176 -17
  220. package/src/extensibility/hooks/types.ts +1 -1
  221. package/src/gjc-runtime/launch-tmux.ts +170 -86
  222. package/src/gjc-runtime/psmux-detect.ts +82 -4
  223. package/src/gjc-runtime/session-state-sidecar.ts +41 -31
  224. package/src/gjc-runtime/team-runtime.ts +54 -35
  225. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +53 -17
  226. package/src/gjc-runtime/tmux-owner-isolation.ts +1 -1
  227. package/src/gjc-runtime/tmux-sessions.ts +19 -13
  228. package/src/gjc-runtime/ultragoal-guard.ts +108 -14
  229. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +102 -1
  230. package/src/gjc-runtime/ultragoal-runtime.ts +227 -11
  231. package/src/gjc-runtime/windows-powershell-command.ts +58 -0
  232. package/src/harness-control-plane/operate.ts +9 -9
  233. package/src/harness-control-plane/owner.ts +283 -39
  234. package/src/harness-control-plane/receipts.ts +0 -1
  235. package/src/harness-control-plane/sdk-transport.ts +395 -0
  236. package/src/harness-control-plane/session-transport.ts +94 -0
  237. package/src/harness-control-plane/types.ts +1 -1
  238. package/src/internal-urls/docs-index.generated.ts +34 -35
  239. package/src/lsp/client.ts +42 -6
  240. package/src/lsp/index.ts +2 -2
  241. package/src/main.ts +299 -204
  242. package/src/modes/DESIGN.md +258 -0
  243. package/src/modes/acp/acp-agent.ts +1228 -2612
  244. package/src/modes/acp/acp-client-bridge.ts +2 -20
  245. package/src/modes/acp/acp-event-mapper.ts +18 -2
  246. package/src/modes/acp/acp-mode.ts +8 -36
  247. package/src/modes/acp/permission-mode.ts +25 -0
  248. package/src/modes/acp/startup-options.ts +6 -0
  249. package/src/modes/components/agent-dashboard.ts +37 -17
  250. package/src/modes/components/model-selector.ts +93 -35
  251. package/src/modes/components/notifications-settings-editor.ts +1379 -0
  252. package/src/modes/components/settings-selector.ts +60 -11
  253. package/src/modes/components/welcome.ts +4 -4
  254. package/src/modes/controllers/command-controller.ts +10 -17
  255. package/src/modes/controllers/event-controller.ts +11 -16
  256. package/src/modes/controllers/extension-ui-controller.ts +396 -28
  257. package/src/modes/controllers/selector-controller.ts +674 -36
  258. package/src/modes/index.ts +0 -22
  259. package/src/modes/interactive-mode.ts +87 -36
  260. package/src/modes/runtime-init.ts +298 -6
  261. package/src/modes/shared/agent-wire/approval-gate.ts +9 -11
  262. package/src/modes/shared/agent-wire/deep-interview-gate.ts +10 -11
  263. package/src/modes/shared/agent-wire/event-contract.ts +7 -8
  264. package/src/modes/shared/agent-wire/event-envelope.ts +9 -17
  265. package/src/modes/shared/agent-wire/event-observation.ts +16 -7
  266. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +1258 -87
  267. package/src/modes/shared/agent-wire/workflow-gate-schema.ts +15 -15
  268. package/src/modes/shared/agent-wire/workflow-gate-types.ts +113 -0
  269. package/src/modes/types.ts +7 -2
  270. package/src/runtime-mcp/client.ts +94 -19
  271. package/src/runtime-mcp/config.ts +27 -15
  272. package/src/runtime-mcp/manager.ts +343 -105
  273. package/src/runtime-mcp/oauth-flow.ts +42 -12
  274. package/src/runtime-mcp/transports/http.ts +49 -19
  275. package/src/runtime-mcp/transports/stdio.ts +47 -28
  276. package/src/runtime-mcp/types.ts +10 -0
  277. package/src/sdk/acp/adapter.ts +418 -0
  278. package/src/sdk/acp/index.ts +2 -0
  279. package/src/sdk/broker/broker.ts +642 -0
  280. package/src/sdk/broker/discovery.ts +113 -0
  281. package/src/sdk/broker/ensure.ts +307 -0
  282. package/src/sdk/broker/identity.ts +42 -0
  283. package/src/sdk/broker/index.ts +8 -0
  284. package/src/sdk/broker/internal-source-marker-2178.txt +1 -0
  285. package/src/sdk/broker/internal-source.bunfig.toml +1 -0
  286. package/src/sdk/broker/lifecycle-ledger.ts +286 -0
  287. package/src/sdk/broker/lifecycle.ts +2157 -0
  288. package/src/sdk/broker/process-incarnation.ts +159 -0
  289. package/src/sdk/broker/runtime.ts +164 -0
  290. package/src/sdk/broker/session-index.ts +318 -0
  291. package/src/sdk/broker/state-version.ts +23 -0
  292. package/src/sdk/broker/transport.ts +125 -0
  293. package/src/sdk/bus/chat-command-policy.ts +107 -0
  294. package/src/sdk/bus/chat-daemon-cli.ts +214 -0
  295. package/src/sdk/bus/chat-daemon-control.ts +713 -0
  296. package/src/sdk/bus/chat-daemon-runtime.ts +526 -0
  297. package/src/sdk/bus/chat-effect-journal.ts +355 -0
  298. package/src/{notifications → sdk/bus}/config-commands.ts +39 -10
  299. package/src/sdk/bus/config.ts +433 -0
  300. package/src/sdk/bus/conversation-store.ts +391 -0
  301. package/src/sdk/bus/discord-conversation.ts +85 -0
  302. package/src/sdk/bus/discord-daemon.ts +2292 -0
  303. package/src/sdk/bus/discord-live-provider.ts +521 -0
  304. package/src/sdk/bus/discord-provider.ts +56 -0
  305. package/src/{notifications → sdk/bus}/html-format.ts +2 -2
  306. package/src/sdk/bus/index.ts +3979 -0
  307. package/src/{notifications → sdk/bus}/lifecycle-commands.ts +2 -0
  308. package/src/{notifications → sdk/bus}/lifecycle-control-runtime.ts +225 -196
  309. package/src/{notifications → sdk/bus}/lifecycle-orchestrator.ts +161 -74
  310. package/src/{notifications → sdk/bus}/managed-daemon.ts +2 -2
  311. package/src/sdk/bus/native-runtime-compatibility.ts +42 -0
  312. package/src/sdk/bus/natives-augment.d.ts +38 -0
  313. package/src/sdk/bus/notification-orchestration.ts +535 -0
  314. package/src/{notifications → sdk/bus}/notification-service.ts +106 -12
  315. package/src/sdk/bus/session-control.ts +333 -0
  316. package/src/sdk/bus/slack-conversation.ts +74 -0
  317. package/src/sdk/bus/slack-daemon.ts +2144 -0
  318. package/src/sdk/bus/slack-live-provider.ts +380 -0
  319. package/src/sdk/bus/slack-provider.ts +76 -0
  320. package/src/{notifications → sdk/bus}/telegram-cli.ts +8 -8
  321. package/src/sdk/bus/telegram-daemon-cli.ts +330 -0
  322. package/src/sdk/bus/telegram-daemon-contract.ts +14 -0
  323. package/src/{notifications → sdk/bus}/telegram-daemon-control.ts +4 -4
  324. package/src/{notifications → sdk/bus}/telegram-daemon.ts +603 -63
  325. package/src/{notifications → sdk/bus}/telegram-reference.ts +2 -2
  326. package/src/sdk/bus/telegram-setup.ts +663 -0
  327. package/src/{notifications → sdk/bus}/topic-registry.ts +32 -0
  328. package/src/sdk/cli/index.ts +1 -0
  329. package/src/sdk/cli/session-cli.ts +275 -0
  330. package/src/sdk/client/client.ts +1 -0
  331. package/src/sdk/client/discovery.ts +108 -0
  332. package/src/sdk/client/index.ts +2 -0
  333. package/src/sdk/host/control/dispatch.ts +367 -0
  334. package/src/sdk/host/control/index.ts +10 -0
  335. package/src/sdk/host/control/operations.ts +63 -0
  336. package/src/sdk/host/events.ts +91 -0
  337. package/src/sdk/host/host.ts +296 -0
  338. package/src/sdk/host/index.ts +4 -0
  339. package/src/sdk/host/query/cursor.ts +189 -0
  340. package/src/sdk/host/query/handlers.ts +533 -0
  341. package/src/sdk/host/query/index.ts +3 -0
  342. package/src/sdk/host/query/revision-store.ts +1126 -0
  343. package/src/sdk/host/reverse-leases.ts +325 -0
  344. package/src/sdk/host/types.ts +29 -0
  345. package/src/sdk/index.ts +13 -0
  346. package/src/sdk/lifecycle-session.ts +39 -0
  347. package/src/sdk/mcp/index.ts +1 -0
  348. package/src/sdk/mcp/server.ts +332 -0
  349. package/src/sdk/models.ts +168 -0
  350. package/src/sdk/protocol/adapter-validation.ts +48 -0
  351. package/src/sdk/protocol/operation-inventory.generated.json +5654 -0
  352. package/src/sdk/protocol/operation-registry.ts +312 -0
  353. package/src/{sdk.ts → sdk/session.ts} +339 -212
  354. package/src/sdk/startup-capability.ts +189 -0
  355. package/src/session/agent-session.ts +2650 -944
  356. package/src/session/default-model-selection.ts +93 -0
  357. package/src/session/fallback-chain-controller.ts +136 -0
  358. package/src/session/session-manager.ts +438 -11
  359. package/src/session/session-storage.ts +8 -1
  360. package/src/setup/credential-auto-import.ts +290 -35
  361. package/src/setup/hermes/templates/operator-instructions.v1.md +3 -5
  362. package/src/slash-commands/builtin-registry.ts +2 -1
  363. package/src/task/executor.ts +62 -13
  364. package/src/task/index.ts +26 -16
  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/subagent-render.ts +19 -8
  384. package/src/tools/subagent.ts +84 -32
  385. package/src/tools/telegram-send.ts +2 -2
  386. package/vendor/insane-search/MANIFEST.json +2 -1
  387. package/vendor/insane-search/engine/bias_check.py +4 -1
  388. package/vendor/insane-search/engine/phase0.py +26 -1
  389. package/dist/types/harness-control-plane/rpc-adapter.d.ts +0 -69
  390. package/dist/types/modes/bridge/auth.d.ts +0 -12
  391. package/dist/types/modes/bridge/bridge-client-bridge.d.ts +0 -9
  392. package/dist/types/modes/bridge/bridge-mode.d.ts +0 -47
  393. package/dist/types/modes/bridge/bridge-ui-context.d.ts +0 -88
  394. package/dist/types/modes/bridge/event-stream.d.ts +0 -8
  395. package/dist/types/modes/rpc/host-tools.d.ts +0 -1
  396. package/dist/types/modes/rpc/host-uris.d.ts +0 -1
  397. package/dist/types/modes/rpc/rpc-client.d.ts +0 -299
  398. package/dist/types/modes/rpc/rpc-mode.d.ts +0 -86
  399. package/dist/types/modes/rpc/rpc-socket-security.d.ts +0 -10
  400. package/dist/types/modes/rpc/rpc-types.d.ts +0 -810
  401. package/dist/types/modes/shared/agent-wire/command-contract.d.ts +0 -18
  402. package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +0 -37
  403. package/dist/types/modes/shared/agent-wire/command-validation.d.ts +0 -2
  404. package/dist/types/modes/shared/agent-wire/handshake.d.ts +0 -56
  405. package/dist/types/modes/shared/agent-wire/host-tool-bridge.d.ts +0 -16
  406. package/dist/types/modes/shared/agent-wire/host-uri-bridge.d.ts +0 -17
  407. package/dist/types/modes/shared/agent-wire/protocol.d.ts +0 -25
  408. package/dist/types/modes/shared/agent-wire/responses.d.ts +0 -4
  409. package/dist/types/modes/shared/agent-wire/scopes.d.ts +0 -18
  410. package/dist/types/modes/shared/agent-wire/session-registry.d.ts +0 -25
  411. package/dist/types/modes/shared/agent-wire/ui-request-broker.d.ts +0 -42
  412. package/dist/types/modes/shared/agent-wire/ui-result.d.ts +0 -27
  413. package/dist/types/modes/shared/agent-wire/unattended-action-policy.d.ts +0 -29
  414. package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +0 -68
  415. package/dist/types/modes/shared/agent-wire/unattended-run-controller.d.ts +0 -166
  416. package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +0 -127
  417. package/dist/types/notifications/config.d.ts +0 -132
  418. package/src/harness-control-plane/rpc-adapter.ts +0 -290
  419. package/src/modes/bridge/auth.ts +0 -41
  420. package/src/modes/bridge/bridge-client-bridge.ts +0 -47
  421. package/src/modes/bridge/bridge-mode.ts +0 -675
  422. package/src/modes/bridge/bridge-ui-context.ts +0 -200
  423. package/src/modes/bridge/event-stream.ts +0 -70
  424. package/src/modes/rpc/host-tools.ts +0 -1
  425. package/src/modes/rpc/host-uris.ts +0 -1
  426. package/src/modes/rpc/rpc-client.ts +0 -1166
  427. package/src/modes/rpc/rpc-mode.ts +0 -1029
  428. package/src/modes/rpc/rpc-socket-security.ts +0 -115
  429. package/src/modes/rpc/rpc-types.ts +0 -637
  430. package/src/modes/shared/agent-wire/command-contract.ts +0 -18
  431. package/src/modes/shared/agent-wire/command-dispatch.ts +0 -498
  432. package/src/modes/shared/agent-wire/command-validation.ts +0 -181
  433. package/src/modes/shared/agent-wire/handshake.ts +0 -157
  434. package/src/modes/shared/agent-wire/host-tool-bridge.ts +0 -197
  435. package/src/modes/shared/agent-wire/host-uri-bridge.ts +0 -236
  436. package/src/modes/shared/agent-wire/protocol.ts +0 -46
  437. package/src/modes/shared/agent-wire/responses.ts +0 -17
  438. package/src/modes/shared/agent-wire/scopes.ts +0 -94
  439. package/src/modes/shared/agent-wire/session-registry.ts +0 -109
  440. package/src/modes/shared/agent-wire/ui-request-broker.ts +0 -150
  441. package/src/modes/shared/agent-wire/ui-result.ts +0 -48
  442. package/src/modes/shared/agent-wire/unattended-action-policy.ts +0 -365
  443. package/src/modes/shared/agent-wire/unattended-audit.ts +0 -176
  444. package/src/modes/shared/agent-wire/unattended-run-controller.ts +0 -426
  445. package/src/modes/shared/agent-wire/unattended-session.ts +0 -528
  446. package/src/notifications/config.ts +0 -219
  447. package/src/notifications/index.ts +0 -1835
  448. package/src/notifications/telegram-daemon-cli.ts +0 -243
  449. package/src/tools/puppeteer/09_stealth_locale.txt +0 -46
  450. /package/dist/types/{notifications → sdk/bus}/attachment-registry.d.ts +0 -0
  451. /package/dist/types/{notifications → sdk/bus}/chat-adapters.d.ts +0 -0
  452. /package/dist/types/{notifications → sdk/bus}/daemon-paths.d.ts +0 -0
  453. /package/dist/types/{notifications → sdk/bus}/engine.d.ts +0 -0
  454. /package/dist/types/{notifications → sdk/bus}/helpers.d.ts +0 -0
  455. /package/dist/types/{notifications → sdk/bus}/lifecycle-commands.d.ts +0 -0
  456. /package/dist/types/{notifications → sdk/bus}/operator-runtime.d.ts +0 -0
  457. /package/dist/types/{notifications → sdk/bus}/rate-limit-pool.d.ts +0 -0
  458. /package/dist/types/{notifications → sdk/bus}/recent-activity.d.ts +0 -0
  459. /package/dist/types/{notifications → sdk/bus}/reply-sent-store.d.ts +0 -0
  460. /package/dist/types/{notifications → sdk/bus}/rich-draft.d.ts +0 -0
  461. /package/dist/types/{notifications → sdk/bus}/rich-render.d.ts +0 -0
  462. /package/dist/types/{notifications → sdk/bus}/threaded-inbound.d.ts +0 -0
  463. /package/dist/types/{notifications → sdk/bus}/threaded-render.d.ts +0 -0
  464. /package/src/{notifications → sdk/bus}/attachment-registry.ts +0 -0
  465. /package/src/{notifications → sdk/bus}/chat-adapters.ts +0 -0
  466. /package/src/{notifications → sdk/bus}/daemon-paths.ts +0 -0
  467. /package/src/{notifications → sdk/bus}/engine.ts +0 -0
  468. /package/src/{notifications → sdk/bus}/helpers.ts +0 -0
  469. /package/src/{notifications → sdk/bus}/operator-runtime.ts +0 -0
  470. /package/src/{notifications → sdk/bus}/rate-limit-pool.ts +0 -0
  471. /package/src/{notifications → sdk/bus}/recent-activity.ts +0 -0
  472. /package/src/{notifications → sdk/bus}/reply-sent-store.ts +0 -0
  473. /package/src/{notifications → sdk/bus}/rich-draft.ts +0 -0
  474. /package/src/{notifications → sdk/bus}/rich-render.ts +0 -0
  475. /package/src/{notifications → sdk/bus}/threaded-inbound.ts +0 -0
  476. /package/src/{notifications → sdk/bus}/threaded-render.ts +0 -0
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Profile-reuse orchestration (plan Phase 2 wiring).
3
+ *
4
+ * Single entry point the launch path calls to decide, for a default headless
5
+ * session, whether to warm up from an isolated copy of the user's real Chrome
6
+ * profile. Ties together discovery + posture + isolated warm-up. Filesystem
7
+ * effects (the copy) are performed only when the posture resolves to `real`.
8
+ * The heavy pieces are injectable so this is unit-testable without a browser.
9
+ */
10
+
11
+ import * as fs from "node:fs";
12
+ import * as os from "node:os";
13
+ import * as path from "node:path";
14
+ import { type DiscoveredProfile, type DiscoveryEnv, discoverDefaultChromeProfile } from "./profile-discovery";
15
+ import { decideProfilePosture, type ProfileReusePosture } from "./profile-posture";
16
+ import { collectWarmupArtifacts, type WarmupManifest } from "./profile-warmup";
17
+
18
+ export interface ResolveProfileReuseInputs {
19
+ posture?: ProfileReusePosture;
20
+ /** Caller explicitly requested real-profile reuse (e.g. app.browser === "chrome"). */
21
+ explicitlyRequested?: boolean;
22
+ discoveryEnv: DiscoveryEnv;
23
+ /** Destination for the isolated warm-up copy; defaults to an ephemeral temp dir. */
24
+ destDir?: string;
25
+ /** Injectable copier (defaults to collectWarmupArtifacts). */
26
+ copy?: (sourceProfileDir: string, destDir: string) => WarmupManifest;
27
+ /** Injectable temp-dir factory (defaults to fs.mkdtemp under os.tmpdir()). */
28
+ makeTempDir?: () => string;
29
+ }
30
+
31
+ export interface ProfileReuseResult {
32
+ mode: "real" | "synthetic";
33
+ reason: string;
34
+ warning: string | null;
35
+ discovered: DiscoveredProfile | null;
36
+ /** Isolated copy directory when mode === "real", else null. */
37
+ warmupDir: string | null;
38
+ manifest: WarmupManifest | null;
39
+ }
40
+
41
+ function defaultTempDir(): string {
42
+ return fs.mkdtempSync(path.join(os.tmpdir(), "gjc-profile-warmup-"));
43
+ }
44
+
45
+ function errorMessage(error: unknown): string {
46
+ return error instanceof Error ? error.message : String(error);
47
+ }
48
+
49
+ /**
50
+ * Resolve profile reuse for a default session. When the posture resolves to
51
+ * `real`, copies an allowlisted, lock-free subset of the discovered profile
52
+ * into an isolated dir and returns that dir for the launcher to use.
53
+ */
54
+ export function resolveProfileReuse(inputs: ResolveProfileReuseInputs): ProfileReuseResult {
55
+ const discovered = discoverDefaultChromeProfile(inputs.discoveryEnv);
56
+ const decision = decideProfilePosture({
57
+ posture: inputs.posture,
58
+ realProfileAvailable: discovered !== null,
59
+ explicitlyRequested: inputs.explicitlyRequested,
60
+ });
61
+
62
+ if (decision.mode !== "real" || !discovered) {
63
+ return {
64
+ mode: "synthetic",
65
+ reason: decision.reason,
66
+ warning: null,
67
+ discovered,
68
+ warmupDir: null,
69
+ manifest: null,
70
+ };
71
+ }
72
+
73
+ const ownsDestDir = inputs.destDir === undefined;
74
+ let destDir: string | undefined;
75
+ const copy = inputs.copy ?? collectWarmupArtifacts;
76
+ try {
77
+ destDir = inputs.destDir ?? (inputs.makeTempDir ?? defaultTempDir)();
78
+ const manifest = copy(discovered.profileDir, destDir);
79
+ return {
80
+ mode: "real",
81
+ reason: decision.reason,
82
+ warning: decision.warning,
83
+ discovered,
84
+ warmupDir: destDir,
85
+ manifest,
86
+ };
87
+ } catch (error) {
88
+ if (ownsDestDir && destDir) {
89
+ try {
90
+ fs.rmSync(destDir, { recursive: true, force: true });
91
+ } catch {}
92
+ }
93
+ return {
94
+ mode: "synthetic",
95
+ reason: "synthetic-copy-failed",
96
+ warning: `Could not safely copy the discovered Chrome profile; using synthetic browser state instead: ${errorMessage(error)}`,
97
+ discovered,
98
+ warmupDir: null,
99
+ manifest: null,
100
+ };
101
+ }
102
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Isolated profile warm-up (plan Phase 2, R-C3).
3
+ *
4
+ * Aggressive real-profile reuse seeds a session with the user's cookies /
5
+ * localStorage / cache. To stay safe (never lock or corrupt the live browser
6
+ * session) we NEVER bind the source profile directly: we copy an allowlisted
7
+ * subset of artifacts into an ephemeral, isolated directory and drive Chromium
8
+ * against that copy. Chromium single-instance lock files (SingletonLock/Socket/
9
+ * Cookie) are explicitly excluded so the isolated copy can never contend with a
10
+ * running browser.
11
+ *
12
+ * This module is pure filesystem logic (no browser, no network) so it is
13
+ * deterministically unit-testable.
14
+ */
15
+
16
+ import * as fs from "node:fs";
17
+ import * as path from "node:path";
18
+
19
+ /** Chromium single-instance lock artifacts — must never be copied. */
20
+ export const CHROME_LOCK_ARTIFACTS = ["SingletonLock", "SingletonSocket", "SingletonCookie"] as const;
21
+ const CHROME_LOCK_ARTIFACT_SET = new Set<string>(CHROME_LOCK_ARTIFACTS);
22
+
23
+ /**
24
+ * Profile-relative artifacts worth seeding for warm-up. Kept intentionally
25
+ * small: session state (cookies, storage) plus a light cache. Anything not
26
+ * listed is skipped, which also keeps the isolated copy cheap.
27
+ */
28
+ export const WARMUP_ARTIFACTS = [
29
+ "Cookies",
30
+ "Cookies-journal",
31
+ "Local Storage",
32
+ "Session Storage",
33
+ "IndexedDB",
34
+ "Network",
35
+ "Preferences",
36
+ ] as const;
37
+
38
+ export interface WarmupManifest {
39
+ sourceProfileDir: string;
40
+ destDir: string;
41
+ copied: string[];
42
+ skippedMissing: string[];
43
+ /** Always empty unless a lock artifact was seen (never copied). */
44
+ excludedLocks: string[];
45
+ }
46
+
47
+ function copyRecursive(src: string, dest: string, manifest: WarmupManifest): void {
48
+ const stat = fs.statSync(src);
49
+ if (stat.isDirectory()) {
50
+ fs.mkdirSync(dest, { recursive: true });
51
+ for (const entry of fs.readdirSync(src)) {
52
+ if (CHROME_LOCK_ARTIFACT_SET.has(entry)) {
53
+ if (!manifest.excludedLocks.includes(entry)) manifest.excludedLocks.push(entry);
54
+ continue;
55
+ }
56
+ copyRecursive(path.join(src, entry), path.join(dest, entry), manifest);
57
+ }
58
+ } else {
59
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
60
+ fs.copyFileSync(src, dest);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Copy warm-up artifacts from `sourceProfileDir` into an isolated `destDir`.
66
+ *
67
+ * Guarantees:
68
+ * - The source profile is only ever read, never written or locked.
69
+ * - Chromium lock artifacts are never copied (recorded in `excludedLocks`).
70
+ * - Missing artifacts are skipped, not fatal (recorded in `skippedMissing`).
71
+ */
72
+ export function collectWarmupArtifacts(sourceProfileDir: string, destDir: string): WarmupManifest {
73
+ const manifest: WarmupManifest = {
74
+ sourceProfileDir,
75
+ destDir,
76
+ copied: [],
77
+ skippedMissing: [],
78
+ excludedLocks: [],
79
+ };
80
+
81
+ if (!fs.existsSync(sourceProfileDir)) {
82
+ throw new Error(`source profile directory does not exist: ${sourceProfileDir}`);
83
+ }
84
+ const resolvedSource = path.resolve(sourceProfileDir);
85
+ const resolvedDest = path.resolve(destDir);
86
+ if (resolvedDest === resolvedSource || resolvedDest.startsWith(`${resolvedSource}${path.sep}`)) {
87
+ throw new Error("warm-up destination must be outside the source profile directory");
88
+ }
89
+ fs.mkdirSync(destDir, { recursive: true });
90
+
91
+ // Record (but never copy) any lock artifacts present in the source.
92
+ for (const lock of CHROME_LOCK_ARTIFACTS) {
93
+ if (fs.existsSync(path.join(sourceProfileDir, lock))) {
94
+ manifest.excludedLocks.push(lock);
95
+ }
96
+ }
97
+
98
+ for (const artifact of WARMUP_ARTIFACTS) {
99
+ const src = path.join(sourceProfileDir, artifact);
100
+ if (!fs.existsSync(src)) {
101
+ manifest.skippedMissing.push(artifact);
102
+ continue;
103
+ }
104
+ copyRecursive(src, path.join(destDir, artifact), manifest);
105
+ manifest.copied.push(artifact);
106
+ }
107
+
108
+ return manifest;
109
+ }
@@ -1,3 +1,4 @@
1
+ import * as fs from "node:fs";
1
2
  import * as path from "node:path";
2
3
  import { logger } from "@gajae-code/utils";
3
4
  import type { Subprocess } from "bun";
@@ -12,6 +13,9 @@ import {
12
13
  waitForCdp,
13
14
  } from "./attach";
14
15
  import { BROWSER_PROTOCOL_TIMEOUT_MS, launchHeadlessBrowser, loadPuppeteer, type UserAgentOverride } from "./launch";
16
+ import { defaultDiscoveryEnv } from "./profile-discovery";
17
+ import type { ProfileReusePosture } from "./profile-posture";
18
+ import { resolveProfileReuse } from "./profile-reuse";
15
19
 
16
20
  export type BrowserKind =
17
21
  | { kind: "headless"; headless: boolean }
@@ -29,9 +33,15 @@ export type BrowserKind =
29
33
 
30
34
  export type BrowserKindTag = BrowserKind["kind"];
31
35
 
36
+ export interface BrowserGeo {
37
+ readonly timezone?: string;
38
+ readonly locale?: string;
39
+ }
40
+
32
41
  export interface BrowserHandle {
33
42
  key: string;
34
43
  kind: BrowserKind;
44
+ readonly geo?: BrowserGeo;
35
45
  browser: Browser;
36
46
  cdpUrl?: string;
37
47
  pid?: number;
@@ -43,6 +53,7 @@ export interface BrowserHandle {
43
53
  type SpawnedChromeProfileKind = Extract<BrowserKind, { kind: "chrome-profile" }>;
44
54
 
45
55
  const browsers = new Map<string, BrowserHandle>();
56
+ const browserOpenPromises = new Map<string, Promise<BrowserHandle>>();
46
57
 
47
58
  /**
48
59
  * Upper bound on the CDP `browser.close()` round-trip during a forced (signal-path)
@@ -51,10 +62,10 @@ const browsers = new Map<string, BrowserHandle>();
51
62
  */
52
63
  const HEADLESS_FORCE_CLOSE_GRACE_MS = 1_500;
53
64
 
54
- function browserKey(kind: BrowserKind): string {
65
+ function browserKey(kind: BrowserKind, geo?: BrowserGeo, profileReuse?: ProfileReusePosture): string {
55
66
  switch (kind.kind) {
56
67
  case "headless":
57
- return `headless:${kind.headless ? "1" : "0"}`;
68
+ return `headless:${kind.headless ? "1" : "0"}:${profileReuse ?? "synthetic"}:${geo?.timezone ?? ""}:${geo?.locale ?? ""}`;
58
69
  case "spawned":
59
70
  return `spawned:${kind.path}`;
60
71
  case "chrome-profile":
@@ -69,28 +80,90 @@ export interface AcquireBrowserOptions {
69
80
  viewport?: { width: number; height: number; deviceScaleFactor?: number };
70
81
  appArgs?: string[];
71
82
  signal?: AbortSignal;
83
+ /**
84
+ * Profile-reuse posture for the default headless path (from settings
85
+ * `browser.profileReuse`, resolved by the settings-aware caller). When set,
86
+ * the headless browser may warm up from an isolated copy of the user's real
87
+ * Chrome profile (see profile-reuse.ts). Omitted = synthetic session.
88
+ */
89
+ profileReuse?: ProfileReusePosture;
90
+ /**
91
+ * Opt-in geo alignment for the default headless path (from settings
92
+ * `browser.geo.*`). Default unset = no-op (real timezone/locale preserved).
93
+ */
94
+ geo?: BrowserGeo;
95
+ }
96
+
97
+ function normalizeGeo(geo: AcquireBrowserOptions["geo"]): BrowserGeo | undefined {
98
+ const timezone = geo?.timezone
99
+ ? new Intl.DateTimeFormat(undefined, { timeZone: geo.timezone }).resolvedOptions().timeZone
100
+ : undefined;
101
+ const locale = geo?.locale ? Intl.getCanonicalLocales(geo.locale)[0] : undefined;
102
+ return timezone || locale ? Object.freeze({ timezone, locale }) : undefined;
103
+ }
104
+
105
+ export function browserKeyForTest(
106
+ kind: BrowserKind,
107
+ opts: Pick<AcquireBrowserOptions, "geo" | "profileReuse">,
108
+ ): string {
109
+ const geo = kind.kind === "headless" ? normalizeGeo(opts.geo) : undefined;
110
+ return browserKey(kind, geo, opts.profileReuse);
72
111
  }
73
112
 
74
113
  export async function acquireBrowser(kind: BrowserKind, opts: AcquireBrowserOptions): Promise<BrowserHandle> {
75
- const key = browserKey(kind);
114
+ const geo = kind.kind === "headless" ? normalizeGeo(opts.geo) : undefined;
115
+ const key = browserKey(kind, geo, opts.profileReuse);
76
116
  const existing = browsers.get(key);
77
117
  if (existing) {
78
118
  if (existing.browser.connected) return existing;
79
119
  browsers.delete(key);
80
120
  await disposeBrowserHandle(existing, { kill: false });
81
121
  }
122
+ const pending = browserOpenPromises.get(key);
123
+ if (pending) return await pending;
82
124
 
83
- const handle = await openBrowserHandle(kind, opts);
84
- browsers.set(key, handle);
85
- return handle;
125
+ const opening = openBrowserHandle(kind, opts, geo, key).then(handle => {
126
+ browsers.set(key, handle);
127
+ return handle;
128
+ });
129
+ browserOpenPromises.set(key, opening);
130
+ try {
131
+ return await opening;
132
+ } finally {
133
+ if (browserOpenPromises.get(key) === opening) browserOpenPromises.delete(key);
134
+ }
86
135
  }
87
136
 
88
- async function openBrowserHandle(kind: BrowserKind, opts: AcquireBrowserOptions): Promise<BrowserHandle> {
137
+ async function openBrowserHandle(
138
+ kind: BrowserKind,
139
+ opts: AcquireBrowserOptions,
140
+ geo: BrowserGeo | undefined,
141
+ key: string,
142
+ ): Promise<BrowserHandle> {
89
143
  if (kind.kind === "headless") {
90
- const browser = await launchHeadlessBrowser({ headless: kind.headless, viewport: opts.viewport });
144
+ let profileWarmupDir: string | undefined;
145
+ if (opts.profileReuse) {
146
+ const reuse = resolveProfileReuse({
147
+ posture: opts.profileReuse,
148
+ discoveryEnv: defaultDiscoveryEnv(fs.existsSync),
149
+ });
150
+ if (reuse.warning) logger.warn(reuse.warning);
151
+ if (reuse.mode === "real" && reuse.warmupDir) {
152
+ profileWarmupDir = reuse.warmupDir;
153
+ }
154
+ }
155
+ const browser = await launchHeadlessBrowser({
156
+ headless: kind.headless,
157
+ viewport: opts.viewport,
158
+ profileWarmupDir,
159
+ ...(geo ? { geo } : {}),
160
+ });
91
161
  return {
92
- key: browserKey(kind),
162
+ key,
163
+
93
164
  kind,
165
+ geo,
166
+
94
167
  browser,
95
168
  refCount: 0,
96
169
  stealth: { browserSession: null, override: null },
@@ -106,7 +179,8 @@ async function openBrowserHandle(kind: BrowserKind, opts: AcquireBrowserOptions)
106
179
  protocolTimeout: BROWSER_PROTOCOL_TIMEOUT_MS,
107
180
  });
108
181
  return {
109
- key: browserKey(kind),
182
+ key,
183
+
110
184
  kind,
111
185
  browser,
112
186
  cdpUrl,
@@ -254,6 +328,7 @@ export async function openChromeProfileHandle(
254
328
  }
255
329
  return {
256
330
  key: browserKey(kind),
331
+
257
332
  kind,
258
333
  browser,
259
334
  cdpUrl,
@@ -320,6 +395,7 @@ async function openSpawnedBrowserHandle(
320
395
  }
321
396
  return {
322
397
  key: browserKey(kind),
398
+
323
399
  kind,
324
400
  browser,
325
401
  cdpUrl,
@@ -337,7 +413,7 @@ export function holdBrowser(handle: BrowserHandle): void {
337
413
  export async function releaseBrowser(handle: BrowserHandle, opts: { kill: boolean }): Promise<void> {
338
414
  handle.refCount = Math.max(0, handle.refCount - 1);
339
415
  if (handle.refCount === 0) {
340
- browsers.delete(handle.key);
416
+ if (browsers.get(handle.key) === handle) browsers.delete(handle.key);
341
417
  await disposeBrowserHandle(handle, opts);
342
418
  }
343
419
  }
@@ -46,6 +46,7 @@ export type WorkerInitPayload =
46
46
  browserWSEndpoint: string;
47
47
  safeDir: string;
48
48
  viewport?: { width: number; height: number; deviceScaleFactor?: number };
49
+ geo?: { readonly timezone?: string; readonly locale?: string };
49
50
  dialogs?: "accept" | "dismiss";
50
51
  url?: string;
51
52
  waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
@@ -427,6 +427,7 @@ async function buildInitPayload(browser: BrowserHandle, opts: AcquireTabOptions)
427
427
  browserWSEndpoint,
428
428
  safeDir,
429
429
  viewport: opts.viewport,
430
+ ...(browser.geo ? { geo: browser.geo } : {}),
430
431
  dialogs: opts.dialogs,
431
432
  url: opts.url,
432
433
  waitUntil: opts.waitUntil,
@@ -454,7 +454,7 @@ export class WorkerCore {
454
454
  });
455
455
  if (payload.mode === "headless") {
456
456
  this.#page = await this.#browser.newPage();
457
- await applyStealthPatches(this.#browser, this.#page, { browserSession: null, override: null });
457
+ await applyStealthPatches(this.#browser, this.#page, { browserSession: null, override: null }, payload.geo);
458
458
  await applyViewport(this.#page, payload.viewport);
459
459
  if (payload.dialogs) this.#applyDialogPolicy(payload.dialogs);
460
460
  if (payload.url) {
@@ -233,6 +233,12 @@ export class BrowserTool implements AgentTool<typeof browserSchema, BrowserToolD
233
233
  }
234
234
  : undefined,
235
235
  appArgs: params.app?.args,
236
+ profileReuse:
237
+ (this.session.settings.get("browser.profileReuse") as "auto" | "opt-in" | undefined) ?? "auto",
238
+ geo: {
239
+ timezone: this.session.settings.get("browser.geo.timezone") as string | undefined,
240
+ locale: this.session.settings.get("browser.geo.locale") as string | undefined,
241
+ },
236
242
  signal,
237
243
  }),
238
244
  );
@@ -10,7 +10,7 @@ import type { GoalModeState, GoalRuntime } from "../goals";
10
10
  import { GoalTool } from "../goals/tools/goal-tool";
11
11
  import type { HindsightSessionState } from "../hindsight/state";
12
12
  import { LspTool } from "../lsp";
13
- import type { WorkflowGateEmitter } from "../modes/shared/agent-wire/unattended-session";
13
+ import type { WorkflowGateEmitter } from "../modes/shared/agent-wire/workflow-gate-broker";
14
14
  import type { PlanModeState } from "../plan-mode/state";
15
15
  import type { AgentRegistry } from "../registry/agent-registry";
16
16
  import type {
@@ -200,9 +200,10 @@ export interface AskRemoteReceipt {
200
200
  export type AskAnswerSourceResult = AskRemoteReceipt | string | undefined;
201
201
 
202
202
  /**
203
- * Source of remote answers. `awaitAnswer` remains the legacy wire for existing
204
- * integrations; typed sources opt into `awaitAnswerRequest`. This keeps an old
205
- * string-only source from accidentally acquiring acknowledgement authority.
203
+ * Source of remote answers for interactive asks. `awaitAnswer` remains the legacy
204
+ * wire for existing integrations; typed sources opt into `awaitAnswerRequest`.
205
+ * This keeps a string-only source from accidentally acquiring acknowledgement
206
+ * authority while allowing SDK-routed interactions to settle durably.
206
207
  */
207
208
  export interface AskAnswerSource {
208
209
  awaitAnswer(question: string, options: string[], signal?: AbortSignal): Promise<string | undefined>;
@@ -305,12 +306,11 @@ export interface ToolSession {
305
306
  getPlanModeState?: () => PlanModeState | undefined;
306
307
  /** Goal mode state (if active or paused) */
307
308
  getGoalModeState?: () => GoalModeState | undefined;
308
- /** Unattended workflow-gate emitter (present only when unattended mode is negotiated). */
309
+ /** SDK workflow-gate emitter, when a remote gate responder is connected. */
309
310
  getWorkflowGateEmitter?: () => WorkflowGateEmitter | undefined;
310
311
  /**
311
- * Optional remote answer source for interactive asks. When present, the ask
312
- * tool races the local UI selection against a remote answer (e.g. a Telegram
313
- * reply via the notifications SDK) so asks can be answered without RPC mode.
312
+ * Optional SDK-routed answer source for interactive asks. When present, the
313
+ * tool races the local UI selection against the remote SDK answer.
314
314
  * No-op when undefined: the ask path behaves exactly as before.
315
315
  */
316
316
  getAskAnswerSource?: () => AskAnswerSource | undefined;
@@ -0,0 +1,57 @@
1
+ // Permissions / notification headless tell.
2
+ //
3
+ // Headless Chrome reports `Notification.permission === "denied"` by default and
4
+ // a matching `permissions.query({name:"notifications"})` state, while a real
5
+ // user browser reports `"default"` / `"prompt"`. Detectors such as CreepJS key
6
+ // on this exact mismatch to flag headless. Normalize both to the human default.
7
+ //
8
+ // Technique adapted (reimplemented) from puppeteer-extra-plugin-stealth
9
+ // (navigator.permissions) — see STEALTH-THIRD-PARTY-NOTICES.md.
10
+
11
+ try {
12
+ if (typeof Notification !== "undefined" && Notification.permission === "denied") {
13
+ Object_defineProperty(Notification, "permission", {
14
+ get() {
15
+ return "default";
16
+ },
17
+ configurable: true,
18
+ });
19
+ }
20
+ } catch (e) {}
21
+
22
+ try {
23
+ const permissions = navigator.permissions;
24
+ const originalQuery = permissions && permissions.query;
25
+ if (originalQuery && typeof PermissionStatus !== "undefined") {
26
+ permissions.query = new Window_Proxy(originalQuery, {
27
+ apply(target, ctx, args) {
28
+ const params = args[0];
29
+ if (params && params.name === "notifications") {
30
+ return Promise_resolve(
31
+ Object_create(PermissionStatus.prototype, {
32
+ state: {
33
+ get() {
34
+ return "prompt";
35
+ },
36
+ enumerable: true,
37
+ configurable: true,
38
+ },
39
+ name: {
40
+ value: "notifications",
41
+ enumerable: true,
42
+ configurable: true,
43
+ },
44
+ onchange: {
45
+ value: null,
46
+ writable: true,
47
+ enumerable: true,
48
+ configurable: true,
49
+ },
50
+ }),
51
+ );
52
+ }
53
+ return Reflect.apply(target, ctx, args);
54
+ },
55
+ });
56
+ }
57
+ } catch (e) {}
@@ -0,0 +1,60 @@
1
+ // WebRTC IP-leak guard.
2
+ //
3
+ // Even with --force-webrtc-ip-handling-policy, RTCPeerConnection can surface
4
+ // ICE candidates that embed a raw local/public IP literal — a bot/deanon tell
5
+ // that CreepJS and others read. This wrapper strips ONLY candidates that carry
6
+ // a non-mDNS raw IP literal; mDNS ".local" candidates and all other WebRTC
7
+ // behavior (offer/answer, data channels) are left intact so real WebRTC keeps
8
+ // working.
9
+ //
10
+ // Technique adapted (reimplemented) from puppeteer-extra-plugin-stealth
11
+ // (navigator.mediaDevices / webrtc) — see STEALTH-THIRD-PARTY-NOTICES.md.
12
+
13
+ try {
14
+ const RTCP = typeof RTCPeerConnection !== "undefined" ? RTCPeerConnection : undefined;
15
+ if (RTCP && RTCP.prototype) {
16
+ // A raw IPv4/IPv6 literal that is NOT an mDNS ".local" hostname.
17
+ const rawIpInCandidate = (candidate) => {
18
+ if (typeof candidate !== "string" || candidate.length === 0) return false;
19
+ if (/\.local\b/i.test(candidate)) return false; // mDNS obfuscated — safe
20
+ const ipv4 = /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/;
21
+ const ipv6 = /\b(?:[0-9a-f]{1,4}:){2,}[0-9a-f]{0,4}\b/i;
22
+ return ipv4.test(candidate) || ipv6.test(candidate);
23
+ };
24
+
25
+ const origAddEventListener = RTCP.prototype.addEventListener;
26
+ RTCP.prototype.addEventListener = new Window_Proxy(origAddEventListener, {
27
+ apply(target, ctx, args) {
28
+ if (args[0] === "icecandidate" && typeof args[1] === "function") {
29
+ const userCb = args[1];
30
+ const wrapped = function (event) {
31
+ if (event && event.candidate && rawIpInCandidate(event.candidate.candidate)) {
32
+ return; // drop the raw-IP candidate; keep gathering others
33
+ }
34
+ return Reflect.apply(userCb, this, arguments);
35
+ };
36
+ return Reflect.apply(target, ctx, [args[0], wrapped, args[2]]);
37
+ }
38
+ return Reflect.apply(target, ctx, args);
39
+ },
40
+ });
41
+
42
+ // Also guard the onicecandidate setter path.
43
+ const desc = Object_getOwnPropertyDescriptor(RTCP.prototype, "onicecandidate");
44
+ if (desc && desc.set) {
45
+ Object_defineProperty(RTCP.prototype, "onicecandidate", {
46
+ configurable: true,
47
+ enumerable: desc.enumerable,
48
+ get: desc.get,
49
+ set(cb) {
50
+ if (typeof cb !== "function") return desc.set.call(this, cb);
51
+ const wrapped = function (event) {
52
+ if (event && event.candidate && rawIpInCandidate(event.candidate.candidate)) return;
53
+ return Reflect.apply(cb, this, arguments);
54
+ };
55
+ return desc.set.call(this, wrapped);
56
+ },
57
+ });
58
+ }
59
+ }
60
+ } catch (e) {}
@@ -23,6 +23,8 @@ import {
23
23
  } from "./render-utils";
24
24
  import { type SubagentSnapshot, type SubagentToolDetails, subagentAwaitRenderedStateSignature } from "./subagent";
25
25
 
26
+ export { subagentAwaitRenderedStateSignature } from "./subagent";
27
+
26
28
  const PREVIEW_LINES_COLLAPSED = 1;
27
29
  const PREVIEW_LINES_EXPANDED = 4;
28
30
  const PREVIEW_LINE_WIDTH = 80;
@@ -128,7 +130,7 @@ function renderSubagentStatusLine(snapshot: SubagentSnapshot, theme: Theme, spin
128
130
  theme,
129
131
  snapshot.status === "running" ? spinnerFrame : undefined,
130
132
  );
131
- const id = theme.fg("muted", snapshot.id);
133
+ const id = theme.fg("muted", truncateToWidth(replaceTabs(snapshot.id), PREVIEW_LINE_WIDTH, Ellipsis.Unicode));
132
134
  const status = theme.fg("dim", snapshot.status);
133
135
  const duration = theme.fg("dim", formatDuration(snapshot.durationMs));
134
136
  return `${icon} ${id} ${status} ${duration}`;
@@ -195,7 +197,12 @@ function renderSubagentSnapshotBody(snapshot: SubagentSnapshot, expanded: boolea
195
197
  }
196
198
  }
197
199
 
198
- if (snapshot.guidance) lines.push(` ${theme.fg("dim", snapshot.guidance)}`);
200
+ if (snapshot.guidance) {
201
+ for (const line of getPreviewLines(snapshot.guidance, 1, PREVIEW_LINE_WIDTH, Ellipsis.Unicode)) {
202
+ lines.push(` ${theme.fg("dim", replaceTabs(line))}`);
203
+ }
204
+ }
205
+
199
206
  return lines;
200
207
  }
201
208
 
@@ -218,6 +225,7 @@ export const subagentToolRenderer = {
218
225
  }
219
226
 
220
227
  const runningCount = subagents.filter(s => s.status === "running").length;
228
+ const interrupted = result.details?.interrupted === true;
221
229
 
222
230
  // Each snapshot's rendered-state signature is constant for this component
223
231
  // instance, so compute them at most once; the heavy per-subagent bodies are
@@ -232,11 +240,12 @@ export const subagentToolRenderer = {
232
240
  // it is never gated by the heavy body cache.
233
241
  const header = renderStatusLine(
234
242
  {
235
- icon: runningCount > 0 ? "info" : "success",
236
- spinnerFrame: runningCount > 0 ? options.spinnerFrame : undefined,
237
- title: "Subagent",
238
- description:
239
- runningCount > 0
243
+ icon: interrupted ? "warning" : runningCount > 0 ? "info" : "success",
244
+ spinnerFrame: !interrupted && runningCount > 0 ? options.spinnerFrame : undefined,
245
+ title: interrupted ? "Subagent await interrupted" : "Subagent",
246
+ description: interrupted
247
+ ? "child subagents continue"
248
+ : runningCount > 0
240
249
  ? `awaiting ${runningCount} of ${subagents.length}`
241
250
  : `${subagents.length} ${subagents.length === 1 ? "subagent" : "subagents"}`,
242
251
  },
@@ -249,7 +258,9 @@ export const subagentToolRenderer = {
249
258
  out.push(truncateToWidth(` ${theme.fg("dim", "(ctrl+s to observe sessions)")}`, width, Ellipsis.Omit));
250
259
  }
251
260
 
252
- snapshotSignatures ??= subagents.map(snapshot => subagentAwaitRenderedStateSignature([snapshot]));
261
+ snapshotSignatures ??= subagents.map(snapshot =>
262
+ subagentAwaitRenderedStateSignature([snapshot], result.details),
263
+ );
253
264
  subagents.forEach((snapshot, index) => {
254
265
  // Fresh per-subagent status line (cheap), then the cached heavy body.
255
266
  out.push(