@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
package/src/tools/ask.ts CHANGED
@@ -758,9 +758,14 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
758
758
  if (receipt) await receipt.settle(settlement);
759
759
  };
760
760
  const gateEmitter = this.session.getWorkflowGateEmitter?.();
761
- const canUseWorkflowGate = gateEmitter?.isUnattended() === true;
762
-
763
- // Headless fallback: unattended workflow gates are the non-TUI answer path.
761
+ // A durable workflow-gate emitter now exists for every session, and its
762
+ // isUnattended() is always true, so it can no longer signal "no local UI".
763
+ // The workflow gate is only the headless (non-TUI) answer path: when a real
764
+ // interactive UI is present, prefer it — otherwise attended TUI asks would
765
+ // route to emitGate() and hang forever waiting on a remote responder.
766
+ const hasInteractiveUi = context?.hasUI === true && !!context.ui;
767
+ const canUseWorkflowGate = !hasInteractiveUi && gateEmitter?.isUnattended() === true;
768
+ // Headless fallback: SDK workflow gates are the non-TUI answer path.
764
769
  if (!canUseWorkflowGate && (!context?.hasUI || !context.ui)) {
765
770
  context?.abort();
766
771
  throw new ToolAbortError("Ask tool requires interactive mode");
@@ -772,9 +777,9 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
772
777
  if (!extensionUi) throw new ToolAbortError("Ask tool requires interactive mode");
773
778
  const source = this.session.getAskAnswerSource?.();
774
779
  if (!source) return extensionUi.select(prompt, options, dialogOptions);
775
- // Race the local UI against a remote answer (e.g. a Telegram reply via the
776
- // notifications SDK) so asks can be answered without RPC mode. First valid
777
- // answer wins; the loser is aborted so neither side is left hanging:
780
+ // Race the local UI against a remote answer (e.g. an SDK reply) so asks
781
+ // can be answered without local UI interaction. The first valid answer
782
+ // wins; the loser is aborted so neither side is left hanging:
778
783
  // - local wins -> abort the remote source (marks the action resolved-locally)
779
784
  // - remote wins -> abort the local selector so the TUI dialog actually closes
780
785
  const remoteController = new AbortController();
@@ -975,8 +980,8 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
975
980
  ) => {
976
981
  const rawOptionLabels = q.options.map(o => o.label);
977
982
  const questionIndex = params.questions.indexOf(q);
978
- // Unattended (#316/#323/G011): route the question through the workflow-gate
979
- // emitter instead of the interactive UI; the external agent answers over RPC.
983
+ // Route headless asks through the SDK workflow-gate emitter; a connected
984
+ // SDK responder supplies the durable answer instead of an interactive UI.
980
985
  if (gateEmitter && canUseWorkflowGate) {
981
986
  const gateQuestion: AskGateQuestion = {
982
987
  id: q.id,
@@ -0,0 +1,26 @@
1
+ # Stealth Third-Party Notices
2
+
3
+ Per the approved plan, anti-detection evasions are **reimplemented/adapted** as
4
+ first-party code (no vendored source tree, no runtime dependency). This file
5
+ records provenance for evasion techniques whose semantics were informed by the
6
+ following open-source projects. Each individual reimplemented script also
7
+ carries an attributing header comment.
8
+
9
+ | Source project | License | Techniques referenced |
10
+ |----------------|---------|-----------------------|
11
+ | rebrowser-patches | MIT | CDP `Runtime.enable` leak mitigation (data-driven; landed only if the offline baseline shows a runtime/CDP leak signal) |
12
+ | puppeteer-extra-plugin-stealth | MIT | Fingerprint evasion signal set (navigator/webgl/plugins/permissions/webrtc surfaces) overlapping and complementing the existing injection scripts |
13
+ | fingerprint-suite (fingerprint-injector / generator) | Apache-2.0 | Coherent fingerprint attribute generation ideas |
14
+
15
+ No source files from these projects are copied into this repository. Only the
16
+ observable behaviors/signal semantics were reimplemented against `puppeteer-core`
17
+ after being confirmed by the offline benchmark suite
18
+ (`test/fixtures/stealth-detectors/`, `src/tools/browser/benchmark/`).
19
+
20
+ ## Detector fixtures
21
+
22
+ The offline detector fixtures under `test/fixtures/stealth-detectors/` are
23
+ first-party probes modeled on the public boolean signal sets used by
24
+ `bot.sannysoft.com` and CreepJS. They are self-contained (inline JS, zero
25
+ network) and are not verbatim copies of those sites. See that directory's
26
+ `MANIFEST.json` for the refresh procedure.
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Offline stealth-detector benchmark model.
3
+ *
4
+ * Pure, deterministic logic for the anti-detection benchmark suite: it converts
5
+ * a browser-collected fingerprint probe into a normalized per-detector verdict,
6
+ * implements the falsifiable CI gate (see plan Phase 1 / R-T3), and renders a
7
+ * before/after report. No browser or network access lives here so it can be
8
+ * unit-tested deterministically; the harness that drives the real browser tool
9
+ * against vendored detector fixtures feeds its output into these functions.
10
+ */
11
+
12
+ export type SignalStatus = "pass" | "fail";
13
+
14
+ /** A single boolean fingerprint signal, e.g. "navigator.webdriver is undefined". */
15
+ export interface DetectorSignal {
16
+ /** Stable identifier, used to diff across runs. */
17
+ id: string;
18
+ /** Human-readable label for the report. */
19
+ label: string;
20
+ /** `pass` means the signal looks human; `fail` means it leaks automation. */
21
+ status: SignalStatus;
22
+ /** Optional observed value for debugging. */
23
+ detail?: string;
24
+ }
25
+
26
+ export type AutomatedVerdict = "human" | "bot" | "unknown";
27
+
28
+ /** Normalized result for one detector (e.g. bot.sannysoft, CreepJS). */
29
+ export interface DetectorResult {
30
+ detector: string;
31
+ signals: DetectorSignal[];
32
+ /** Overall automated/not classification when the detector exposes one. */
33
+ automatedVerdict: AutomatedVerdict;
34
+ /** Absolute trust score (observability-only, never a hard gate). */
35
+ trustScore: number | null;
36
+ }
37
+
38
+ /** Shape emitted by the offline probe fixture on `window.__stealthProbe`. */
39
+ export interface RawProbe {
40
+ detector: string;
41
+ signals: Array<{ id: string; label: string; pass: boolean; detail?: string }>;
42
+ automatedVerdict?: AutomatedVerdict;
43
+ trustScore?: number | null;
44
+ }
45
+
46
+ /** Convert a raw browser-collected probe into a normalized {@link DetectorResult}. */
47
+ export function parseProbe(raw: RawProbe): DetectorResult {
48
+ if (!raw || typeof raw.detector !== "string" || !Array.isArray(raw.signals)) {
49
+ throw new Error("invalid stealth probe payload");
50
+ }
51
+ const signals: DetectorSignal[] = raw.signals.map(s => ({
52
+ id: s.id,
53
+ label: s.label,
54
+ status: s.pass ? "pass" : "fail",
55
+ ...(s.detail !== undefined ? { detail: s.detail } : {}),
56
+ }));
57
+ return {
58
+ detector: raw.detector,
59
+ signals,
60
+ automatedVerdict: raw.automatedVerdict ?? "unknown",
61
+ trustScore: raw.trustScore ?? null,
62
+ };
63
+ }
64
+
65
+ function passCount(result: DetectorResult): number {
66
+ return result.signals.filter(s => s.status === "pass").length;
67
+ }
68
+
69
+ function failingSignalIds(result: DetectorResult): Set<string> {
70
+ return new Set(result.signals.filter(s => s.status === "fail").map(s => s.id));
71
+ }
72
+
73
+ export interface GateOutcome {
74
+ pass: boolean;
75
+ reasons: string[];
76
+ }
77
+
78
+ /**
79
+ * Falsifiable CI gate (plan Phase 1 / R-T3, R-C1, R-C2):
80
+ * (a) zero failing signals in the current run, AND
81
+ * current pass-count >= baseline pass-count (no silent regression), AND
82
+ * (b) either at least one signal that failed at baseline now passes,
83
+ * OR the baseline was already all-green (empty-gap regression lock).
84
+ * The absolute `trustScore` is never consulted here; it is observability-only.
85
+ */
86
+ export function evaluateGate(baseline: DetectorResult, current: DetectorResult): GateOutcome {
87
+ const reasons: string[] = [];
88
+ const currentFail = failingSignalIds(current);
89
+ if (currentFail.size > 0) {
90
+ reasons.push(
91
+ `${current.detector}: ${currentFail.size} signal(s) still leak automation: ${[...currentFail].join(", ")}`,
92
+ );
93
+ }
94
+
95
+ const baselinePass = passCount(baseline);
96
+ const currentPass = passCount(current);
97
+ if (currentPass < baselinePass) {
98
+ reasons.push(`${current.detector}: pass-count regressed (${currentPass} < baseline ${baselinePass})`);
99
+ }
100
+
101
+ const baselineFail = failingSignalIds(baseline);
102
+ const currentPassIds = new Set(current.signals.filter(s => s.status === "pass").map(s => s.id));
103
+ const newlyPassing = [...baselineFail].filter(id => currentPassIds.has(id));
104
+ const baselineAllGreen = baselineFail.size === 0;
105
+ if (!baselineAllGreen && newlyPassing.length === 0) {
106
+ reasons.push(`${current.detector}: no baseline-failing signal was fixed (improvement required)`);
107
+ }
108
+
109
+ return { pass: reasons.length === 0, reasons };
110
+ }
111
+
112
+ /** Aggregate gate across every detector; passes only when all detectors pass. */
113
+ export function evaluateSuiteGate(
114
+ baseline: readonly DetectorResult[],
115
+ current: readonly DetectorResult[],
116
+ ): GateOutcome {
117
+ const reasons: string[] = [];
118
+ const currentByName = new Map(current.map(r => [r.detector, r]));
119
+ for (const base of baseline) {
120
+ const now = currentByName.get(base.detector);
121
+ if (!now) {
122
+ reasons.push(`${base.detector}: missing from current run`);
123
+ continue;
124
+ }
125
+ reasons.push(...evaluateGate(base, now).reasons);
126
+ }
127
+ return { pass: reasons.length === 0, reasons };
128
+ }
129
+
130
+ /** Render a deterministic before/after markdown report for one run. */
131
+ export function renderReport(baseline: readonly DetectorResult[], current: readonly DetectorResult[]): string {
132
+ const lines: string[] = ["# Stealth Benchmark Report", ""];
133
+ const gate = evaluateSuiteGate(baseline, current);
134
+ lines.push(`Gate: ${gate.pass ? "PASS" : "FAIL"}`, "");
135
+ const currentByName = new Map(current.map(r => [r.detector, r]));
136
+ for (const base of baseline) {
137
+ const now = currentByName.get(base.detector);
138
+ lines.push(`## ${base.detector}`);
139
+ lines.push(
140
+ `- Automated verdict: ${base.automatedVerdict} -> ${now?.automatedVerdict ?? "n/a"}`,
141
+ `- Trust score (observability): ${base.trustScore ?? "n/a"} -> ${now?.trustScore ?? "n/a"}`,
142
+ `- Passing signals: ${passCount(base)} -> ${now ? passCount(now) : "n/a"} of ${base.signals.length}`,
143
+ "",
144
+ "| Signal | Baseline | Current |",
145
+ "|--------|----------|---------|",
146
+ );
147
+ for (const sig of base.signals) {
148
+ const nowSig = now?.signals.find(s => s.id === sig.id);
149
+ lines.push(`| ${sig.label} | ${sig.status} | ${nowSig?.status ?? "n/a"} |`);
150
+ }
151
+ lines.push("");
152
+ }
153
+ if (!gate.pass) {
154
+ lines.push("## Blocking reasons", ...gate.reasons.map(r => `- ${r}`), "");
155
+ }
156
+ return lines.join("\n");
157
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Offline stealth benchmark harness (plan Phase 1).
3
+ *
4
+ * Drives a puppeteer Page (already stealth-patched by the browser tool) against
5
+ * self-contained offline detector fixtures and collects normalized results. The
6
+ * navigation/evaluation is injected so this stays unit-testable without a real
7
+ * browser; the integration test wires a live puppeteer Page.
8
+ */
9
+
10
+ import type { Page } from "puppeteer-core";
11
+ import type { DetectorResult, RawProbe } from "./detector-report";
12
+ import { parseProbe } from "./detector-report";
13
+
14
+ /** Minimal surface of a puppeteer Page that the harness needs. */
15
+ export type SuitePage = Pick<Page, "goto" | "evaluate">;
16
+
17
+ export interface SuiteFixture {
18
+ /** file:// (or http://) URL of the offline detector fixture. */
19
+ url: string;
20
+ }
21
+
22
+ /** Read the probe the fixture placed on `window.__stealthProbe`. */
23
+ function readProbe(): RawProbe {
24
+ // Runs in the browser context.
25
+ const probe = (globalThis as unknown as { __stealthProbe?: RawProbe }).__stealthProbe;
26
+ if (!probe) throw new Error("fixture did not expose window.__stealthProbe");
27
+ return probe;
28
+ }
29
+
30
+ /** Navigate to each fixture and collect a normalized DetectorResult per fixture. */
31
+ export async function runOfflineSuite(page: SuitePage, fixtures: readonly SuiteFixture[]): Promise<DetectorResult[]> {
32
+ const results: DetectorResult[] = [];
33
+ for (const fixture of fixtures) {
34
+ await page.goto(fixture.url, { waitUntil: "load" });
35
+ const raw = await page.evaluate(readProbe);
36
+ results.push(parseProbe(raw));
37
+ }
38
+ return results;
39
+ }
@@ -14,11 +14,12 @@ import stealthWebglScript from "../puppeteer/05_stealth_webgl.txt" with { type:
14
14
  import stealthScreenScript from "../puppeteer/06_stealth_screen.txt" with { type: "text" };
15
15
  import stealthFontsScript from "../puppeteer/07_stealth_fonts.txt" with { type: "text" };
16
16
  import stealthAudioScript from "../puppeteer/08_stealth_audio.txt" with { type: "text" };
17
- import stealthLocaleScript from "../puppeteer/09_stealth_locale.txt" with { type: "text" };
18
17
  import stealthPluginsScript from "../puppeteer/10_stealth_plugins.txt" with { type: "text" };
19
18
  import stealthHardwareScript from "../puppeteer/11_stealth_hardware.txt" with { type: "text" };
20
19
  import stealthCodecsScript from "../puppeteer/12_stealth_codecs.txt" with { type: "text" };
21
20
  import stealthWorkerScript from "../puppeteer/13_stealth_worker.txt" with { type: "text" };
21
+ import stealthPermissionsScript from "../puppeteer/14_stealth_permissions.txt" with { type: "text" };
22
+ import stealthWebrtcScript from "../puppeteer/15_stealth_webrtc.txt" with { type: "text" };
22
23
  import { ToolError } from "../tool-errors";
23
24
 
24
25
  export const DEFAULT_VIEWPORT = { width: 1365, height: 768, deviceScaleFactor: 1.25 };
@@ -35,7 +36,6 @@ const STEALTH_IGNORE_DEFAULT_ARGS = [
35
36
  "--disable-default-apps",
36
37
  "--disable-component-extensions-with-background-pages",
37
38
  ];
38
- const STEALTH_ACCEPT_LANGUAGE = "en-US,en";
39
39
 
40
40
  const USER_AGENT_TARGET_TIMEOUT_MS = 5_000;
41
41
  const USER_AGENT_TARGET_TYPES = new Set(["page", "webview", "background_page"]);
@@ -230,6 +230,22 @@ function resolveSystemChromium(): string | undefined {
230
230
  export interface LaunchHeadlessOptions {
231
231
  headless: boolean;
232
232
  viewport?: { width: number; height: number; deviceScaleFactor?: number };
233
+ /**
234
+ * Optional isolated warm-up profile directory (see profile-reuse.ts). When
235
+ * set, the headless browser launches against this isolated copy of the user's
236
+ * real Chrome profile so sessions inherit real cookies/storage/cache for
237
+ * stronger stealth. Callers resolve this via resolveProfileReuse and MUST
238
+ * pass an isolated copy, never a live profile dir.
239
+ */
240
+ profileWarmupDir?: string;
241
+ /**
242
+ * Optional opt-in geo alignment (see settings browser.geo.*). When set, the
243
+ * browser is launched with a matching timezone (TZ env) and/or UI language
244
+ * (--lang) at the source. Default unset = strict no-op: the real environment
245
+ * timezone/locale are preserved (never half-spoof). Only align these to a
246
+ * coherent value (e.g. one matching a configured proxy egress).
247
+ */
248
+ geo?: { timezone?: string; locale?: string };
233
249
  }
234
250
 
235
251
  export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promise<Browser> {
@@ -244,8 +260,16 @@ export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promis
244
260
  "--no-sandbox",
245
261
  "--disable-setuid-sandbox",
246
262
  "--disable-blink-features=AutomationControlled",
263
+ // Suppress WebRTC leaking the real local/public IP via ICE candidates
264
+ // (a common bot tell). Keeps WebRTC functional over the default public
265
+ // interface only; 15_stealth_webrtc.txt adds a candidate-level guard.
266
+ "--force-webrtc-ip-handling-policy=default_public_interface_only",
247
267
  `--window-size=${initialViewport.width},${initialViewport.height}`,
248
268
  ];
269
+ if (opts.profileWarmupDir) {
270
+ // Isolated copy only (caller guarantees). Never point this at a live profile.
271
+ launchArgs.push(`--user-data-dir=${opts.profileWarmupDir}`);
272
+ }
249
273
  const proxy = process.env.PUPPETEER_PROXY;
250
274
  if (proxy) {
251
275
  launchArgs.push(`--proxy-server=${proxy}`);
@@ -256,15 +280,22 @@ export async function launchHeadlessBrowser(opts: LaunchHeadlessOptions): Promis
256
280
  launchArgs.push("--proxy-bypass-list=<-loopback>");
257
281
  }
258
282
  }
283
+ // Opt-in geo alignment (default no-op). Locale via --lang at the source;
284
+ // timezone via the TZ env passed to the Chrome subprocess.
285
+ if (opts.geo?.locale) {
286
+ launchArgs.push(`--lang=${opts.geo.locale}`);
287
+ }
259
288
  const ignoreCert = process.env.PUPPETEER_PROXY_IGNORE_CERT_ERRORS?.toLowerCase();
260
289
  if (ignoreCert === "true" || ignoreCert === "1" || ignoreCert === "yes" || ignoreCert === "on") {
261
290
  launchArgs.push("--ignore-certificate-errors");
262
291
  }
292
+ const launchEnv = opts.geo?.timezone ? { ...process.env, TZ: opts.geo.timezone } : undefined;
263
293
  return await puppeteer.launch({
264
294
  headless: opts.headless,
265
295
  defaultViewport: opts.headless ? initialViewport : null,
266
296
  executablePath: await ensureChromiumExecutable(),
267
297
  args: launchArgs,
298
+ ...(launchEnv ? { env: launchEnv } : {}),
268
299
  ignoreDefaultArgs: [...STEALTH_IGNORE_DEFAULT_ARGS],
269
300
  protocolTimeout: BROWSER_PROTOCOL_TIMEOUT_MS,
270
301
  });
@@ -308,6 +339,20 @@ export interface UserAgentOverride {
308
339
  };
309
340
  }
310
341
 
342
+ function acceptLanguageForLocale(locale: string): string {
343
+ const canonicalLocale = Intl.getCanonicalLocales(locale)[0]!;
344
+ const baseLanguage = canonicalLocale.split("-")[0]!;
345
+ return canonicalLocale === baseLanguage ? canonicalLocale : `${canonicalLocale},${baseLanguage}`;
346
+ }
347
+
348
+ async function resolveAcceptLanguage(page: Page, locale?: string): Promise<string> {
349
+ if (locale) return acceptLanguageForLocale(locale);
350
+ const languages = await page.evaluate(() => [...navigator.languages]);
351
+ const acceptLanguage = languages.filter(Boolean).join(",");
352
+ if (!acceptLanguage) throw new ToolError("Chromium reported no native navigator languages");
353
+ return acceptLanguage;
354
+ }
355
+
311
356
  function resolvePageClient(page: Page): PuppeteerCdpClient | null {
312
357
  const pageWithClient = page as Page & {
313
358
  _client?: (() => PuppeteerCdpClient) | PuppeteerCdpClient;
@@ -362,7 +407,7 @@ function patchSourceUrl(page: Page): void {
362
407
  };
363
408
  }
364
409
 
365
- async function resolveUserAgentOverride(page: Page): Promise<UserAgentOverride> {
410
+ async function resolveUserAgentOverride(page: Page, locale?: string): Promise<UserAgentOverride> {
366
411
  const rawUserAgent = await page.browser().userAgent();
367
412
  let userAgent = rawUserAgent.replace("HeadlessChrome/", "Chrome/");
368
413
  if (userAgent.includes("Linux") && !userAgent.includes("Android")) {
@@ -413,11 +458,13 @@ async function resolveUserAgentOverride(page: Page): Promise<UserAgentOverride>
413
458
  brands[order[0]!] = { brand: greaseyBrand, version: "99" };
414
459
  brands[order[1]!] = { brand: "Chromium", version: String(majorVersion) };
415
460
  brands[order[2]!] = { brand: "Google Chrome", version: String(majorVersion) };
461
+ const acceptLanguage = await resolveAcceptLanguage(page, locale);
416
462
 
417
463
  return {
418
464
  userAgent,
419
465
  platform,
420
- acceptLanguage: STEALTH_ACCEPT_LANGUAGE,
466
+ acceptLanguage,
467
+
421
468
  userAgentMetadata: {
422
469
  brands,
423
470
  fullVersion: uaVersion,
@@ -436,7 +483,17 @@ function wrapSession(session: CDPSession): PuppeteerCdpClient {
436
483
  };
437
484
  }
438
485
 
439
- async function sendUserAgentOverride(client: PuppeteerCdpClient, override: UserAgentOverride): Promise<void> {
486
+ async function sendUserAgentOverride(
487
+ client: PuppeteerCdpClient,
488
+ override: UserAgentOverride,
489
+ strict = false,
490
+ ): Promise<void> {
491
+ if (strict) {
492
+ await client.send("Network.enable");
493
+ await client.send("Network.setUserAgentOverride", override as unknown as Record<string, unknown>);
494
+ await client.send("Emulation.setUserAgentOverride", override as unknown as Record<string, unknown>);
495
+ return;
496
+ }
440
497
  try {
441
498
  await client.send("Network.enable");
442
499
  } catch {}
@@ -464,28 +521,52 @@ export interface UserAgentSession {
464
521
  /** Configure UA override on the browser + auto-attach to new targets. */
465
522
  async function configureUserAgentTargets(
466
523
  browser: Browser,
467
- state: { browserSession: CDPSession | null; override: UserAgentOverride },
524
+ state: { browserSession: CDPSession | null; override: UserAgentOverride; locale?: string },
525
+
468
526
  targetTimeoutMs = USER_AGENT_TARGET_TIMEOUT_MS,
469
527
  ): Promise<void> {
470
528
  if (!state.browserSession) {
471
529
  state.browserSession = await browser.target().createCDPSession();
472
530
  await state.browserSession.send("Target.setAutoAttach", {
473
531
  autoAttach: true,
474
- waitForDebuggerOnStart: false,
532
+ waitForDebuggerOnStart: true,
475
533
  flatten: true,
476
534
  });
477
535
  state.browserSession.on(
478
536
  "Target.attachedToTarget",
479
- async (event: { sessionId: string; targetInfo?: { type?: string } }) => {
480
- if (!targetInfoSupportsUserAgentOverride(event.targetInfo)) return;
537
+ async (event: { sessionId: string; targetInfo?: { targetId?: string; type?: string } }) => {
538
+ if (!targetInfoSupportsUserAgentOverride(event.targetInfo)) {
539
+ const connection = state.browserSession?.connection();
540
+ await connection
541
+ ?.session(event.sessionId)
542
+ ?.send("Runtime.runIfWaitingForDebugger")
543
+ .catch(() => undefined);
544
+ return;
545
+ }
481
546
  const connection = state.browserSession?.connection();
482
547
  const session = connection?.session(event.sessionId);
483
548
  if (!session) return;
484
- await withSoftTimeout(
485
- sendUserAgentOverride(wrapSession(session), state.override),
486
- targetTimeoutMs,
487
- "new target user-agent override",
488
- );
549
+ try {
550
+ if (state.locale) {
551
+ await applyClientOverrides(wrapSession(session), state.override, state.locale, true);
552
+ } else {
553
+ await withSoftTimeout(
554
+ sendUserAgentOverride(wrapSession(session), state.override),
555
+ targetTimeoutMs,
556
+ "new target user-agent override",
557
+ );
558
+ }
559
+ await session.send("Runtime.runIfWaitingForDebugger");
560
+ } catch (error) {
561
+ if (event.targetInfo?.targetId) {
562
+ await state.browserSession
563
+ ?.send("Target.closeTarget", { targetId: event.targetInfo.targetId })
564
+ .catch(() => undefined);
565
+ }
566
+ logger.debug("Closed target after geo override failure", {
567
+ error: error instanceof Error ? error.message : String(error),
568
+ });
569
+ }
489
570
  },
490
571
  );
491
572
  }
@@ -511,14 +592,33 @@ function targetInfoSupportsUserAgentOverride(targetInfo: { type?: string } | und
511
592
  }
512
593
 
513
594
  async function applyTargetUserAgentOverride(target: Target, override: UserAgentOverride): Promise<void> {
595
+ await applyTargetOverrides(target, override);
596
+ }
597
+
598
+ async function applyTargetOverrides(
599
+ target: Target,
600
+ override: UserAgentOverride,
601
+ locale?: string,
602
+ strict = false,
603
+ ): Promise<void> {
514
604
  const session = await target.createCDPSession();
515
605
  try {
516
- await sendUserAgentOverride(wrapSession(session), override);
606
+ await applyClientOverrides(wrapSession(session), override, locale, strict);
517
607
  } finally {
518
608
  await session.detach().catch(() => undefined);
519
609
  }
520
610
  }
521
611
 
612
+ async function applyClientOverrides(
613
+ client: PuppeteerCdpClient,
614
+ override: UserAgentOverride,
615
+ locale?: string,
616
+ strict = false,
617
+ ): Promise<void> {
618
+ await sendUserAgentOverride(client, override, strict);
619
+ if (locale) await client.send("Emulation.setLocaleOverride", { locale });
620
+ }
621
+
522
622
  async function withSoftTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T | undefined> {
523
623
  let timeout: NodeJS.Timeout | undefined;
524
624
  const timeoutPromise = new Promise<undefined>(resolve => {
@@ -550,11 +650,12 @@ const STEALTH_PATCH_SCRIPTS = [
550
650
  stealthScreenScript,
551
651
  stealthFontsScript,
552
652
  stealthAudioScript,
553
- stealthLocaleScript,
554
653
  stealthPluginsScript,
555
654
  stealthHardwareScript,
556
655
  stealthCodecsScript,
557
656
  stealthWorkerScript,
657
+ stealthPermissionsScript,
658
+ stealthWebrtcScript,
558
659
  ];
559
660
 
560
661
  function buildStealthInjectionScript(scripts: readonly string[] = STEALTH_PATCH_SCRIPTS): string {
@@ -624,17 +725,21 @@ export async function applyStealthPatches(
624
725
  browser: Browser,
625
726
  page: Page,
626
727
  state: { browserSession: CDPSession | null; override: UserAgentOverride | null },
728
+ geo?: { readonly timezone?: string; readonly locale?: string },
627
729
  ): Promise<void> {
628
730
  patchSourceUrl(page);
629
731
  if (!state.override) {
630
- state.override = await resolveUserAgentOverride(page);
732
+ state.override = await resolveUserAgentOverride(page, geo?.locale);
631
733
  }
632
734
  const client = resolvePageClient(page);
633
735
  if (client) {
634
- await sendUserAgentOverride(client, state.override);
736
+ await sendUserAgentOverride(client, state.override, Boolean(geo?.locale));
635
737
  }
636
- const targetState = { browserSession: state.browserSession, override: state.override };
738
+ const targetState = { browserSession: state.browserSession, override: state.override, locale: geo?.locale };
637
739
  await configureUserAgentTargets(browser, targetState);
740
+ if (client && geo?.locale) {
741
+ await client.send("Emulation.setLocaleOverride", { locale: geo.locale });
742
+ }
638
743
  state.browserSession = targetState.browserSession;
639
744
  await injectStealthScripts(page);
640
745
  }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Default Chrome profile discovery (plan Phase 2 wiring).
3
+ *
4
+ * Locates the user's real Chrome "Default" profile directory per OS so the
5
+ * default headless launch path can warm up synthetic sessions from an isolated
6
+ * copy of it (see profile-warmup.ts + profile-posture.ts). Pure path logic with
7
+ * an injectable existence check, so it is unit-testable without touching the
8
+ * real filesystem.
9
+ */
10
+
11
+ import * as os from "node:os";
12
+ import * as path from "node:path";
13
+
14
+ export interface DiscoveryEnv {
15
+ platform: NodeJS.Platform;
16
+ home: string;
17
+ /** Injectable for tests; defaults to fs.existsSync at call sites. */
18
+ exists: (p: string) => boolean;
19
+ /** Windows LOCALAPPDATA override (tests / non-default installs). */
20
+ localAppData?: string;
21
+ }
22
+
23
+ /** Candidate Chrome user-data roots for the platform (most common first). */
24
+ export function chromeUserDataRoots(env: DiscoveryEnv): string[] {
25
+ switch (env.platform) {
26
+ case "darwin":
27
+ return [path.join(env.home, "Library", "Application Support", "Google", "Chrome")];
28
+ case "win32": {
29
+ const localAppData = env.localAppData ?? path.join(env.home, "AppData", "Local");
30
+ return [path.join(localAppData, "Google", "Chrome", "User Data")];
31
+ }
32
+ default:
33
+ return [path.join(env.home, ".config", "google-chrome"), path.join(env.home, ".config", "chromium")];
34
+ }
35
+ }
36
+
37
+ export interface DiscoveredProfile {
38
+ userDataDir: string;
39
+ profileDirectory: string;
40
+ profileDir: string;
41
+ }
42
+
43
+ /**
44
+ * Discover the default Chrome profile, or null when none is present.
45
+ * Only returns a profile whose directory actually exists.
46
+ */
47
+ export function discoverDefaultChromeProfile(
48
+ env: DiscoveryEnv,
49
+ profileDirectory = "Default",
50
+ ): DiscoveredProfile | null {
51
+ for (const userDataDir of chromeUserDataRoots(env)) {
52
+ const profileDir = path.join(userDataDir, profileDirectory);
53
+ if (env.exists(profileDir)) {
54
+ return { userDataDir, profileDirectory, profileDir };
55
+ }
56
+ }
57
+ return null;
58
+ }
59
+
60
+ /** Convenience wrapper using the live OS environment + fs. */
61
+ export function defaultDiscoveryEnv(exists: (p: string) => boolean): DiscoveryEnv {
62
+ return { platform: process.platform, home: os.homedir(), exists };
63
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Auto-default profile-reuse posture decision (plan Phase 2, Intent Reconciliation).
3
+ *
4
+ * User-confirmed policy: when a usable real Chrome profile is available, the
5
+ * browser tool uses it automatically (AUTO), emits a warning, and falls back to
6
+ * synthetic stealth when unavailable. The posture is overridable via a setting.
7
+ * This is pure decision logic (no browser, no filesystem side effects) so the
8
+ * launch/attach layer can call it and it can be unit-tested deterministically.
9
+ */
10
+
11
+ /** Configured posture: `auto` (default) prefers the real profile; `opt-in` stays synthetic unless explicitly asked. */
12
+ export type ProfileReusePosture = "auto" | "opt-in";
13
+
14
+ export const DEFAULT_PROFILE_REUSE_POSTURE: ProfileReusePosture = "auto";
15
+
16
+ export interface PostureInputs {
17
+ /** Configured posture; defaults to `auto` when unset. */
18
+ posture?: ProfileReusePosture;
19
+ /** A usable real Chrome profile was detected and is attachable/safe to copy. */
20
+ realProfileAvailable: boolean;
21
+ /** Caller explicitly requested real-profile reuse for this run (e.g. app.browser === "chrome"). */
22
+ explicitlyRequested?: boolean;
23
+ }
24
+
25
+ export interface PostureDecision {
26
+ /** `real` = use the (isolated copy of the) real profile; `synthetic` = synthetic stealth session. */
27
+ mode: "real" | "synthetic";
28
+ /** User-facing warning to emit when real credentials/state are about to be used. */
29
+ warning: string | null;
30
+ /** Machine-readable reason for logs/audit. */
31
+ reason: "auto-real" | "explicit-real" | "synthetic-fallback" | "synthetic-opt-in";
32
+ }
33
+
34
+ /**
35
+ * Decide whether to reuse the real profile or run synthetic.
36
+ *
37
+ * - `auto` + available -> real (with warning)
38
+ * - `opt-in` + explicitly asked -> real (with warning)
39
+ * - `opt-in` (no explicit ask) -> synthetic
40
+ * - not available -> synthetic (fallback), even under `auto`
41
+ */
42
+ export function decideProfilePosture(inputs: PostureInputs): PostureDecision {
43
+ const posture = inputs.posture ?? DEFAULT_PROFILE_REUSE_POSTURE;
44
+ const warning =
45
+ "Using an isolated copy of your real Chrome profile (cookies/session/cache) for stealth. " +
46
+ "Real logged-in credentials may be exercised on visited pages. Set browser.profileReuse to 'opt-in' to disable.";
47
+
48
+ if (!inputs.realProfileAvailable) {
49
+ return { mode: "synthetic", warning: null, reason: "synthetic-fallback" };
50
+ }
51
+ if (posture === "auto") {
52
+ return { mode: "real", warning, reason: "auto-real" };
53
+ }
54
+ // opt-in
55
+ if (inputs.explicitlyRequested) {
56
+ return { mode: "real", warning, reason: "explicit-real" };
57
+ }
58
+ return { mode: "synthetic", warning: null, reason: "synthetic-opt-in" };
59
+ }