talos-code 0.0.1 → 0.1.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 (401) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +111 -3
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/dist/archive/zip.js +73 -0
  5. package/dist/args.js +97 -0
  6. package/dist/automations/history-store.js +34 -0
  7. package/dist/automations/policy-store.js +43 -0
  8. package/dist/automations/runner.js +107 -0
  9. package/dist/automations/schedule.js +85 -0
  10. package/dist/automations/windows-task.js +53 -0
  11. package/dist/commands/advanced-cli.js +233 -0
  12. package/dist/commands/checkpoint-cli.js +45 -0
  13. package/dist/commands/config-cli.js +107 -0
  14. package/dist/commands/context.js +17 -0
  15. package/dist/commands/extensions-cli.js +207 -0
  16. package/dist/commands/project-cli.js +74 -0
  17. package/dist/commands/project-commands.js +143 -0
  18. package/dist/commands/provider-cli.js +185 -0
  19. package/dist/commands/services-cli.js +248 -0
  20. package/dist/commands/session-cli.js +171 -0
  21. package/dist/commands/system-cli.js +405 -0
  22. package/dist/config/commands.js +60 -0
  23. package/dist/config/load.js +382 -0
  24. package/dist/config/migrations.js +48 -0
  25. package/dist/config/types.js +11 -0
  26. package/dist/diagnostics/development-log.js +154 -0
  27. package/dist/diagnostics/doctor.js +104 -0
  28. package/dist/diagnostics/redact.js +80 -0
  29. package/dist/diagnostics/zip.js +52 -0
  30. package/dist/errors.js +156 -0
  31. package/dist/events/bridge.js +130 -0
  32. package/dist/extensions/installer.js +178 -0
  33. package/dist/extensions/package-schema.js +24 -0
  34. package/dist/headless/result.js +84 -0
  35. package/dist/headless/run.js +230 -0
  36. package/dist/i18n/en/approval.js +43 -0
  37. package/dist/i18n/en/common.js +9 -0
  38. package/dist/i18n/en/credentials.js +82 -0
  39. package/dist/i18n/en/errors.js +246 -0
  40. package/dist/i18n/en/firstrun.js +89 -0
  41. package/dist/i18n/en/providers.js +37 -0
  42. package/dist/i18n/en/screen.js +736 -0
  43. package/dist/i18n/en/tools.js +127 -0
  44. package/dist/i18n/en.js +14 -0
  45. package/dist/i18n/error-view.js +307 -0
  46. package/dist/i18n/index.js +19 -0
  47. package/dist/i18n/kernel-map.js +139 -0
  48. package/dist/io.js +45 -0
  49. package/dist/main.js +330 -0
  50. package/dist/paths.js +41 -0
  51. package/dist/protocol/v2/codec.js +9 -0
  52. package/dist/protocol/v2/events.js +555 -0
  53. package/dist/protocol/v2/index.js +4 -0
  54. package/dist/protocol/v2/replay.js +28 -0
  55. package/dist/protocol/v2/types.js +1 -0
  56. package/dist/provider/control-plane.js +135 -0
  57. package/dist/provider/environment-keys.js +275 -0
  58. package/dist/provider/health.js +110 -0
  59. package/dist/provider/missing-key.js +48 -0
  60. package/dist/provider/model-catalog.js +168 -0
  61. package/dist/provider/provider-text.js +13 -0
  62. package/dist/provider/store.js +95 -0
  63. package/dist/provider/system-keyring.js +214 -0
  64. package/dist/runtime/active-run.js +63 -0
  65. package/dist/runtime/agent-tree.js +74 -0
  66. package/dist/runtime/attachments.js +84 -0
  67. package/dist/runtime/brokered-executor.js +395 -0
  68. package/dist/runtime/context-status.js +76 -0
  69. package/dist/runtime/create-runtime.js +111 -0
  70. package/dist/runtime/model-profile.js +361 -0
  71. package/dist/runtime/output-store.js +137 -0
  72. package/dist/runtime/provider-attempts.js +185 -0
  73. package/dist/runtime/replay-buffer.js +153 -0
  74. package/dist/runtime/repo.js +95 -0
  75. package/dist/runtime/session-facade.js +135 -0
  76. package/dist/runtime/session-summary.js +125 -0
  77. package/dist/runtime/supervisor.js +262 -0
  78. package/dist/runtime/talos-composition.js +1006 -0
  79. package/dist/runtime/types.js +5 -0
  80. package/dist/runtime/usage-snapshot.js +82 -0
  81. package/dist/security/auto-classifier.js +38 -0
  82. package/dist/security/credential-free-environment.js +54 -0
  83. package/dist/security/evaluate.js +243 -0
  84. package/dist/security/execution-backends.js +236 -0
  85. package/dist/security/execution-broker.js +102 -0
  86. package/dist/security/forge-scan.js +74 -0
  87. package/dist/security/from-approval.js +39 -0
  88. package/dist/security/mxc-execution-backend.js +281 -0
  89. package/dist/security/permission-engine.js +138 -0
  90. package/dist/security/permission-explanation.js +54 -0
  91. package/dist/security/persist.js +179 -0
  92. package/dist/security/plugin-guard.js +230 -0
  93. package/dist/security/process-tree-evidence-store.js +74 -0
  94. package/dist/security/process-tree-probe.js +554 -0
  95. package/dist/security/project-resource-inventory.js +186 -0
  96. package/dist/security/project-trust-gate.js +38 -0
  97. package/dist/security/project-trust.js +367 -0
  98. package/dist/security/rule-parser.js +201 -0
  99. package/dist/security/shell-segmentation.js +68 -0
  100. package/dist/security/trust-authority.js +324 -0
  101. package/dist/security/types.js +1 -0
  102. package/dist/security/workspace-identity.js +102 -0
  103. package/dist/services/automation-facade.js +59 -0
  104. package/dist/services/forge-facade.js +77 -0
  105. package/dist/services/hook-facade.js +240 -0
  106. package/dist/services/index.js +16 -0
  107. package/dist/services/library-facade.js +174 -0
  108. package/dist/services/mcp-facade.js +348 -0
  109. package/dist/services/memory-facade.js +126 -0
  110. package/dist/services/notes-facade.js +157 -0
  111. package/dist/services/plugin-facade.js +308 -0
  112. package/dist/services/research-facade.js +110 -0
  113. package/dist/services/task-board-facade.js +236 -0
  114. package/dist/services/workflow-catalog.js +246 -0
  115. package/dist/sessions/export-format.js +98 -0
  116. package/dist/sessions/metadata-store.js +160 -0
  117. package/dist/sessions/share.js +122 -0
  118. package/dist/sessions/transfer.js +16 -0
  119. package/dist/subcommands.js +62 -0
  120. package/dist/tui/agent-roster.js +36 -0
  121. package/dist/tui/app.js +3761 -0
  122. package/dist/tui/approval.js +36 -0
  123. package/dist/tui/boot/boot-sequence.js +76 -0
  124. package/dist/tui/boot/cinematic.js +243 -0
  125. package/dist/tui/boot/desktop-logo.js +82 -0
  126. package/dist/tui/boot.js +3 -0
  127. package/dist/tui/busy-input.js +58 -0
  128. package/dist/tui/catalog-service.js +559 -0
  129. package/dist/tui/components/assistant-stream.js +1 -0
  130. package/dist/tui/components/command-menu.js +68 -0
  131. package/dist/tui/components/composer.js +219 -0
  132. package/dist/tui/components/diff.js +35 -0
  133. package/dist/tui/components/footer.js +48 -0
  134. package/dist/tui/components/header.js +6 -0
  135. package/dist/tui/components/markdown.js +305 -0
  136. package/dist/tui/components/status-indicator.js +32 -0
  137. package/dist/tui/components/terminal-shell.js +205 -0
  138. package/dist/tui/components/thinking-row.js +19 -0
  139. package/dist/tui/components/tool-row.js +97 -0
  140. package/dist/tui/components/transcript-virtualizer.js +165 -0
  141. package/dist/tui/components/transcript.js +43 -0
  142. package/dist/tui/diff-model.js +77 -0
  143. package/dist/tui/editor-history.js +21 -0
  144. package/dist/tui/editor.js +210 -0
  145. package/dist/tui/event-adapter.js +436 -0
  146. package/dist/tui/exit-output.js +58 -0
  147. package/dist/tui/external-editor.js +53 -0
  148. package/dist/tui/file-completion.js +89 -0
  149. package/dist/tui/focus-manager.js +5 -0
  150. package/dist/tui/highlight.js +30 -0
  151. package/dist/tui/input-router.js +18 -0
  152. package/dist/tui/interrupt.js +99 -0
  153. package/dist/tui/keybindings.js +194 -0
  154. package/dist/tui/keymap-resolver.js +91 -0
  155. package/dist/tui/launch-state.js +139 -0
  156. package/dist/tui/line-diff.js +57 -0
  157. package/dist/tui/live-activity.js +45 -0
  158. package/dist/tui/metrics.js +123 -0
  159. package/dist/tui/onboarding.js +36 -0
  160. package/dist/tui/overlays/agent-tree.js +39 -0
  161. package/dist/tui/overlays/approval-dialog.js +640 -0
  162. package/dist/tui/overlays/automation-center.js +51 -0
  163. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  164. package/dist/tui/overlays/context-inspector.js +36 -0
  165. package/dist/tui/overlays/effort-line.js +110 -0
  166. package/dist/tui/overlays/forge-center.js +46 -0
  167. package/dist/tui/overlays/help-dialog.js +16 -0
  168. package/dist/tui/overlays/history-picker.js +37 -0
  169. package/dist/tui/overlays/hook-center.js +70 -0
  170. package/dist/tui/overlays/library-center.js +55 -0
  171. package/dist/tui/overlays/mcp-center.js +60 -0
  172. package/dist/tui/overlays/memory-center.js +63 -0
  173. package/dist/tui/overlays/model-picker.js +72 -0
  174. package/dist/tui/overlays/notes-center.js +53 -0
  175. package/dist/tui/overlays/overlay-host.js +8 -0
  176. package/dist/tui/overlays/plugin-center.js +76 -0
  177. package/dist/tui/overlays/provider-picker.js +145 -0
  178. package/dist/tui/overlays/queue-editor.js +32 -0
  179. package/dist/tui/overlays/research-center.js +59 -0
  180. package/dist/tui/overlays/scroll-window.js +234 -0
  181. package/dist/tui/overlays/session-picker.js +115 -0
  182. package/dist/tui/overlays/tasks-center.js +83 -0
  183. package/dist/tui/overlays/theme-picker.js +21 -0
  184. package/dist/tui/overlays/transcript-search.js +58 -0
  185. package/dist/tui/overlays/trust-center.js +29 -0
  186. package/dist/tui/overlays/workflow-center.js +46 -0
  187. package/dist/tui/project-file-index.js +214 -0
  188. package/dist/tui/project-references.js +85 -0
  189. package/dist/tui/project-trust-prompt.js +287 -0
  190. package/dist/tui/prompt-history-store.js +116 -0
  191. package/dist/tui/queue-store.js +110 -0
  192. package/dist/tui/regions/budget.js +59 -0
  193. package/dist/tui/regions/views.js +96 -0
  194. package/dist/tui/render-coordinator.js +148 -0
  195. package/dist/tui/render-scheduler.js +4 -0
  196. package/dist/tui/run.js +69 -0
  197. package/dist/tui/selection-list.js +29 -0
  198. package/dist/tui/session-controller.js +778 -0
  199. package/dist/tui/session-export.js +54 -0
  200. package/dist/tui/shell-input.js +35 -0
  201. package/dist/tui/shell-layout.js +48 -0
  202. package/dist/tui/shell-model.js +98 -0
  203. package/dist/tui/slash-commands.js +80 -0
  204. package/dist/tui/state.js +151 -0
  205. package/dist/tui/status-bar.js +125 -0
  206. package/dist/tui/status-view.js +40 -0
  207. package/dist/tui/terminal-capabilities.js +9 -0
  208. package/dist/tui/terminal-session.js +11 -0
  209. package/dist/tui/text-width.js +66 -0
  210. package/dist/tui/theme-catalog.js +25 -0
  211. package/dist/tui/theme-store.js +23 -0
  212. package/dist/tui/theme.js +23 -0
  213. package/dist/tui/tool-display.js +135 -0
  214. package/dist/tui/tool-facts.js +1 -0
  215. package/dist/tui/tools/bash-renderer.js +32 -0
  216. package/dist/tui/tools/change.js +61 -0
  217. package/dist/tui/tools/edit-renderer.js +17 -0
  218. package/dist/tui/tools/generic-renderer.js +13 -0
  219. package/dist/tui/tools/list-renderer.js +14 -0
  220. package/dist/tui/tools/read-renderer.js +13 -0
  221. package/dist/tui/tools/registry.js +20 -0
  222. package/dist/tui/tools/row-format.js +187 -0
  223. package/dist/tui/tools/search-renderer.js +38 -0
  224. package/dist/tui/tools/shared.js +97 -0
  225. package/dist/tui/tools/write-renderer.js +15 -0
  226. package/dist/tui/transcript-model.js +441 -0
  227. package/dist/tui/ui-preferences.js +79 -0
  228. package/dist/tui/usage-view.js +77 -0
  229. package/dist/tui/vim-mode.js +99 -0
  230. package/dist/update/check.js +15 -0
  231. package/dist/update/npm.js +51 -0
  232. package/dist/update/run.js +129 -0
  233. package/dist/version.js +2 -0
  234. package/dist/workspace/checkpoint-store.js +210 -0
  235. package/dist/workspace/checkpoint.js +413 -0
  236. package/dist/workspace/restore.js +232 -0
  237. package/package.json +63 -5
  238. package/vendor/context-engine/package.json +11 -0
  239. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  240. package/vendor/context-engine/src/contracts.mjs +48 -0
  241. package/vendor/context-engine/src/engine.mjs +445 -0
  242. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  243. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  244. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  245. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  246. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  247. package/vendor/context-engine/src/profiles.mjs +10 -0
  248. package/vendor/context-engine/src/retrieval.mjs +104 -0
  249. package/vendor/context-engine/src/summary.mjs +97 -0
  250. package/vendor/context-engine/src/usage.mjs +34 -0
  251. package/vendor/harness-ui/package.json +38 -0
  252. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  253. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  254. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  255. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  256. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  257. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  258. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  259. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  260. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  261. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  262. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  263. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  264. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  265. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  266. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  267. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  268. package/vendor/harness-ui/src/config.mjs +697 -0
  269. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  270. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  271. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  272. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  273. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  274. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  275. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  276. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  277. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  278. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  279. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  280. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  281. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  282. package/vendor/harness-ui/src/doctor.mjs +142 -0
  283. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  284. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  285. package/vendor/harness-ui/src/document-report.mjs +331 -0
  286. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  287. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  288. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  289. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  290. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  291. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  292. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  293. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  294. package/vendor/harness-ui/src/git-service.mjs +626 -0
  295. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  296. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  297. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  298. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  299. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  300. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  301. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  302. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  303. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  304. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  305. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  306. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  307. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  308. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  309. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  310. package/vendor/harness-ui/src/library-store.mjs +652 -0
  311. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  312. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  313. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  314. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  315. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  316. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  317. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  318. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  319. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  320. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  321. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  322. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  323. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  324. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  325. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  326. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  327. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  328. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  329. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  330. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  331. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  332. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  333. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  334. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  335. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  336. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  337. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  338. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  339. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  340. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  341. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  342. package/vendor/harness-ui/src/research/card.mjs +235 -0
  343. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  344. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  345. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  346. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  347. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  348. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  349. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  350. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  351. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  352. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  353. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  354. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  355. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  356. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  357. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  358. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  359. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  360. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  361. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  362. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  363. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  364. package/vendor/harness-ui/src/research/report.mjs +203 -0
  365. package/vendor/harness-ui/src/research/run.mjs +527 -0
  366. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  367. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  368. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  369. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  370. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  371. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  372. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  373. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  374. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  375. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  376. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  377. package/vendor/harness-ui/src/session-store.mjs +220 -0
  378. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  379. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  380. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  381. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  382. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  383. package/vendor/harness-ui/src/static-files.mjs +96 -0
  384. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  385. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  386. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  387. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  388. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  389. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  390. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  391. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  392. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  393. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  394. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  395. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  396. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  397. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  398. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  399. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  400. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  401. package/vendor/manifest.json +170 -0
