talos-code 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +661 -0
  3. package/README.md +116 -3
  4. package/THIRD_PARTY_NOTICES.md +40 -0
  5. package/dist/archive/zip.js +73 -0
  6. package/dist/args.js +100 -0
  7. package/dist/automations/history-store.js +34 -0
  8. package/dist/automations/policy-store.js +43 -0
  9. package/dist/automations/runner.js +107 -0
  10. package/dist/automations/schedule.js +85 -0
  11. package/dist/automations/windows-task.js +53 -0
  12. package/dist/commands/advanced-cli.js +233 -0
  13. package/dist/commands/checkpoint-cli.js +45 -0
  14. package/dist/commands/config-cli.js +107 -0
  15. package/dist/commands/context.js +17 -0
  16. package/dist/commands/extensions-cli.js +207 -0
  17. package/dist/commands/project-cli.js +74 -0
  18. package/dist/commands/project-commands.js +143 -0
  19. package/dist/commands/provider-cli.js +244 -0
  20. package/dist/commands/services-cli.js +248 -0
  21. package/dist/commands/session-cli.js +171 -0
  22. package/dist/commands/system-cli.js +405 -0
  23. package/dist/config/commands.js +60 -0
  24. package/dist/config/load.js +382 -0
  25. package/dist/config/migrations.js +48 -0
  26. package/dist/config/types.js +11 -0
  27. package/dist/diagnostics/development-log.js +154 -0
  28. package/dist/diagnostics/doctor.js +104 -0
  29. package/dist/diagnostics/redact.js +80 -0
  30. package/dist/diagnostics/zip.js +52 -0
  31. package/dist/errors.js +156 -0
  32. package/dist/events/bridge.js +130 -0
  33. package/dist/extensions/installer.js +178 -0
  34. package/dist/extensions/package-schema.js +24 -0
  35. package/dist/headless/result.js +84 -0
  36. package/dist/headless/run.js +230 -0
  37. package/dist/i18n/en/approval.js +46 -0
  38. package/dist/i18n/en/common.js +9 -0
  39. package/dist/i18n/en/credentials.js +92 -0
  40. package/dist/i18n/en/errors.js +267 -0
  41. package/dist/i18n/en/firstrun.js +101 -0
  42. package/dist/i18n/en/providers.js +37 -0
  43. package/dist/i18n/en/screen.js +774 -0
  44. package/dist/i18n/en/tools.js +127 -0
  45. package/dist/i18n/en.js +14 -0
  46. package/dist/i18n/error-view.js +307 -0
  47. package/dist/i18n/index.js +19 -0
  48. package/dist/i18n/kernel-map.js +139 -0
  49. package/dist/io.js +45 -0
  50. package/dist/main.js +331 -0
  51. package/dist/paths.js +41 -0
  52. package/dist/protocol/v2/codec.js +9 -0
  53. package/dist/protocol/v2/events.js +555 -0
  54. package/dist/protocol/v2/index.js +4 -0
  55. package/dist/protocol/v2/replay.js +28 -0
  56. package/dist/protocol/v2/types.js +1 -0
  57. package/dist/provider/control-plane.js +135 -0
  58. package/dist/provider/environment-keys.js +275 -0
  59. package/dist/provider/health.js +110 -0
  60. package/dist/provider/missing-key.js +48 -0
  61. package/dist/provider/model-catalog.js +168 -0
  62. package/dist/provider/openrouter-login.js +172 -0
  63. package/dist/provider/provider-text.js +13 -0
  64. package/dist/provider/store.js +95 -0
  65. package/dist/provider/system-keyring.js +214 -0
  66. package/dist/runtime/active-run.js +63 -0
  67. package/dist/runtime/agent-tree.js +74 -0
  68. package/dist/runtime/attachments.js +84 -0
  69. package/dist/runtime/brokered-executor.js +395 -0
  70. package/dist/runtime/context-archive.js +69 -0
  71. package/dist/runtime/context-status.js +76 -0
  72. package/dist/runtime/create-runtime.js +111 -0
  73. package/dist/runtime/model-profile.js +361 -0
  74. package/dist/runtime/output-store.js +137 -0
  75. package/dist/runtime/provider-attempts.js +185 -0
  76. package/dist/runtime/replay-buffer.js +153 -0
  77. package/dist/runtime/repo.js +95 -0
  78. package/dist/runtime/session-facade.js +135 -0
  79. package/dist/runtime/session-summary.js +125 -0
  80. package/dist/runtime/supervisor.js +265 -0
  81. package/dist/runtime/talos-composition.js +1011 -0
  82. package/dist/runtime/types.js +5 -0
  83. package/dist/runtime/usage-snapshot.js +82 -0
  84. package/dist/security/auto-classifier.js +38 -0
  85. package/dist/security/credential-free-environment.js +54 -0
  86. package/dist/security/evaluate.js +243 -0
  87. package/dist/security/execution-backends.js +236 -0
  88. package/dist/security/execution-broker.js +102 -0
  89. package/dist/security/forge-scan.js +74 -0
  90. package/dist/security/from-approval.js +39 -0
  91. package/dist/security/mxc-execution-backend.js +281 -0
  92. package/dist/security/permission-engine.js +138 -0
  93. package/dist/security/permission-explanation.js +54 -0
  94. package/dist/security/persist.js +179 -0
  95. package/dist/security/plugin-guard.js +230 -0
  96. package/dist/security/process-tree-evidence-store.js +74 -0
  97. package/dist/security/process-tree-probe.js +554 -0
  98. package/dist/security/project-resource-inventory.js +186 -0
  99. package/dist/security/project-trust-gate.js +38 -0
  100. package/dist/security/project-trust.js +367 -0
  101. package/dist/security/rule-parser.js +201 -0
  102. package/dist/security/shell-segmentation.js +68 -0
  103. package/dist/security/trust-authority.js +324 -0
  104. package/dist/security/types.js +1 -0
  105. package/dist/security/workspace-identity.js +102 -0
  106. package/dist/services/automation-facade.js +59 -0
  107. package/dist/services/forge-facade.js +77 -0
  108. package/dist/services/hook-facade.js +240 -0
  109. package/dist/services/index.js +16 -0
  110. package/dist/services/library-facade.js +174 -0
  111. package/dist/services/mcp-facade.js +348 -0
  112. package/dist/services/memory-facade.js +126 -0
  113. package/dist/services/notes-facade.js +157 -0
  114. package/dist/services/plugin-facade.js +308 -0
  115. package/dist/services/research-facade.js +110 -0
  116. package/dist/services/task-board-facade.js +236 -0
  117. package/dist/services/workflow-catalog.js +246 -0
  118. package/dist/sessions/export-format.js +98 -0
  119. package/dist/sessions/metadata-store.js +160 -0
  120. package/dist/sessions/share.js +122 -0
  121. package/dist/sessions/transfer.js +16 -0
  122. package/dist/subcommands.js +62 -0
  123. package/dist/tui/agent-roster.js +36 -0
  124. package/dist/tui/agent-view.js +51 -0
  125. package/dist/tui/app.js +4098 -0
  126. package/dist/tui/approval.js +36 -0
  127. package/dist/tui/boot/boot-sequence.js +76 -0
  128. package/dist/tui/boot/cinematic.js +243 -0
  129. package/dist/tui/boot/desktop-logo.js +82 -0
  130. package/dist/tui/boot.js +3 -0
  131. package/dist/tui/busy-input.js +58 -0
  132. package/dist/tui/catalog-service.js +560 -0
  133. package/dist/tui/components/assistant-stream.js +1 -0
  134. package/dist/tui/components/command-menu.js +68 -0
  135. package/dist/tui/components/composer.js +219 -0
  136. package/dist/tui/components/diff.js +35 -0
  137. package/dist/tui/components/footer.js +48 -0
  138. package/dist/tui/components/header.js +6 -0
  139. package/dist/tui/components/markdown.js +305 -0
  140. package/dist/tui/components/status-indicator.js +38 -0
  141. package/dist/tui/components/terminal-shell.js +214 -0
  142. package/dist/tui/components/thinking-row.js +19 -0
  143. package/dist/tui/components/tool-row.js +97 -0
  144. package/dist/tui/components/transcript-virtualizer.js +165 -0
  145. package/dist/tui/components/transcript.js +43 -0
  146. package/dist/tui/descendant-approvals.js +104 -0
  147. package/dist/tui/diff-model.js +77 -0
  148. package/dist/tui/editor-history.js +21 -0
  149. package/dist/tui/editor.js +210 -0
  150. package/dist/tui/event-adapter.js +436 -0
  151. package/dist/tui/exit-output.js +58 -0
  152. package/dist/tui/external-editor.js +53 -0
  153. package/dist/tui/file-completion.js +89 -0
  154. package/dist/tui/focus-manager.js +5 -0
  155. package/dist/tui/highlight.js +30 -0
  156. package/dist/tui/input-router.js +18 -0
  157. package/dist/tui/interrupt.js +99 -0
  158. package/dist/tui/keybindings.js +194 -0
  159. package/dist/tui/keymap-resolver.js +91 -0
  160. package/dist/tui/launch-state.js +148 -0
  161. package/dist/tui/line-diff.js +57 -0
  162. package/dist/tui/live-activity.js +56 -0
  163. package/dist/tui/metrics.js +123 -0
  164. package/dist/tui/onboarding.js +36 -0
  165. package/dist/tui/overlays/agent-tree.js +43 -0
  166. package/dist/tui/overlays/approval-dialog.js +642 -0
  167. package/dist/tui/overlays/automation-center.js +51 -0
  168. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  169. package/dist/tui/overlays/context-inspector.js +36 -0
  170. package/dist/tui/overlays/effort-line.js +110 -0
  171. package/dist/tui/overlays/forge-center.js +46 -0
  172. package/dist/tui/overlays/help-dialog.js +16 -0
  173. package/dist/tui/overlays/history-picker.js +37 -0
  174. package/dist/tui/overlays/hook-center.js +70 -0
  175. package/dist/tui/overlays/library-center.js +55 -0
  176. package/dist/tui/overlays/mcp-center.js +60 -0
  177. package/dist/tui/overlays/memory-center.js +63 -0
  178. package/dist/tui/overlays/model-picker.js +77 -0
  179. package/dist/tui/overlays/notes-center.js +53 -0
  180. package/dist/tui/overlays/overlay-host.js +8 -0
  181. package/dist/tui/overlays/plugin-center.js +76 -0
  182. package/dist/tui/overlays/provider-picker.js +171 -0
  183. package/dist/tui/overlays/queue-editor.js +32 -0
  184. package/dist/tui/overlays/research-center.js +59 -0
  185. package/dist/tui/overlays/scroll-window.js +234 -0
  186. package/dist/tui/overlays/session-picker.js +115 -0
  187. package/dist/tui/overlays/tasks-center.js +83 -0
  188. package/dist/tui/overlays/theme-picker.js +21 -0
  189. package/dist/tui/overlays/transcript-search.js +58 -0
  190. package/dist/tui/overlays/trust-center.js +29 -0
  191. package/dist/tui/overlays/workflow-center.js +46 -0
  192. package/dist/tui/project-file-index.js +214 -0
  193. package/dist/tui/project-references.js +85 -0
  194. package/dist/tui/project-trust-prompt.js +289 -0
  195. package/dist/tui/prompt-history-store.js +116 -0
  196. package/dist/tui/queue-store.js +110 -0
  197. package/dist/tui/regions/budget.js +59 -0
  198. package/dist/tui/regions/views.js +96 -0
  199. package/dist/tui/render-coordinator.js +148 -0
  200. package/dist/tui/render-scheduler.js +4 -0
  201. package/dist/tui/run.js +69 -0
  202. package/dist/tui/selection-list.js +29 -0
  203. package/dist/tui/session-controller.js +791 -0
  204. package/dist/tui/session-export.js +54 -0
  205. package/dist/tui/setup-wizard.js +46 -0
  206. package/dist/tui/shell-input.js +35 -0
  207. package/dist/tui/shell-layout.js +48 -0
  208. package/dist/tui/shell-model.js +98 -0
  209. package/dist/tui/slash-commands.js +82 -0
  210. package/dist/tui/state.js +151 -0
  211. package/dist/tui/status-bar.js +125 -0
  212. package/dist/tui/status-view.js +40 -0
  213. package/dist/tui/terminal-capabilities.js +9 -0
  214. package/dist/tui/terminal-session.js +11 -0
  215. package/dist/tui/text-width.js +66 -0
  216. package/dist/tui/theme-catalog.js +27 -0
  217. package/dist/tui/theme-store.js +23 -0
  218. package/dist/tui/theme.js +23 -0
  219. package/dist/tui/tool-display.js +135 -0
  220. package/dist/tui/tool-facts.js +1 -0
  221. package/dist/tui/tools/bash-renderer.js +32 -0
  222. package/dist/tui/tools/change.js +61 -0
  223. package/dist/tui/tools/edit-renderer.js +17 -0
  224. package/dist/tui/tools/generic-renderer.js +13 -0
  225. package/dist/tui/tools/list-renderer.js +14 -0
  226. package/dist/tui/tools/read-renderer.js +13 -0
  227. package/dist/tui/tools/registry.js +20 -0
  228. package/dist/tui/tools/row-format.js +187 -0
  229. package/dist/tui/tools/search-renderer.js +38 -0
  230. package/dist/tui/tools/shared.js +97 -0
  231. package/dist/tui/tools/write-renderer.js +15 -0
  232. package/dist/tui/transcript-model.js +441 -0
  233. package/dist/tui/ui-preferences.js +79 -0
  234. package/dist/tui/usage-view.js +77 -0
  235. package/dist/tui/vim-mode.js +99 -0
  236. package/dist/update/check.js +15 -0
  237. package/dist/update/npm.js +51 -0
  238. package/dist/update/run.js +129 -0
  239. package/dist/version.js +2 -0
  240. package/dist/workspace/checkpoint-store.js +210 -0
  241. package/dist/workspace/checkpoint.js +413 -0
  242. package/dist/workspace/restore.js +232 -0
  243. package/package.json +64 -5
  244. package/vendor/context-engine/package.json +11 -0
  245. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  246. package/vendor/context-engine/src/contracts.mjs +48 -0
  247. package/vendor/context-engine/src/engine.mjs +445 -0
  248. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  249. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  250. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  251. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  252. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  253. package/vendor/context-engine/src/profiles.mjs +10 -0
  254. package/vendor/context-engine/src/retrieval.mjs +104 -0
  255. package/vendor/context-engine/src/summary.mjs +97 -0
  256. package/vendor/context-engine/src/usage.mjs +34 -0
  257. package/vendor/harness-ui/package.json +38 -0
  258. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  259. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  260. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  261. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  262. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  263. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  264. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  265. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  266. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  267. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  268. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  269. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  270. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  271. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  272. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  273. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  274. package/vendor/harness-ui/src/config.mjs +697 -0
  275. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  276. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  277. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  278. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  279. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  280. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  281. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  282. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  283. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  284. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  285. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  286. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  287. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  288. package/vendor/harness-ui/src/doctor.mjs +142 -0
  289. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  290. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  291. package/vendor/harness-ui/src/document-report.mjs +331 -0
  292. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  293. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  294. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  295. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  296. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  297. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  298. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  299. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  300. package/vendor/harness-ui/src/git-service.mjs +626 -0
  301. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  302. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  303. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  304. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  305. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  306. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  307. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  308. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  309. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  310. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  311. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  312. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  313. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  314. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  315. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  316. package/vendor/harness-ui/src/library-store.mjs +652 -0
  317. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  318. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  319. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  320. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  321. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  322. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  323. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  324. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  325. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  326. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  327. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  328. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  329. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  330. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  331. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  332. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  333. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  334. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  335. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  336. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  337. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  338. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  339. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  340. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  341. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  342. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  343. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  344. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  345. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  346. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  347. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  348. package/vendor/harness-ui/src/research/card.mjs +235 -0
  349. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  350. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  351. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  352. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  353. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  354. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  355. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  356. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  357. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  358. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  359. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  360. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  361. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  362. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  363. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  364. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  365. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  366. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  367. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  368. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  369. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  370. package/vendor/harness-ui/src/research/report.mjs +203 -0
  371. package/vendor/harness-ui/src/research/run.mjs +527 -0
  372. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  373. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  374. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  375. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  376. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  377. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  378. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  379. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  380. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  381. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  382. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  383. package/vendor/harness-ui/src/session-store.mjs +220 -0
  384. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  385. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  386. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  387. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  388. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  389. package/vendor/harness-ui/src/static-files.mjs +96 -0
  390. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  391. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  392. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  393. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  394. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  395. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  396. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  397. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  398. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  399. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  400. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  401. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  402. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  403. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  404. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  405. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  406. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  407. package/vendor/manifest.json +170 -0
@@ -0,0 +1,694 @@
1
+ /**
2
+ * browser-vivo.mjs — M1 (07/09/2026): il MOTORE del browser vivo, cioè far
3
+ * vivere un Chromium che è NOSTRO.
4
+ *
5
+ * Perché esiste. La vista Browser incornicia le pagine con un `<iframe>`, e su
6
+ * un sito che vieta la cornice resta un rettangolo grigio: misurato oggi, il
7
+ * ripiego non scattava nemmeno, perché Chrome carica DENTRO la cornice la
8
+ * propria pagina d'errore e spara un `load` regolare — la cornice non sa di
9
+ * aver fallito. Il ripiego B è quindi un browser VERO: il Chromium già
10
+ * installato sul computer (Chrome o Edge — su Windows 11 Edge c'è sempre),
11
+ * avviato dal nostro server con un PROFILO SEPARATO e pilotato via CDP.
12
+ *
13
+ * ⛔ SICUREZZA, non negoziabile. La pagina di terzi è SEMPRE contenuto non
14
+ * affidabile. Perciò: processo separato che non vede le nostre API; profilo in
15
+ * una cartella nostra, MAI quello personale della persona (i suoi cookie e le
16
+ * sue sessioni non entrano qui); porta di debug solo su loopback; e la sandbox
17
+ * di Chrome resta ACCESA — vedi `argomentiChromium()`.
18
+ *
19
+ * Fonti lette il 07/09/2026, PRIMA di scrivere:
20
+ * · chrome-launcher, `docs/chrome-flags-for-tools.md` — la lista dei flag per
21
+ * un browser pilotato e, testuale, che `--no-sandbox` «is not recommended»;
22
+ * lì stanno anche i flag RIMOSSI da Chrome (`--disable-infobars` via nel
23
+ * 2019, `--headless=new` inutile dal 132 di gennaio 2025) che qui NON si
24
+ * scrivono.
25
+ * · ChromeDevTools/devtools-protocol issue #55 (FAQ) — con
26
+ * `--remote-debugging-port=0` Chrome sceglie una porta libera e scrive
27
+ * l'indirizzo del browser SIA su stderr SIA nel file `DevToolsActivePort`
28
+ * dentro il profilo.
29
+ * · Puppeteer, `waitForWSEndpoint` — la riga si riconosce con
30
+ * `/^DevTools listening on (ws:\/\/.*?)\r?$/`, letta A RIGHE da stderr.
31
+ * · CDP, dominio Target — `flatten` «Enables "flat" access to the session via
32
+ * specifying sessionId attribute in the commands», e «We plan to make this
33
+ * the default, deprecate non-flattened mode, and eventually retire it»
34
+ * (crbug 991325).
35
+ * · chromium issue 40096993 — dal 111 il server CDP risponde **403** a una
36
+ * stretta di mano WebSocket che porta un'intestazione `Origin` non
37
+ * dichiarata in `--remote-allow-origins`. Vedi `argomentiChromium()`: qui
38
+ * quel flag NON si passa, ed è una scelta motivata.
39
+ *
40
+ * ⛔ Nessuna dipendenza nuova: `node:child_process`, `node:fs`, `node:readline`
41
+ * e il `ws` che il backend ha già. Il browser NON si avvia mai nei test —
42
+ * `avvia` ed `esiste` stanno nel contratto proprio perché si possano fingere.
43
+ */
44
+ import { spawn, execFile } from 'node:child_process';
45
+ import { mkdirSync, existsSync } from 'node:fs';
46
+ import { createInterface } from 'node:readline';
47
+
48
+ import { ambienteSenzaVariabiliDelServer } from './ambiente-solo-server.mjs';
49
+ import { urlAmmesso } from './browser-frame.mjs';
50
+
51
+ /** Tetto d'attesa della riga «DevTools listening on …»: oltre, errore parlante. */
52
+ export const ATTESA_AVVIO_MS = 20_000;
53
+ /** Tetto d'attesa di UNA risposta CDP: un comando che non torna non resta appeso per sempre. */
54
+ export const ATTESA_CDP_MS = 30_000;
55
+ /** Tetto d'attesa del caricamento di una pagina in `vaiA`. */
56
+ export const ATTESA_CARICAMENTO_MS = 20_000;
57
+ /** Quanto si concede al processo per uscire da solo prima di ucciderlo davvero. */
58
+ export const GRAZIA_CHIUSURA_MS = 3_000;
59
+
60
+ /**
61
+ * La riga che Chrome stampa su stderr quando il server CDP è in piedi.
62
+ * ⛔ Il `\r` finale è obbligatorio nel modello: su Windows la riga arriva con
63
+ * il ritorno a capo dentro, e senza questo il ws finirebbe con un carattere
64
+ * invisibile che poi rompe la connessione (è la stessa cura che sta nel
65
+ * `waitForWSEndpoint` di Puppeteer, letto il 07/09/2026).
66
+ */
67
+ const RIGA_DEVTOOLS = /^\s*DevTools listening on (ws:\/\/\S+?)\s*$/;
68
+
69
+ function errore(codice, messaggio, causa) {
70
+ const e = new Error(messaggio);
71
+ e.codice = codice;
72
+ if (causa !== undefined) e.causa = causa;
73
+ return e;
74
+ }
75
+
76
+ /** Toglie i doppioni tenendo il PRIMO: l'ordine dei candidati È la preferenza. */
77
+ function senzaDoppioni(elenco) {
78
+ const visti = new Set();
79
+ const fuori = [];
80
+ for (const voce of elenco) {
81
+ const v = String(voce || '').trim();
82
+ if (!v || visti.has(v)) continue;
83
+ visti.add(v);
84
+ fuori.push(v);
85
+ }
86
+ return fuori;
87
+ }
88
+
89
+ /**
90
+ * Il canale si legge dal PERCORSO. Edge si controlla PER PRIMO: il suo percorso
91
+ * su macOS è «Microsoft Edge.app/Contents/MacOS/Microsoft Edge» e non contiene
92
+ * «chrome», ma un domani un canale Dev potrebbe contenerlo — l'ordine dei
93
+ * controlli è la difesa.
94
+ * @param {string} percorso
95
+ * @returns {'chrome'|'edge'|'chromium'}
96
+ */
97
+ export function canaleDaPercorso(percorso) {
98
+ const p = String(percorso || '').toLowerCase();
99
+ if (p.includes('msedge') || p.includes('microsoft edge') || p.includes('microsoft-edge')) return 'edge';
100
+ if (p.includes('chromium')) return 'chromium';
101
+ if (p.includes('chrome')) return 'chrome';
102
+ return 'chromium';
103
+ }
104
+
105
+ /**
106
+ * I percorsi da provare, IN ORDINE di preferenza: prima quello dichiarato
107
+ * dall'ambiente (se l'owner ha un binario suo, comanda lui), poi Chrome, poi
108
+ * Edge, poi Chromium. ⛔ Qui non si tocca il disco: questa funzione dice solo
109
+ * DOVE guardare — chi guarda è `trovaChromium`.
110
+ * @param {string} piattaforma `process.platform`
111
+ * @param {Record<string,string|undefined>} ambiente `process.env`
112
+ * @returns {string[]}
113
+ */
114
+ export function candidatiChromium(piattaforma = process.platform, ambiente = process.env) {
115
+ const amb = ambiente || {};
116
+ const dichiarato = amb.TALOS_CHROMIUM || amb.CHROME_PATH || '';
117
+ const fuori = dichiarato ? [String(dichiarato)] : [];
118
+
119
+ if (piattaforma === 'win32') {
120
+ const radici = [amb.PROGRAMFILES, amb['PROGRAMFILES(X86)'], amb.LOCALAPPDATA].filter(Boolean);
121
+ /* ⛔ Edge è un binario a 64 bit e sta comunque, per default, sotto
122
+ «Program Files (x86)» (Microsoft Learn, 07/09/2026): la radice si prova
123
+ tutte e tre le volte invece di scommettere su una sola. */
124
+ for (const r of radici) fuori.push(`${r}\\Google\\Chrome\\Application\\chrome.exe`);
125
+ for (const r of radici) fuori.push(`${r}\\Microsoft\\Edge\\Application\\msedge.exe`);
126
+ for (const r of radici) fuori.push(`${r}\\Chromium\\Application\\chrome.exe`);
127
+ return senzaDoppioni(fuori);
128
+ }
129
+
130
+ if (piattaforma === 'darwin') {
131
+ const casa = amb.HOME || '';
132
+ fuori.push('/Applications/Google Chrome.app/Contents/MacOS/Google Chrome');
133
+ if (casa) fuori.push(`${casa}/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`);
134
+ fuori.push('/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge');
135
+ if (casa) fuori.push(`${casa}/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge`);
136
+ fuori.push('/Applications/Chromium.app/Contents/MacOS/Chromium');
137
+ return senzaDoppioni(fuori);
138
+ }
139
+
140
+ if (piattaforma === 'linux') {
141
+ fuori.push('/usr/bin/google-chrome-stable', '/usr/bin/google-chrome', '/opt/google/chrome/chrome');
142
+ fuori.push('/usr/bin/microsoft-edge-stable', '/usr/bin/microsoft-edge');
143
+ fuori.push('/usr/bin/chromium', '/usr/bin/chromium-browser', '/snap/bin/chromium');
144
+ return senzaDoppioni(fuori);
145
+ }
146
+
147
+ /* Una piattaforma che non conosciamo non riceve percorsi INVENTATI: resta il
148
+ solo binario dichiarato dall'ambiente, se c'è. Meglio zero candidati che
149
+ una lista che finge di sapere. */
150
+ return senzaDoppioni(fuori);
151
+ }
152
+
153
+ /**
154
+ * Il primo candidato che esiste DAVVERO sul disco.
155
+ * @param {{piattaforma?:string, ambiente?:Record<string,string|undefined>, esiste?:(percorso:string)=>boolean}} deps
156
+ * @returns {{percorso:string, canale:'chrome'|'edge'|'chromium'}|null}
157
+ */
158
+ export function trovaChromium({ piattaforma = process.platform, ambiente = process.env, esiste } = {}) {
159
+ /*
160
+ * ⛔⛔ 07/9 — qui il ripiego era `() => false`: senza il parametro `esiste` la funzione
161
+ * rispondeva SEMPRE «nessun browser», cioè era INERTE PER COSTRUZIONE fuori dai test. Trovato
162
+ * dal vivo al primo giro vero: Chrome era al suo posto
163
+ * (`C:\Program Files\Google\Chrome\Application\chrome.exe`) e la app diceva di non trovarlo.
164
+ * È la stessa famiglia del cancello semantico spento da sempre: un ripiego che non guarda
165
+ * niente supera ogni prova finché nessuno gli chiede la verità.
166
+ * ⇒ Il ripiego è la domanda VERA al disco; l'iniezione resta, e serve ai test.
167
+ */
168
+ const guarda = typeof esiste === 'function' ? esiste : ((dove) => existsSync(dove));
169
+ for (const percorso of candidatiChromium(piattaforma, ambiente)) {
170
+ let c = false;
171
+ try { c = Boolean(guarda(percorso)); } catch { c = false; }
172
+ if (c) return { percorso, canale: canaleDaPercorso(percorso) };
173
+ }
174
+ return null;
175
+ }
176
+
177
+ /**
178
+ * Gli argomenti di un Chromium PILOTATO nel 2026 (fonte: chrome-launcher,
179
+ * `chrome-flags-for-tools.md`, letto il 07/09/2026).
180
+ *
181
+ * ⛔ Quello che NON c'è, e perché:
182
+ * · `--no-sandbox` — la documentazione di chrome-launcher lo dichiara «not
183
+ * recommended», e qui la pagina caricata è contenuto ostile per definizione:
184
+ * la sandbox è esattamente il muro che ci serve. Non si spegne, mai.
185
+ * · `--remote-allow-origins=*` — servirebbe solo a un client che manda
186
+ * l'intestazione `Origin` nella stretta di mano (chromium 40096993, il 403
187
+ * dal Chrome 111). Il nostro client è `ws` da Node e NON manda `Origin`;
188
+ * metterlo a `*` aprirebbe l'endpoint di debug a QUALUNQUE pagina web aperta
189
+ * in quel browser — cioè al contenuto non affidabile che stiamo isolando.
190
+ * · `--disable-dev-shm-usage` — è una cura per `/dev/shm` piccola dentro
191
+ * Docker; qui il browser gira sul computer della persona.
192
+ * · `--headless=new` — ⛔ 08/09/2026, owner: «non si devono aprire schede chrome in bg». Fino a
193
+ * oggi qui c'era scritto il contrario («la finestra serve viva») ed era una premessa mai
194
+ * misurata: una finestra vera spuntava sullo schermo della persona, e restava li' mentre lei
195
+ * lavorava. MISURATO prima di cambiarla (C35 nello scratchpad): stessa pagina animata, 6 secondi,
196
+ * **599 fotogrammi** con la finestra davanti, **599** con la finestra coperta da un'altra e
197
+ * **599** in headless. Il compositore produce lo stesso, e lo screencast li prende lo stesso.
198
+ * ⇒ la finestra non serviva a vedere: serviva solo a disturbare.
199
+ *
200
+ * @param {{cartellaProfilo:string, porta?:number}} opzioni
201
+ * @returns {string[]}
202
+ */
203
+ /**
204
+ * Lo user agent di Chrome senza la parola «Headless»: in headless Chrome la aggiunge da se', e
205
+ * alcuni siti servono una pagina diversa (o un blocco) a chi la porta. Non si finge un altro
206
+ * browser — versione, sistema e motore restano quelli veri: si toglie solo l'etichetta.
207
+ */
208
+ export function userAgentSenzaHeadless(agente = AGENTE_PREDEFINITO) {
209
+ return String(agente).replace(/HeadlessChrome/gi, 'Chrome');
210
+ }
211
+
212
+ export const AGENTE_PREDEFINITO = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36';
213
+
214
+ export function argomentiChromium({ cartellaProfilo, porta = 0 } = {}) {
215
+ if (!cartellaProfilo) throw errore('BROWSER_VIVO_PROFILO_MANCANTE', 'Serve la cartella del profilo: il browser pilotato non usa mai quello personale');
216
+ return [
217
+ /* ⛔ Porta 0: la sceglie Chrome e la leggiamo dalla riga su stderr. Una
218
+ porta fissa è un conflitto che prima o poi capita — due sessioni, o un
219
+ altro strumento che l'ha già presa. */
220
+ `--remote-debugging-port=${porta}`,
221
+ /* Il profilo è NOSTRO. È questo flag, non una promessa, a tenere fuori i
222
+ cookie e le sessioni della persona. */
223
+ `--user-data-dir=${cartellaProfilo}`,
224
+ /* Niente primo avvio, niente «vuoi Chrome come predefinito», niente app di
225
+ default installate al volo. */
226
+ '--no-first-run',
227
+ '--no-default-browser-check',
228
+ '--disable-default-apps',
229
+ /* Niente finestre di aggiornamento né traffico di componenti. */
230
+ '--disable-component-update',
231
+ '--disable-sync',
232
+ /* Niente ripristino di sessione: un profilo pilotato che si riapre con le
233
+ schede di ieri è uno stato che nessuno ha chiesto. */
234
+ '--disable-session-crashed-bubble',
235
+ '--hide-crash-restore-bubble',
236
+ /* ⛔ Senza questi tre, una finestra coperta o in secondo piano viene
237
+ strozzata dal browser e ogni misura di fluidità diventa falsa (lezione
238
+ già pagata: `requestAnimationFrame` a ~1/s su una finestra occlusa). */
239
+ '--disable-background-timer-throttling',
240
+ '--disable-backgrounding-occluded-windows',
241
+ '--disable-renderer-backgrounding',
242
+ /* Niente estensioni (il profilo è nuovo, ma un profilo aziendale con
243
+ policy potrebbe iniettarle) e niente richieste di permesso che bloccano
244
+ l'agente aspettando un clic umano. */
245
+ '--disable-extensions',
246
+ '--deny-permission-prompts',
247
+ '--disable-notifications',
248
+ '--disable-features=Translate,MediaRouter,OptimizationHints',
249
+ /* ⛔ Nessuna finestra sullo schermo di chi lavora. `--headless=new` e' lo stesso browser
250
+ completo (stesso rendering, stesse estensioni possibili, stesso compositore): dal 132 il
251
+ vecchio headless non esiste piu' e `--headless` da solo significa gia' questo, ma il nome
252
+ esplicito dice a chi legge QUALE dei due si intende. */
253
+ '--headless=new',
254
+ /* ⛔ In headless lo user agent contiene «HeadlessChrome» e alcuni siti lo rifiutano o servono
255
+ una pagina diversa: la lettura sarebbe di un'altra pagina rispetto a quella che la persona
256
+ vede nel suo browser. Si toglie la parola, non si finge un altro browser. */
257
+ `--user-agent=${userAgentSenzaHeadless()}`,
258
+ /* Si parte da una pagina vuota: la «nuova scheda» di Chrome fa rete e
259
+ mostra contenuti che non c'entrano niente con noi. */
260
+ 'about:blank',
261
+ ];
262
+ }
263
+
264
+ /**
265
+ * Avvia il browser e risolve quando il server CDP RISPONDE DAVVERO — cioè
266
+ * quando la riga «DevTools listening on ws://…» è arrivata su stderr. Prima di
267
+ * quella riga il processo esiste ma non è pilotabile: dire «avviato» lì
268
+ * sarebbe dire una cosa che non è vera.
269
+ *
270
+ * @param {{percorso:string, cartellaProfilo:string, avvia?:Function, attesaMs?:number, creaCartella?:Function, terminaAlbero?:Function}} opzioni
271
+ * @returns {Promise<{pid:number|null, wsUrl:string, percorso:string, chiudi:()=>Promise<{chiuso:boolean, modo:string}>, processo:object}>}
272
+ */
273
+ export async function avviaBrowserVivo({
274
+ percorso,
275
+ cartellaProfilo,
276
+ avvia,
277
+ attesaMs = ATTESA_AVVIO_MS,
278
+ creaCartella = (dove) => mkdirSync(dove, { recursive: true }),
279
+ terminaAlbero = terminaAlberoDiSistema,
280
+ graziaMs = GRAZIA_CHIUSURA_MS,
281
+ } = {}) {
282
+ if (!percorso) throw errore('BROWSER_VIVO_SENZA_BINARIO', 'Nessun Chromium da avviare: manca il percorso del binario');
283
+ if (!cartellaProfilo) throw errore('BROWSER_VIVO_PROFILO_MANCANTE', 'Serve la cartella del profilo: il browser pilotato non usa mai quello personale');
284
+
285
+ /* La cartella del profilo si crea se manca: Chrome la creerebbe da sé, ma
286
+ allora un errore di permessi arriverebbe come «il browser non è partito»
287
+ invece che come quello che è. */
288
+ try { creaCartella(cartellaProfilo); } catch (causa) {
289
+ throw errore('BROWSER_VIVO_PROFILO_NON_CREABILE', `Non riesco a creare la cartella del profilo (${cartellaProfilo})`, causa);
290
+ }
291
+
292
+ const argomenti = argomentiChromium({ cartellaProfilo });
293
+ const lancia = typeof avvia === 'function' ? avvia : avviaDiSistema;
294
+ /* ⛔ D1: l'ambiente del browser NON è quello del server — vedi `opzioniAvvioBrowser`. */
295
+ const opzioni = opzioniAvvioBrowser();
296
+ let processo;
297
+ try { processo = lancia(percorso, argomenti, opzioni); } catch (causa) {
298
+ throw errore('BROWSER_VIVO_NON_PARTE', `Non riesco ad avviare il browser (${percorso})`, causa);
299
+ }
300
+ if (!processo || !processo.stderr) {
301
+ throw errore('BROWSER_VIVO_SENZA_STDERR', 'Il browser è partito senza stderr: la porta di debug si legge solo da lì');
302
+ }
303
+
304
+ const chiudi = creaChiusura(processo, terminaAlbero, graziaMs);
305
+ let wsUrl;
306
+ try {
307
+ wsUrl = await attendiWsUrl(processo, attesaMs);
308
+ } catch (causa) {
309
+ /* ⛔ Se non è arrivato l'indirizzo, il processo NON si lascia in giro: un
310
+ Chromium orfano con un profilo nostro è un consumo che nessuno vedrà. */
311
+ await chiudi().catch(() => {});
312
+ throw causa;
313
+ }
314
+ return { pid: processo.pid ?? null, wsUrl, percorso, processo, chiudi };
315
+ }
316
+
317
+ /**
318
+ * ⛔⛔⛔ D1 del secondo giro CLI-REQ (17/09/2026), autorizzato dall'owner: «la stessa cura del
319
+ * terminale». Fino a oggi questo `spawn` non passava `env`, e il valore predefinito di
320
+ * `node:child_process` è `process.env` INTERO (nodejs.org/api/child_process.html, letto il
321
+ * 17/09/2026) ⇒ il Chromium che TALOS avvia PER LA PERSONA nasceva col token di loopback che
322
+ * protegge tutta la nostra API locale, con la chiave privata che firma le ricevute e con la
323
+ * chiave della ricerca web. Un'estensione, o un processo figlio del browser, li leggeva.
324
+ *
325
+ * ⛔ Stesso elenco del terminale, IMPORTATO e non ricopiato: due copie divergerebbero al primo
326
+ * segreto nuovo, e nessuno se ne accorgerebbe (il risultato sbagliato ha lo stesso aspetto di
327
+ * quello giusto). La fonte unica è `ambiente-solo-server.mjs`, che sta in un file suo proprio
328
+ * perché `pty-terminal.mjs` importa `node-pty` — un modulo nativo che qui non serve.
329
+ *
330
+ * ⛔ Le opzioni vivono in una funzione con un nome perché la prova possa LEGGERLE: l'iniezione
331
+ * `avvia` riceve anche loro, quindi «quale ambiente riceve il browser» è una cosa misurabile e
332
+ * non una cosa che si legge nel sorgente e si spera.
333
+ */
334
+ export function opzioniAvvioBrowser(ambienteFn = ambienteSenzaVariabiliDelServer) {
335
+ /* `stdio` con stderr a tubo: è la nostra unica fonte per la porta. */
336
+ return { stdio: ['ignore', 'pipe', 'pipe'], windowsHide: false, env: ambienteFn() };
337
+ }
338
+
339
+ /** Il lancio vero. */
340
+ function avviaDiSistema(percorso, argomenti, opzioni) {
341
+ return spawn(percorso, argomenti, opzioni);
342
+ }
343
+
344
+ /**
345
+ * Legge stderr A RIGHE finché non trova l'indirizzo. ⛔ A pezzi (`on('data')`)
346
+ * non funzionerebbe: la riga può arrivare spezzata in due chunk.
347
+ */
348
+ function attendiWsUrl(processo, attesaMs) {
349
+ return new Promise((risolvi, rifiuta) => {
350
+ const lettore = createInterface({ input: processo.stderr });
351
+ const coda = [];
352
+ let finito = false;
353
+ const chiudiLettore = () => { try { lettore.close(); } catch { /* già chiuso */ } };
354
+ const finisci = (fn, valore) => {
355
+ if (finito) return;
356
+ finito = true;
357
+ clearTimeout(scadenza);
358
+ chiudiLettore();
359
+ processo.off?.('exit', suUscita);
360
+ processo.off?.('error', suErrore);
361
+ fn(valore);
362
+ };
363
+ const scadenza = setTimeout(() => {
364
+ finisci(rifiuta, errore(
365
+ 'BROWSER_VIVO_ATTESA_SCADUTA',
366
+ `Il browser non ha annunciato la porta di debug entro ${Math.round(attesaMs / 1000)} secondi. Ultime righe: ${coda.join(' | ') || '(nessuna)'}`,
367
+ ));
368
+ }, attesaMs);
369
+ scadenza.unref?.();
370
+
371
+ const suUscita = (codice) => finisci(rifiuta, errore(
372
+ 'BROWSER_VIVO_USCITO_SUBITO',
373
+ `Il browser è uscito (codice ${codice}) prima di annunciare la porta di debug. Ultime righe: ${coda.join(' | ') || '(nessuna)'}`,
374
+ ));
375
+ const suErrore = (causa) => finisci(rifiuta, errore('BROWSER_VIVO_NON_PARTE', 'Il browser non è partito', causa));
376
+ processo.on?.('exit', suUscita);
377
+ processo.on?.('error', suErrore);
378
+
379
+ lettore.on('line', (riga) => {
380
+ /* Le ultime righe si tengono: se scade l'attesa, l'errore deve DIRE cosa
381
+ il browser stava dicendo, non solo che il tempo è finito. */
382
+ coda.push(String(riga).trim());
383
+ if (coda.length > 5) coda.shift();
384
+ const trovato = RIGA_DEVTOOLS.exec(String(riga));
385
+ if (trovato) finisci(risolvi, trovato[1]);
386
+ });
387
+ lettore.on('close', () => {
388
+ if (!finito) finisci(rifiuta, errore('BROWSER_VIVO_STDERR_CHIUSO', 'Lo stderr del browser si è chiuso senza annunciare la porta di debug'));
389
+ });
390
+ });
391
+ }
392
+
393
+ /**
394
+ * Chiude DAVVERO. Non si chiede al browser via CDP di chiudersi: se il
395
+ * problema è proprio che CDP non risponde, quella strada è chiusa per
396
+ * definizione. Si va al processo.
397
+ */
398
+ function creaChiusura(processo, terminaAlbero, graziaMs = GRAZIA_CHIUSURA_MS) {
399
+ let gia = false;
400
+ return async function chiudi() {
401
+ if (gia) return { chiuso: true, modo: 'già chiuso' };
402
+ gia = true;
403
+ if (processo.exitCode !== null && processo.exitCode !== undefined) return { chiuso: true, modo: 'era già uscito' };
404
+
405
+ const uscita = new Promise((r) => {
406
+ if (typeof processo.once === 'function') processo.once('exit', () => r(true));
407
+ else r(false);
408
+ });
409
+
410
+ let modo = 'segnale';
411
+ try {
412
+ const fatto = await Promise.resolve(terminaAlbero(processo));
413
+ if (fatto) modo = 'albero';
414
+ } catch { /* si prosegue col segnale: un fallimento qui non è una scusa */ }
415
+
416
+ const scaduto = new Promise((r) => { const t = setTimeout(() => r(false), graziaMs); t.unref?.(); });
417
+ const uscitoInTempo = await Promise.race([uscita, scaduto]);
418
+ if (uscitoInTempo) return { chiuso: true, modo };
419
+
420
+ /* ⛔ Ultima parola: SIGKILL. Un `taskkill` che fallisce o un processo che
421
+ ignora il segnale non devono lasciare un browser vivo. */
422
+ try { processo.kill?.('SIGKILL'); } catch { /* già morto */ }
423
+ return { chiuso: true, modo: 'forzata' };
424
+ };
425
+ }
426
+
427
+ /**
428
+ * Su Windows i figli di Chrome (renderer, GPU, utility) NON muoiono col padre:
429
+ * si uccide l'albero con `taskkill /T /F`. Altrove basta il segnale al gruppo.
430
+ */
431
+ function terminaAlberoDiSistema(processo) {
432
+ const pid = processo?.pid;
433
+ if (!pid) return false;
434
+ if (process.platform === 'win32') {
435
+ return new Promise((r) => {
436
+ execFile('taskkill', ['/PID', String(pid), '/T', '/F'], () => r(true));
437
+ });
438
+ }
439
+ try { processo.kill('SIGTERM'); } catch { /* già morto */ }
440
+ return true;
441
+ }
442
+
443
+ /**
444
+ * Il client CDP: un contatore di id, una mappa delle richieste in volo, e gli
445
+ * eventi smistati per nome.
446
+ *
447
+ * `socket` è un WebSocket alla maniera di `ws` (già dipendenza del backend):
448
+ * `send`, `close`, e `on('message'|'close'|'error')`. Nei test è un finto con
449
+ * la stessa forma — per questo il socket lo passa il chiamante e non lo apre
450
+ * questo modulo.
451
+ *
452
+ * @param {{send:Function, close?:Function, on?:Function, addEventListener?:Function}} socket
453
+ * @param {{attesaMs?:number}} opzioni
454
+ */
455
+ export function creaClientCdp(socket, { attesaMs = ATTESA_CDP_MS } = {}) {
456
+ if (!socket || typeof socket.send !== 'function') throw errore('CDP_SOCKET_INVALIDO', 'Serve un WebSocket con «send» per parlare col browser');
457
+
458
+ let prossimoId = 1;
459
+ let chiuso = false;
460
+ const inVolo = new Map();
461
+ const ascoltatori = new Map();
462
+
463
+ const ascolta = (evento, cb) => {
464
+ if (typeof socket.on === 'function') socket.on(evento, cb);
465
+ else if (typeof socket.addEventListener === 'function') socket.addEventListener(evento, (e) => cb(evento === 'message' ? e.data : e));
466
+ };
467
+
468
+ function smista(messaggio) {
469
+ let m;
470
+ try { m = JSON.parse(typeof messaggio === 'string' ? messaggio : String(messaggio)); } catch { return; }
471
+ if (m && m.id !== undefined && inVolo.has(m.id)) {
472
+ const attesa = inVolo.get(m.id);
473
+ inVolo.delete(m.id);
474
+ clearTimeout(attesa.scadenza);
475
+ if (m.error) {
476
+ /* ⛔ L'errore del browser si riporta INTERO: il codice e il messaggio
477
+ veri. Un «comando fallito» generico costringe chi legge a indovinare. */
478
+ attesa.rifiuta(errore('CDP_ERRORE', `${attesa.metodo}: ${m.error.message || 'errore senza messaggio'} (codice ${m.error.code ?? 'ignoto'})`, m.error));
479
+ } else {
480
+ attesa.risolvi(m.result ?? {});
481
+ }
482
+ return;
483
+ }
484
+ if (m && m.method) {
485
+ const contesto = { sessionId: m.sessionId ?? null, metodo: m.method };
486
+ for (const cb of ascoltatori.get(m.method) || []) { try { cb(m.params ?? {}, contesto); } catch { /* un ascoltatore rotto non ferma gli altri */ } }
487
+ for (const cb of ascoltatori.get('*') || []) { try { cb(m.params ?? {}, contesto); } catch { /* idem */ } }
488
+ }
489
+ }
490
+
491
+ function abbandonaTutti(motivo) {
492
+ for (const [, attesa] of inVolo) {
493
+ clearTimeout(attesa.scadenza);
494
+ attesa.rifiuta(errore('CDP_SOCKET_CHIUSO', `${attesa.metodo}: ${motivo}`));
495
+ }
496
+ inVolo.clear();
497
+ }
498
+
499
+ ascolta('message', smista);
500
+ ascolta('close', () => { chiuso = true; abbandonaTutti('la connessione col browser si è chiusa'); });
501
+ ascolta('error', () => { chiuso = true; abbandonaTutti('la connessione col browser è andata in errore'); });
502
+
503
+ /**
504
+ * @param {string} metodo es. `Page.navigate`
505
+ * @param {object} parametri
506
+ * @param {string|null} sessionId ⛔ in modo «flat» il sessionId viaggia in
507
+ * OGNI messaggio destinato a una scheda: senza, il comando finisce al
508
+ * browser invece che alla pagina.
509
+ */
510
+ function invia(metodo, parametri = {}, sessionId = null) {
511
+ if (chiuso) return Promise.reject(errore('CDP_SOCKET_CHIUSO', `${metodo}: la connessione col browser è già chiusa`));
512
+ const id = prossimoId++;
513
+ const messaggio = { id, method: metodo, params: parametri || {} };
514
+ if (sessionId) messaggio.sessionId = sessionId;
515
+ return new Promise((risolvi, rifiuta) => {
516
+ const scadenza = setTimeout(() => {
517
+ inVolo.delete(id);
518
+ rifiuta(errore('CDP_ATTESA_SCADUTA', `${metodo}: nessuna risposta dal browser entro ${Math.round(attesaMs / 1000)} secondi`));
519
+ }, attesaMs);
520
+ scadenza.unref?.();
521
+ inVolo.set(id, { risolvi, rifiuta, scadenza, metodo });
522
+ try {
523
+ socket.send(JSON.stringify(messaggio));
524
+ } catch (causa) {
525
+ inVolo.delete(id);
526
+ clearTimeout(scadenza);
527
+ rifiuta(errore('CDP_INVIO_FALLITO', `${metodo}: non riesco a scrivere sulla connessione col browser`, causa));
528
+ }
529
+ });
530
+ }
531
+
532
+ /** Ritorna la funzione per STACCARSI: un ascoltatore che nessuno può togliere è una perdita. */
533
+ function su(evento, cb) {
534
+ if (!ascoltatori.has(evento)) ascoltatori.set(evento, new Set());
535
+ ascoltatori.get(evento).add(cb);
536
+ return () => { ascoltatori.get(evento)?.delete(cb); };
537
+ }
538
+
539
+ function chiudiClient() {
540
+ chiuso = true;
541
+ abbandonaTutti('il client è stato chiuso');
542
+ ascoltatori.clear();
543
+ try { socket.close?.(); } catch { /* un socket già morto non si richiude */ }
544
+ }
545
+
546
+ return { invia, su, chiudi: chiudiClient };
547
+ }
548
+
549
+ /**
550
+ * Apre una scheda vuota e ci si aggancia.
551
+ *
552
+ * ⛔ `flatten: true` è obbligatorio, non un'opzione. La documentazione del
553
+ * dominio Target (letta il 07/09/2026) dice testualmente: «Enables "flat"
554
+ * access to the session via specifying sessionId attribute in the commands.
555
+ * We plan to make this the default, deprecate non-flattened mode, and
556
+ * eventually retire it» (crbug 991325). Nel modo vecchio i messaggi della
557
+ * scheda viaggiavano incapsulati dentro `Target.sendMessageToTarget` e gli
558
+ * eventi dentro `Target.receivedMessageFromTarget`: una busta dentro la busta.
559
+ * Nel modo flat il `sessionId` sta in cima a ogni messaggio — ed è così che il
560
+ * resto di questo modulo (e i quattro moduli che ci si appoggiano) parla con
561
+ * la pagina.
562
+ *
563
+ * @param {{invia:Function}} cdp
564
+ * @returns {Promise<{targetId:string, sessionId:string}>}
565
+ */
566
+ export async function apriSchedaVuota(cdp, { url = 'about:blank', browserContextId = null } = {}) {
567
+ /*
568
+ * ⛔ 07/9 — `browserContextId` non è un dettaglio: le schede aperte nel contesto PREDEFINITO si
569
+ * scambiano cookie, localStorage e sessionStorage, quindi due sessioni di TALOS che aprono due
570
+ * pagine si vedrebbero i dati a vicenda. Il contesto è un confine di STORAGE (non di finestra:
571
+ * schede di contesti diversi convivono nella stessa finestra). Chi apre la scheda decide se
572
+ * isolarla; qui si passa solo ciò che il chiamante ha chiesto.
573
+ * Ricerca 07/09/2026: vercel-labs/agent-browser #1068, guida CDP di browser-use.
574
+ */
575
+ const creata = await cdp.invia('Target.createTarget', browserContextId ? { url, browserContextId } : { url });
576
+ const targetId = creata?.targetId;
577
+ if (!targetId) throw errore('BROWSER_VIVO_SCHEDA_SENZA_ID', 'Il browser ha aperto una scheda senza dirci il suo identificativo');
578
+ const agganciata = await cdp.invia('Target.attachToTarget', { targetId, flatten: true });
579
+ const sessionId = agganciata?.sessionId;
580
+ if (!sessionId) throw errore('BROWSER_VIVO_SESSIONE_MANCANTE', 'Il browser non ha assegnato una sessione alla scheda: senza sessionId non si può pilotare');
581
+ return { targetId, sessionId };
582
+ }
583
+
584
+ /** Traduce i `net::ERR_…` di Chrome in una frase che una persona può leggere. */
585
+ function motivoDiRete(testo) {
586
+ const t = String(testo || '');
587
+ if (/ERR_NAME_NOT_RESOLVED/.test(t)) return 'Il nome del sito non esiste';
588
+ if (/ERR_CONNECTION_REFUSED/.test(t)) return 'Nessuno risponde a quell\'indirizzo';
589
+ if (/ERR_CONNECTION_TIMED_OUT|ERR_TIMED_OUT/.test(t)) return 'Il sito non ha risposto in tempo';
590
+ if (/ERR_CERT|ERR_SSL/.test(t)) return 'Il certificato del sito non è valido';
591
+ if (/ERR_ABORTED/.test(t)) return 'Il caricamento è stato interrotto';
592
+ if (/ERR_BLOCKED_BY/.test(t)) return 'Il browser ha bloccato la pagina';
593
+ return t || 'La pagina non si è caricata';
594
+ }
595
+
596
+ /**
597
+ * Porta la scheda a un indirizzo e dice com'è andata DAVVERO.
598
+ *
599
+ * ⛔ `Page.navigate` non riporta lo stato HTTP: da solo direbbe «ok» anche su
600
+ * un 404 o un 500 (la pagina d'errore del sito È una pagina che si carica —
601
+ * è lo stesso inganno per cui la cornice `<iframe>` non si accorgeva del
602
+ * fallimento). Perciò si accende anche `Network`, si prende lo stato dalla
603
+ * risposta del documento principale e si aspetta che il frame smetta di
604
+ * caricare.
605
+ *
606
+ * Il filtro sugli indirizzi è quello che il repo ha già, `urlAmmesso` di
607
+ * `browser-frame.mjs`: solo `http:`/`https:` (fuori `file:`, `chrome:`,
608
+ * `devtools:`, `view-source:` — con cui una pagina ostile leggerebbe il disco
609
+ * o si prenderebbe il browser) e mai credenziali nell'indirizzo. ⛔ Loopback e
610
+ * indirizzi privati restano AMMESSI di proposito: `localhost:5173` è il motivo
611
+ * per cui questo browser esiste — sta dove sta l'agente, ed è il vantaggio che
612
+ * un gateway in cloud non può avere.
613
+ *
614
+ * @param {{invia:Function, su:Function}} cdp
615
+ * @param {string} sessionId
616
+ * @param {string} url
617
+ * @returns {Promise<{ok:boolean, stato:number|null, errore:string|null, url:string}>}
618
+ */
619
+ export async function vaiA(cdp, sessionId, url, { attesaMs = ATTESA_CARICAMENTO_MS } = {}) {
620
+ let indirizzo;
621
+ try { indirizzo = new URL(String(url)); } catch {
622
+ return { ok: false, stato: null, errore: 'URL non valido', url: String(url) };
623
+ }
624
+ const ammesso = urlAmmesso(indirizzo);
625
+ if (!ammesso.ok) return { ok: false, stato: null, errore: ammesso.motivo, url: indirizzo.href };
626
+
627
+ await cdp.invia('Page.enable', {}, sessionId);
628
+ await cdp.invia('Network.enable', {}, sessionId);
629
+
630
+ /* Gli stati dei documenti si raccolgono PRIMA di navigare: il frameId lo
631
+ conosciamo solo dopo, e la risposta può arrivare prima che `Page.navigate`
632
+ abbia risolto. */
633
+ const statiPerFrame = new Map();
634
+ const staccaRisposta = cdp.su('Network.responseReceived', (p) => {
635
+ if (p?.type === 'Document' && p?.frameId) statiPerFrame.set(p.frameId, p?.response?.status ?? null);
636
+ });
637
+
638
+ let fine;
639
+ const caricata = new Promise((r) => { fine = r; });
640
+ let frameAtteso = null;
641
+ /* ⛔ La fine del caricamento può arrivare PRIMA che `Page.navigate` ci abbia
642
+ detto quale riquadro guardare: senza memoria, quel caso resterebbe in
643
+ attesa fino allo scadere del tetto e verrebbe raccontato come «lenta». */
644
+ const fermiVisti = new Set();
645
+ let loadVisto = false;
646
+ const controlla = (p) => {
647
+ if (p?.frameId) fermiVisti.add(p.frameId);
648
+ if (frameAtteso && p?.frameId === frameAtteso) fine('caricata');
649
+ };
650
+ const staccaFermo = cdp.su('Page.frameStoppedLoading', controlla);
651
+ const staccaLoad = cdp.su('Page.loadEventFired', () => { loadVisto = true; if (frameAtteso) fine('caricata'); });
652
+
653
+ const smonta = () => { staccaRisposta(); staccaFermo(); staccaLoad(); };
654
+
655
+ try {
656
+ const esito = await cdp.invia('Page.navigate', { url: indirizzo.href }, sessionId);
657
+ if (esito?.errorText) {
658
+ return { ok: false, stato: null, errore: motivoDiRete(esito.errorText), url: indirizzo.href };
659
+ }
660
+ frameAtteso = esito?.frameId || null;
661
+ if (!frameAtteso) return { ok: false, stato: null, errore: 'Il browser non ha detto quale riquadro sta caricando', url: indirizzo.href };
662
+ if (fermiVisti.has(frameAtteso) || loadVisto) fine('caricata');
663
+ /* Se la risposta era già arrivata mentre navigavamo, non si aspetta a vuoto. */
664
+ const scaduta = new Promise((r) => { const t = setTimeout(() => r('scaduta'), attesaMs); t.unref?.(); });
665
+ const come = await Promise.race([caricata, scaduta]);
666
+ const stato = statiPerFrame.has(frameAtteso) ? statiPerFrame.get(frameAtteso) : null;
667
+ if (come === 'scaduta') {
668
+ return { ok: false, stato, errore: `La pagina non ha finito di caricare entro ${Math.round(attesaMs / 1000)} secondi`, url: indirizzo.href };
669
+ }
670
+ /* ⛔ Uno stato HTTP di errore È un fallimento, anche se il documento si è
671
+ caricato: è esattamente il caso che la cornice non sapeva vedere. */
672
+ if (typeof stato === 'number' && stato >= 400) {
673
+ return { ok: false, stato, errore: `Il sito ha risposto ${stato}`, url: indirizzo.href };
674
+ }
675
+ return { ok: true, stato, errore: null, url: indirizzo.href };
676
+ } catch (causa) {
677
+ return { ok: false, stato: null, errore: causa?.message || 'Il browser non ha eseguito la navigazione', url: indirizzo.href };
678
+ } finally {
679
+ smonta();
680
+ }
681
+ }
682
+
683
+ /**
684
+ * La riga che Chrome stampa su stderr → l'indirizzo del server CDP, o `null`.
685
+ * ⛔ Solo `ws://`: una riga che parla d'altro non è un indirizzo, e restituire
686
+ * qualcosa «che ci somiglia» qui vorrebbe dire fallire più avanti, dove è più
687
+ * difficile capire perché.
688
+ * @param {string} riga
689
+ * @returns {string|null}
690
+ */
691
+ export function wsUrlDaRiga(riga) {
692
+ const trovato = RIGA_DEVTOOLS.exec(String(riga ?? ''));
693
+ return trovato ? trovato[1] : null;
694
+ }