@@ -0,0 +1,452 @@
1
+ /*
2
+ * L9 (12/09/2026) — LA RACCOLTA VIVA: il collettore portato, messo DENTRO la corsa vera.
3
+ *
4
+ * ═══════════════════════════════════════════════════════════════════════════
5
+ * Che cosa c'era prima di questo file, e perché non bastava
6
+ * ═══════════════════════════════════════════════════════════════════════════
7
+ *
8
+ * `collector.mjs` è il porto fedele di `researchCollector.ts`: dato un ramo, cerca, legge le
9
+ * prime pagine e tiene il testo. È provato, è puro, e l'I/O gli entra da `deps` — ma sul
10
+ * desktop NON GIRAVA. La ricerca approfondita qui è una sessione figlia che parla col kernel e
11
+ * chiama `web_search` e `naviga` quando vuole lei: non c'è nessun punto in cui qualcuno chiami
12
+ * `talosResearchCollect(deps, ramo)`. Conseguenza misurata l'11 e il 12/09: giornale con TRE
13
+ * eventi (`run_started`, `run_finished` e poco altro), `Piano` e `Speso` vuoti a schermo, le
14
+ * fonti mai tenute su disco, e quindi le affermazioni depositate tutte «non verificate» —
15
+ * perché senza il testo della pagina non c'è niente contro cui confrontare un passaggio.
16
+ *
17
+ * ⛔ La cura NON è far guidare il collettore al posto della figlia. Il motore del mobile
18
+ * decide lui che cosa cercare; qui è il modello a decidere, ed è il prodotto che l'owner ha
19
+ * scelto (una ricerca che ragiona, non uno scraper con un piano fisso). Costringere la figlia
20
+ * dentro `talosResearchCollect` vorrebbe dire buttare via la parte che funziona.
21
+ *
22
+ * ⇒ Questo file è il PONTE: gli attrezzi che la figlia usa davvero (`web_search`, `naviga`)
23
+ * passano da qui, e qui succede esattamente quello che il collettore faceva — la cache della
24
+ * corsa (`fetch-cache.mjs`), il budget di pagina (`page-budget.mjs`), il testo TENUTO
25
+ * (`fonti/<sha256>.txt`), la spesa CONTATA — più la cosa che il collettore non poteva fare da
26
+ * solo: ogni passo finisce nel GIORNALE, che è ciò che rende la corsa riprendibile.
27
+ *
28
+ * ⛔⛔ LA PORTA È UNA SOLA, ed è deliberato: questo oggetto espone `around(descrittore,
29
+ * produttore)`, cioè la STESSA firma di `fetch-cache.mjs`. Il kernel riceve un `cacheWeb` e
30
+ * non sa (e non deve sapere) se dietro c'è la cache nuda o la raccolta: un secondo punto di
31
+ * aggancio nel kernel sarebbe un secondo contratto da tenere allineato, ed è il modo in cui
32
+ * due strade divergono in silenzio. Il kernel resta condiviso col mobile e non importa una
33
+ * sola riga di `src/research/`.
34
+ *
35
+ * ═══════════════════════════════════════════════════════════════════════════
36
+ * Ricerca web PRIMA di scrivere — fonti primarie, lette il 12/09/2026
37
+ * ═══════════════════════════════════════════════════════════════════════════
38
+ * (`WebSearch` esaurito, 200/200 — come per L1-L6: fonti primarie via `WebFetch`.)
39
+ *
40
+ * - **Anthropic, «How we built our multi-agent research system»**
41
+ * (<https://www.anthropic.com/engineering/multi-agent-research-system>): «agents typically
42
+ * use about 4× more tokens than chat interactions, and multi-agent systems use about **15×**
43
+ * more tokens than chats». ⇒ è il numero che giustifica il «costo detto prima» del piano: su
44
+ * una corsa che costa quindici volte una chat, scoprire la spesa DOPO non è un dettaglio di
45
+ * presentazione. Stessa pagina: «The LeadResearcher begins by thinking through the approach
46
+ * and **saving its plan to Memory to persist the context**» e «we built systems that can
47
+ * **resume from where the agent was** when the errors occurred» — le due cose che questo
48
+ * lotto aggiunge alla corsa, non inventate qui.
49
+ * - **Gao et al., «Enabling Large Language Models to Generate Text with Citations»**
50
+ * (arXiv:2305.14627, 24/05/2023, rev. 31/10/2023 — benchmark ALCE): «on the ELI5 dataset,
51
+ * even the best models **lack complete citation support 50% of the time**». ⇒ il passaggio
52
+ * che il modello dichiara non è una prova finché qualcuno non lo ritrova nel testo della
53
+ * pagina: è esattamente ciò per cui il testo va TENUTO, e perché tenerlo è un lotto e non un
54
+ * optional.
55
+ *
56
+ * ⛔ Il vincolo che la ricerca ha aggiunto e che non conoscevo: il costo di una corsa
57
+ * multi-agente non è dominato dalla generazione ma dalla RILETTURA del contesto. È la ragione
58
+ * per cui qui si conta la FINESTRA (ciò che arriva al modello) e non il testo conservato:
59
+ * conservare di più non costa un token, mostrare di più sì.
60
+ *
61
+ * ═══════════════════════════════════════════════════════════════════════════
62
+ * L'attribuzione al ramo — una CONVENZIONE dichiarata, non una misura
63
+ * ═══════════════════════════════════════════════════════════════════════════
64
+ *
65
+ * Il giornale vuole un `branchId` per ogni passo, e il modello non riceve gli id dei rami (sono
66
+ * un dato del server: `b1`, `b2`, …). L'attribuzione è quindi **per ordine di arrivo**: la
67
+ * prima ricerca distinta è il ramo 1 del piano, la seconda il ramo 2, e così via; oltre il
68
+ * piano si usano nomi `oltre-N` che NON esistono nel piano, così nessuno li scambi per rami
69
+ * approvati. Le letture di pagina ereditano il ramo della ricerca che le ha trovate.
70
+ *
71
+ * ⛔ Perché è onesta abbastanza: la consegna della figlia ADESSO elenca le linee d'indagine del
72
+ * piano, nell'ordine (`research-orchestrator.promptRicerca`), quindi l'ordine non è un
73
+ * sorteggio — è quello che le abbiamo chiesto di seguire. Ma resta una convenzione: ciò che è
74
+ * **esatto** è il conteggio (quante ricerche, quante pagine, quanti caratteri); ciò che è
75
+ * **approssimato** è a quale ramo appartengano, e serve a una cosa sola — dire a una ripresa
76
+ * quali linee sono ancora aperte. Dichiararlo qui costa una riga; scoprirlo dopo costa una
77
+ * diagnosi sbagliata.
78
+ * ⛔ E la chiave del passo è derivata dal CONTENUTO (la chiave di cache del descrittore), mai da
79
+ * un contatore: la stessa ricerca fatta due volte è LO STESSO passo, che è la regola che
80
+ * `run.mjs` scrive a lettere maiuscole («un nome nuovo è un passo che nessuno può riconoscere
81
+ * come già fatto — il modo in cui un giro ripreso paga due volte la stessa ricerca»).
82
+ */
83
+
84
+ import { createHash } from 'node:crypto';
85
+
86
+ import { talosResearchFetchKey } from './fetch-cache.mjs';
87
+ import { TALOS_RESEARCH_HEAD_SHARE, talosResearchPageBudget } from './page-budget.mjs';
88
+ import { talosResearchStepIdFor } from './run.mjs';
89
+
90
+ /**
91
+ * @typedef {import('./run.mjs').TalosResearchBranch} TalosResearchBranch
92
+ * @typedef {import('./collector.mjs').TalosResearchSource} TalosResearchSource
93
+ * @typedef {import('./fetch-cache.mjs').TalosResearchFetchDescriptor} TalosResearchFetchDescriptor
94
+ */
95
+
96
+ /**
97
+ * Quanto testo di una pagina si CONSERVA sul desktop.
98
+ *
99
+ * ⛔ Diverso dal telefono di proposito, e il numero ha una ragione: sul mobile
100
+ * (`collector.mjs`) il tetto è 20.000 caratteri perché l'intero dossier vive in un database
101
+ * solo. Qui ogni fonte è un file suo in `fonti/<sha256>.txt`, e il collettore lo dice in chiaro
102
+ * («Sul desktop il dossier sta su disco e non in un database solo: chi lo sa alza questo, e il
103
+ * budget resta sotto»). 200.000 caratteri sono ~50 pagine di prosa: abbastanza per ritrovare un
104
+ * passaggio un anno dopo, abbastanza poco da non riempire un disco con una corsa.
105
+ * ⛔ E resta SOPRA la finestra mostrata al modello: ciò che si conserva non è ciò che si mostra,
106
+ * e invertirli renderebbe inutile il budget.
107
+ */
108
+ export const TALOS_RESEARCH_CONSERVA_DESKTOP = 200_000;
109
+
110
+ /**
111
+ * ⛔⛔⛔⭐⭐⭐ TROVATO DAL VIVO IL 12/09/2026 — IL BUDGET DA 15.000 NON PASSAVA DALLA PORTA.
112
+ *
113
+ * Il rapporto L6 ha misurato su **430 pagine vere** che 15.000 caratteri (testa 75%, coda 25%)
114
+ * sono il taglio giusto, e `TALOS_RESEARCH_PAGE_BUDGET` vale quello. Ma il risultato di un
115
+ * attrezzo, nel kernel, viene tagliato a **8.000 caratteri** prima di entrare nel messaggio
116
+ * `role:'tool'` (`talosHarness.mjs`: `String(esito).slice(0, 8_000)`, salvo `contextHooks`
117
+ * attivi). ⇒ una finestra da 15.191 caratteri arrivava al modello **senza il marcatore e senza
118
+ * la coda**: cioè esattamente le due cose per cui il budget esiste, tolte in silenzio da un
119
+ * tetto che sta in un altro file e che nessuno aveva confrontato col primo.
120
+ *
121
+ * ⛔ L'ho trovato perché un test lo ha fatto fallire (`CHIUSURA` non arrivava), non perché
122
+ * l'avessi previsto: due tetti su due file diversi, scritti in due lotti diversi, e il più
123
+ * stretto vince senza dirlo. È la firma del difetto che questo repo chiama «il cancello che
124
+ * non guardava tutto».
125
+ *
126
+ * ⛔ Perché NON si alza il tetto del kernel: quel `slice` vale per OGNI attrezzo di OGNI harness,
127
+ * TALOS-BANCO compreso — cambiarlo sposterebbe il profilo di token di ogni campagna per una
128
+ * ragione che riguarda una sola funzione. Si abbassa la finestra, che è la cosa locale.
129
+ *
130
+ * ⇒ 7.500, e il numero è ARITMETICA, non una scelta tonda. Il tetto conta **tutto** ciò che
131
+ * finisce nella stringa, e la finestra non è solo testa+coda:
132
+ * 7.500 i caratteri MOSTRATI (`cap`, cioè testa + coda: è ciò che `page-budget` garantisce)
133
+ * + ~190 il MARCATORE, che porta dentro il percorso della fonte (`fonti/<64 esadecimali>.txt`)
134
+ * + ~60 la riga di testa di `naviga` (`HTTP 200 · <url>\n`)
135
+ * ─────
136
+ * ~7.750 su 8.000 — con margine, invece che al pelo.
137
+ * ⛔ La prima versione di questa costante era **7.800** e continuava a perdere la coda: il
138
+ * marcatore non era nel conto. È lo stesso difetto una seconda volta, in miniatura — un
139
+ * tetto confrontato con un numero che non è quello vero — e l'ha trovato di nuovo il test,
140
+ * non il ragionamento.
141
+ * ⇒ Resta comunque quasi il DOPPIO dei 4.000 di oggi, e soprattutto porta la CODA, che il
142
+ * taglio di oggi non porta mai.
143
+ * ⛔ E quando `contextHooks` è attivo il tetto del kernel non si applica: allora questa finestra
144
+ * è più stretta del necessario. Dichiarato, non risolto — legare i due significherebbe far
145
+ * dipendere il budget da una configurazione che qui dentro non si vede.
146
+ */
147
+ export const TALOS_RESEARCH_FINESTRA_SOTTO_IL_KERNEL = 7_500;
148
+
149
+ /** Quattro caratteri per token: la stessa regola pratica di `collector.mjs`, non una seconda. */
150
+ const CARATTERI_PER_TOKEN = 4;
151
+
152
+ /** @param {string} testo */
153
+ function normalizza(testo) {
154
+ return String(testo ?? '').replace(/\s+/g, ' ').trim();
155
+ }
156
+
157
+ /** @param {string} valore */
158
+ function impronta12(valore) {
159
+ return createHash('sha256').update(String(valore), 'utf8').digest('hex').slice(0, 12);
160
+ }
161
+
162
+ /**
163
+ * ⛔ `null` invece di lanciare: una data che la fonte non dichiara è `null`, mai una
164
+ * supposizione — è la stessa regola di `formattaRisultatiRicerca` nel kernel («una data assente
165
+ * si DICHIARA, mai una supposizione»).
166
+ *
167
+ * @param {unknown} valore
168
+ * @returns {string | null}
169
+ */
170
+ function dataDichiarata(valore) {
171
+ return typeof valore === 'string' && valore.trim().length > 0 ? valore.trim() : null;
172
+ }
173
+
174
+ /**
175
+ * La raccolta di UNA corsa.
176
+ *
177
+ * @param {object} opzioni
178
+ * @param {{around: (d: TalosResearchFetchDescriptor, p: () => Promise<any>) => Promise<{value:any, fromCache:boolean, key:string}>, snapshot?: () => any, restore?: (i:any) => number, stats?: () => any}} opzioni.cache
179
+ * La cache vera della corsa (`fetch-cache.mjs`). ⛔ Obbligatoria: senza, questa classe
180
+ * sarebbe una seconda implementazione della stessa idea.
181
+ * @param {(evento: object) => Promise<void> | void} opzioni.registra
182
+ * Una riga nel giornale. ⛔ Un guasto del giornale NON deve fermare la corsa: chi la passa
183
+ * inghiotte già l'errore (`research-orchestrator.registra`), e qui non si ricontrolla —
184
+ * due `catch` sullo stesso guasto sono due politiche che divergono.
185
+ * @param {(testo: string) => Promise<{ref: string, giaPresente: boolean}>} opzioni.tieniFonte
186
+ * Deposita il testo di una pagina e torna il suo `fonti/<sha256>.txt`.
187
+ * @param {(voce: {url: string, ref: string, titolo: string, dataDichiarata: string|null, ottenuta: 'page'|'snippet'}) => Promise<void> | void} [opzioni.annotaFonte]
188
+ * L'indice url → ref, che è ciò che permette a una VERIFICA dopo un riavvio di ritrovare il
189
+ * testo. Senza, la verifica funziona finché il processo vive e non un minuto di più.
190
+ * @param {() => readonly TalosResearchBranch[]} [opzioni.piano] I rami approvati, per l'attribuzione.
191
+ * @param {number} [opzioni.tettoFinestra] Caratteri mostrati al modello.
192
+ * @param {number} [opzioni.tettoConservato] Caratteri conservati su disco.
193
+ */
194
+ export function creaRaccoltaViva({
195
+ cache,
196
+ registra,
197
+ tieniFonte,
198
+ annotaFonte = null,
199
+ piano = () => [],
200
+ tettoFinestra = TALOS_RESEARCH_FINESTRA_SOTTO_IL_KERNEL,
201
+ tettoConservato = TALOS_RESEARCH_CONSERVA_DESKTOP,
202
+ }) {
203
+ /** chiave di cache → id del passo. Il nome di un passo è derivato, mai contato. */
204
+ const passoPerChiave = new Map();
205
+ /** url (richiesto E finale) → la finestra già calcolata per quella pagina. */
206
+ const finestraPerUrl = new Map();
207
+ /** url → la fonte raccolta, col testo TENUTO. È ciò che la verifica legge. */
208
+ const fontiPerUrl = new Map();
209
+ /** Quante ricerche distinte sono già passate: decide il ramo della prossima. */
210
+ let ricercheDistinte = 0;
211
+ /** Il ramo dell'ultima ricerca: le pagine aperte dopo appartengono a lei. */
212
+ let ramoCorrente = null;
213
+
214
+ /** @param {TalosResearchFetchDescriptor} descrittore */
215
+ function nomeDelPasso(descrittore) {
216
+ const chiave = talosResearchFetchKey(descrittore);
217
+ const gia = passoPerChiave.get(chiave);
218
+ if (gia) return { ...gia, chiave, nuovo: false };
219
+
220
+ if (descrittore.kind === 'search') {
221
+ const rami = piano() ?? [];
222
+ const ramo = ricercheDistinte < rami.length
223
+ ? rami[ricercheDistinte].id
224
+ // ⛔ `oltre-N` e non `b<N>`: un id che non è nel piano non deve poter essere scambiato
225
+ // per un ramo approvato da chi rilegge il giornale.
226
+ : `oltre-${ricercheDistinte + 1 - rami.length}`;
227
+ ricercheDistinte += 1;
228
+ ramoCorrente = ramo;
229
+ const voce = { stepId: talosResearchStepIdFor(ramo, 'search'), branchId: ramo, stepKind: /** @type {const} */ ('search') };
230
+ passoPerChiave.set(chiave, voce);
231
+ return { ...voce, chiave, nuovo: true };
232
+ }
233
+
234
+ const ramo = ramoCorrente ?? (piano()?.[0]?.id ?? 'oltre-1');
235
+ const voce = {
236
+ // ⛔ L'impronta dell'URL dentro il nome: due pagine dello stesso ramo sono due passi, e
237
+ // la stessa pagina riaperta è LO STESSO passo, qualunque cosa sia successa in mezzo.
238
+ stepId: `${ramo}:read:${impronta12(descrittore.url ?? '')}`,
239
+ branchId: ramo,
240
+ stepKind: /** @type {const} */ ('read'),
241
+ };
242
+ passoPerChiave.set(chiave, voce);
243
+ return { ...voce, chiave, nuovo: true };
244
+ }
245
+
246
+ /**
247
+ * @param {string} testo
248
+ * @param {string} url
249
+ * @returns {{finestra: string, omessi: number}}
250
+ */
251
+ function finestraDi(testo, url) {
252
+ const tagliata = talosResearchPageBudget(testo, {
253
+ cap: tettoFinestra,
254
+ headShare: TALOS_RESEARCH_HEAD_SHARE,
255
+ /*
256
+ * ⛔ Il riferimento è il PERCORSO VERO del testo tenuto, non una promessa: il marcatore
257
+ * dice al modello dove sta il resto, e `page-budget.mjs` tace quando non c'è un
258
+ * deposito («chi non ha un deposito non passa niente»). Qui c'è, perché la fonte è già
259
+ * stata scritta prima di calcolare la finestra.
260
+ */
261
+ reference: fontiPerUrl.get(url)?.riferimento ?? null,
262
+ });
263
+ return { finestra: tagliata.window, omessi: tagliata.omitted };
264
+ }
265
+
266
+ /**
267
+ * Tiene una pagina: il testo su disco, la voce nell'indice, la finestra in memoria.
268
+ *
269
+ * @param {string} url
270
+ * @param {string} corpo
271
+ * @param {{titolo?: string, pubblicato?: string|null, ottenuta?: 'page'|'snippet'}} [extra]
272
+ */
273
+ async function tieni(url, corpo, extra = {}) {
274
+ const testo = normalizza(corpo).slice(0, tettoConservato);
275
+ if (testo.length === 0) return null;
276
+ const ottenuta = extra.ottenuta ?? 'page';
277
+ let ref = null;
278
+ try {
279
+ ({ ref } = await tieniFonte(testo));
280
+ } catch {
281
+ /*
282
+ * ⛔ Una fonte che non si è potuta SCRIVERE non ferma la corsa, esattamente come il
283
+ * giornale: si perde la ri-verifica di quella pagina, non il lavoro già pagato. Ma la
284
+ * fonte resta in memoria, quindi la verifica di QUESTA corsa la vede lo stesso.
285
+ */
286
+ ref = null;
287
+ }
288
+ const voce = {
289
+ url,
290
+ title: extra.titolo || url,
291
+ publishedAt: dataDichiarata(extra.pubblicato),
292
+ text: testo,
293
+ obtained: ottenuta,
294
+ ref,
295
+ riferimento: ref ? { percorso: ref } : null,
296
+ };
297
+ fontiPerUrl.set(url, voce);
298
+ if (annotaFonte && ref) {
299
+ try {
300
+ await annotaFonte({
301
+ url, ref, titolo: voce.title, dataDichiarata: voce.publishedAt, ottenuta,
302
+ });
303
+ } catch { /* come sopra: l'indice è un risparmio, non una condizione per lavorare. */ }
304
+ }
305
+ return voce;
306
+ }
307
+
308
+ return Object.freeze({
309
+ /**
310
+ * LA PORTA. Stessa firma di `fetch-cache.around`, e il kernel non distingue le due.
311
+ *
312
+ * @template T
313
+ * @param {TalosResearchFetchDescriptor} descrittore
314
+ * @param {() => Promise<T>} produttore
315
+ * @returns {Promise<{value: T, fromCache: boolean, key: string}>}
316
+ */
317
+ async around(descrittore, produttore) {
318
+ const passo = nomeDelPasso(descrittore);
319
+ await registra({ kind: 'step_started', stepId: passo.stepId, branchId: passo.branchId, stepKind: passo.stepKind });
320
+
321
+ let esito;
322
+ try {
323
+ esito = await cache.around(descrittore, produttore);
324
+ } catch (guasto) {
325
+ /*
326
+ * ⛔ `step_failed`, e poi si RILANCIA. Il giornale registra che quel passo ha provato e
327
+ * non ce l'ha fatta — che è diverso da «interrotto» (`run.mjs`: «il primo è un
328
+ * risultato, il secondo è una domanda») — ma l'errore appartiene al kernel, che sa
329
+ * dirlo al modello. Inghiottirlo qui trasformerebbe una pagina bloccata in una
330
+ * pagina vuota, cioè in un silenzio.
331
+ */
332
+ await registra({
333
+ kind: 'step_failed', stepId: passo.stepId,
334
+ error: guasto instanceof Error ? guasto.message : String(guasto),
335
+ });
336
+ throw guasto;
337
+ }
338
+
339
+ if (descrittore.kind === 'search') {
340
+ const righe = Array.isArray(esito.value) ? esito.value : [];
341
+ /*
342
+ * ⛔ Gli estratti dei risultati si TENGONO come fonti `snippet`, e non è zelo: se la
343
+ * figlia cita una pagina che non ha mai aperto, senza questo la verifica direbbe «la
344
+ * fonte citata non esiste fra quelle raccolte» — un motivo FALSO per un fatto vero
345
+ * («l'ha vista solo dall'elenco»). `resolved: 'snippet'` è la risposta giusta, e
346
+ * `report.mjs` la stampa come «solo estratto dal motore di ricerca».
347
+ * ⛔ Ma non si sovrascrive una pagina già APERTA con il suo estratto: una prova più
348
+ * debole non deve poter cancellare una più forte.
349
+ */
350
+ for (const riga of righe) {
351
+ const url = typeof riga?.url === 'string' ? riga.url : '';
352
+ if (!url || fontiPerUrl.has(url)) continue;
353
+ await tieni(url, riga?.snippet ?? '', {
354
+ titolo: riga?.title, pubblicato: riga?.pubblicato ?? riga?.publishedAt, ottenuta: 'snippet',
355
+ });
356
+ }
357
+ const caratteri = righe.reduce(
358
+ (t, r) => t + String(r?.title ?? '').length + String(r?.url ?? '').length + String(r?.snippet ?? '').length,
359
+ 0,
360
+ );
361
+ await registra({
362
+ kind: 'step_finished', stepId: passo.stepId,
363
+ spend: {
364
+ // ⛔ Servita dalla cache ⇒ NON è stata pagata. Contarla renderebbe invisibile il
365
+ // risparmio proprio nel numero fatto per mostrare il costo (`collector.mjs`).
366
+ searches: esito.fromCache ? 0 : 1,
367
+ pages: 0,
368
+ tokens: Math.ceil(caratteri / CARATTERI_PER_TOKEN),
369
+ },
370
+ resultRef: null,
371
+ });
372
+ return esito;
373
+ }
374
+
375
+ const pagina = esito.value ?? {};
376
+ const urlRichiesto = descrittore.url ?? '';
377
+ const urlFinale = typeof pagina?.url === 'string' && pagina.url ? pagina.url : urlRichiesto;
378
+ const tenuta = await tieni(urlFinale, pagina?.corpo ?? '');
379
+ const { finestra, omessi } = tenuta ? finestraDi(tenuta.text, urlFinale) : { finestra: '', omessi: 0 };
380
+ // Indicizzata su ENTRAMBI gli indirizzi: il modello ha chiesto il primo, la pagina ha
381
+ // risposto col secondo dopo i reindirizzamenti, e chi cerca la finestra può avere l'uno o
382
+ // l'altro in mano.
383
+ finestraPerUrl.set(urlFinale, finestra);
384
+ if (urlRichiesto) finestraPerUrl.set(urlRichiesto, finestra);
385
+ await registra({
386
+ kind: 'step_finished', stepId: passo.stepId,
387
+ spend: {
388
+ searches: 0,
389
+ pages: esito.fromCache ? 0 : 1,
390
+ // ⛔ Si contano i caratteri della FINESTRA, non del testo conservato: i token si
391
+ // pagano su ciò che arriva al modello. Identica scelta e identica riga in
392
+ // `collector.mjs`.
393
+ tokens: Math.ceil((finestra.length || 0) / CARATTERI_PER_TOKEN),
394
+ },
395
+ resultRef: tenuta?.ref ?? null,
396
+ });
397
+ if (omessi > 0) { /* il marcatore lo dice già dentro la finestra: niente evento in più. */ }
398
+ return esito;
399
+ },
400
+
401
+ /**
402
+ * Che cosa vede il modello di questa pagina. `null` = «non lo so», e chi chiama ricade sul
403
+ * suo taglio di sempre invece di mostrare il vuoto.
404
+ *
405
+ * @param {string} url
406
+ * @param {string} corpo
407
+ * @returns {Promise<string | null>}
408
+ */
409
+ async paginaLetta(url, corpo) {
410
+ const gia = finestraPerUrl.get(url);
411
+ if (typeof gia === 'string') return gia;
412
+ /*
413
+ * ⛔ Il ripiego esiste per un caso reale, non per simmetria: `naviga` può arrivare qui
414
+ * senza essere passato da `around` (una versione futura del kernel, un test che aggancia
415
+ * solo questo). Meglio una finestra calcolata al volo che nessuna.
416
+ */
417
+ const tenuta = await tieni(url, corpo ?? '');
418
+ if (!tenuta) return null;
419
+ const { finestra } = finestraDi(tenuta.text, url);
420
+ finestraPerUrl.set(url, finestra);
421
+ return finestra;
422
+ },
423
+
424
+ /**
425
+ * Le fonti raccolte, nell'ordine in cui sono arrivate. ⛔ Col `text` TENUTO: è ciò che la
426
+ * verifica confronta col passaggio citato, e senza il quale ogni affermazione resta «non
427
+ * verificata» per mancanza di prova invece che per mancanza di giudice.
428
+ *
429
+ * @returns {readonly (TalosResearchSource & {ref: string|null})[]}
430
+ */
431
+ fonti() {
432
+ return [...fontiPerUrl.values()].map(({ riferimento, ...resto }) => resto);
433
+ },
434
+
435
+ /** url → testo tenuto. La mappa che la verifica usa per riempire `source.text`. */
436
+ testiPerUrl() {
437
+ const mappa = new Map();
438
+ for (const [url, voce] of fontiPerUrl) mappa.set(url, voce.text);
439
+ return mappa;
440
+ },
441
+
442
+ /** Quanti passi distinti sono stati nominati finora. Per i test e per le sonde, mai per il modello. */
443
+ passiNominati() {
444
+ return passoPerChiave.size;
445
+ },
446
+
447
+ /** La cache vera che sta sotto: chi deve salvarne l'istantanea la prende da qui. */
448
+ cacheSottostante() {
449
+ return cache;
450
+ },
451
+ });
452
+ }
@@ -0,0 +1,69 @@
1
+ /*
2
+ * PORTO FEDELE di AVM/mobile/src/lib/research/researchRecheckDocument.ts (57 righe, 11/09/2026).
3
+ *
4
+ * Il ricontrollo, scritto giù così non si deve pagare due volte.
5
+ *
6
+ * Stesso principio del rapporto: un controllo che non lascia traccia è un
7
+ * controllo che viene rifatto ogni volta che qualcuno se lo chiede e — peggio —
8
+ * non si può confrontare con quello di prima. Archiviato in Libreria accanto al
9
+ * dossier di cui parla.
10
+ *
11
+ * La frase più semplice del file è quella sulle fonti irraggiungibili: la
12
+ * pagina è sparita E il testo è ancora qui. È la frase che nessun concorrente
13
+ * può scrivere, e vale la pena spenderci una riga.
14
+ *
15
+ * ⛔ E porta in coda un blocco che si RILEGGE esatto: la prosa qui sopra è
16
+ * per una persona, e ricavarne i numeri ripassando l'italiano stampato è il
17
+ * modo in cui una misura diventa un'invenzione. Senza, «quanto vale oggi un
18
+ * rapporto di ieri» resta una domanda a cui il file non sa rispondere.
19
+ */
20
+
21
+ import { talosResearchRecheckStanding } from './recheck.mjs';
22
+ import { talosResearchRecheckBlock } from './recheck-history.mjs';
23
+
24
+ /** @typedef {import('./recheck.mjs').TalosResearchRecheck} TalosResearchRecheck */
25
+
26
+ /**
27
+ * @param {string} question
28
+ * @param {TalosResearchRecheck} recheck
29
+ * @param {string} runId
30
+ * @returns {string}
31
+ */
32
+ export function talosResearchRecheckDocument(question, recheck, runId) {
33
+ const standing = talosResearchRecheckStanding(recheck);
34
+
35
+ return [
36
+ `# Ricontrollo — ${question}`,
37
+ '',
38
+ `Data del ricontrollo: ${recheck.at}`,
39
+ '',
40
+ `Fonti ricontrollate: ${standing.total}`,
41
+ `- intatte: ${standing.intact}`,
42
+ `- cambiate dal giorno della ricerca: ${standing.changed}`,
43
+ `- non rispondono più: ${standing.unreachable}`,
44
+ standing.passagesLost > 0
45
+ ? `\n**${standing.passagesLost} passaggi citati non sono più nella loro fonte.**`
46
+ : '\nTutti i passaggi citati sono ancora nelle loro fonti.',
47
+ standing.unreachable > 0
48
+ ? '\nLe fonti che non rispondono più restano leggibili qui: il testo estratto\nè stato conservato il giorno della ricerca.'
49
+ : '',
50
+ '',
51
+ '## Fonte per fonte',
52
+ ...recheck.sources.map((source) => {
53
+ const head = source.state === 'unreachable'
54
+ ? `non risponde più${source.reason ? ` (${source.reason})` : ''}`
55
+ : source.state === 'intact'
56
+ ? `intatta (${Math.round((source.survived ?? 0) * 100)}% del testo di allora è ancora lì)`
57
+ : `cambiata (${Math.round((source.survived ?? 0) * 100)}% del testo di allora è ancora lì)`;
58
+ const quotes = source.passagesLost > 0
59
+ ? ` ${source.passagesLost} passaggi citati non ci sono più, ${source.passagesStanding} reggono ancora`
60
+ : source.passagesStanding > 0
61
+ ? ` i ${source.passagesStanding} passaggi citati reggono ancora`
62
+ : '';
63
+ return [`- ${source.title} — ${source.url}`, ` ${head}`, quotes].filter(Boolean).join('\n');
64
+ }),
65
+ // Il `filter` qui sotto toglie le righe vuote: la riga bianca prima del
66
+ // blocco va dentro la stringa, se no prosa e recinto si toccano.
67
+ '\n' + talosResearchRecheckBlock(runId, recheck),
68
+ ].filter((line) => line !== '').join('\n');
69
+ }