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,337 @@
1
+ /**
2
+ * elenco-profondo.mjs — P-13: il COSTRUTTORE dell'elenco profondo dei file.
3
+ *
4
+ * ⛔ Perché esiste, misurato (non da rifare): l'attrezzo di elenco del kernel arriva a
5
+ * profondità 2. I 106 percorsi dei task del corpus `storia` stanno a profondità 4-6, ZERO a
6
+ * profondità ≤ 2, e 35 consegne su 35 non nominano nessun file. TALOS non può risolverne
7
+ * nessuno — e non per bravura del modello: non li VEDE. Pass-rate oggi 0 su 35.
8
+ * Su questo repo, con potatura: profondità 2 → 336 file (~3.400 token) · tutto l'albero, con
9
+ * fuori anche ciò che `git` ignora → 717 file (~7.900 token). Il costo della vista completa è
10
+ * ~4.500 token: si paga. (Misurato con questo modulo il 10/09/2026, e non combacia al file con
11
+ * la stima che me l'ha commissionato — 333 e 676 — perché quella non contava `frontend/`.)
12
+ *
13
+ * ⛔ PURA rispetto alla configurazione: il filesystem si inietta (`fs`), come in
14
+ * `local-model-store.mjs` (`fsImpl`) e `workspace-disk.mjs` (`readdirFn`). Nessuna decisione
15
+ * presa leggendo l'ambiente: tutto arriva dagli argomenti.
16
+ *
17
+ * ── LE QUATTRO DECISIONI, con le loro fonti ──────────────────────────────────────────────
18
+ *
19
+ * (1) COSA SI POTA. La lista non è inventata qui: è l'unione di quelle che questo repo ha già
20
+ * — `src/workspace-info.mjs:40` (la più ampia) e `src/workspace-context.mjs:25` — più
21
+ * `bower_components`, che è l'altra metà del default di VS Code («search.exclude» di
22
+ * serie esclude `node_modules` e `bower_components` a ogni livello, microsoft/vscode
23
+ * #84619 e #8934, letti il 10/09/2026). Prima di cercare fuori ho cercato dentro: la lezione
24
+ * `chi-guarda-da-fuori-inventa-quello-che-dentro-aveva-gia` dice esattamente questo.
25
+ * ⛔ `dist`, `build`, `out`, `target`, `vendor` a volte contengono codice VERO (il
26
+ * `vendor/` di Go è sorgente). Si potano lo stesso — sono grandi, generati e non si
27
+ * modificano — ma la potatura è un ARGOMENTO (`escludiCartelle`), non una legge: chi
28
+ * lavora su un progetto Go passa la sua lista.
29
+ *
30
+ * (2) QUALI ESTENSIONI TENERE — e perché una DENYLIST, non una allowlist.
31
+ * Gli strumenti veri non decidono per estensione: ripgrep chiama binario un file se e
32
+ * solo se contiene un byte NUL, guardando i primi kilobyte (BurntSushi/ripgrep
33
+ * discussion #2050 e la guida ufficiale, letti il 10/09/2026). Qui la firma NON si usa,
34
+ * per due ragioni misurabili: (a) questo elenco è fatto di NOMI, non di contenuti —
35
+ * leggere i primi byte di 676 file sono 676 `open`+`read` in più per rispondere a una
36
+ * domanda che non riguarda il contenuto; (b) non risolverebbe il caso vero: un `.png` da
37
+ * 4 MB è legittimamente binario, la firma lo confermerebbe e basta.
38
+ * ⛔ E il verso della lista conta più della lista: una ALLOWLIST («tieni solo .js e .md»)
39
+ * invecchiando fa sparire un file che esiste — `.astro`, `.zig`, `Dockerfile`, `Makefile`
40
+ * (che estensione non ha), il linguaggio nato il mese scorso — e il modello concluderebbe
41
+ * che non c'è: è la BUGIA che questo modulo esiste per non dire. Una DENYLIST che
42
+ * invecchia produce al massimo una riga di rumore. I due errori non costano uguale, e si
43
+ * sceglie il verso che sbaglia dalla parte del rumore.
44
+ *
45
+ * (3) L'ORDINE — deterministico, e imposto DURANTE la camminata, non solo alla fine.
46
+ * La cache del prefisso si aggancia solo se i byte sono IDENTICI: il suo indice è il
47
+ * rendering esatto fino al punto di taglio, e un solo byte diverso alla posizione N
48
+ * invalida tutto da lì in poi; chi assembla da un dizionario o da un insieme deve
49
+ * ordinare per nome, altrimenti la cache si azzera in silenzio (documentazione Claude
50
+ * «Prompt caching» e la guida `anthropics/skills` prompt-caching.md, letti il
51
+ * 10/09/2026). In memoria è già misurato: la cache costa 1/6 e prende dalla terza
52
+ * chiamata — l'87% dei token entra e non esce.
53
+ * ⇒ Tre conseguenze, tutte e tre nel codice qui sotto:
54
+ * · le voci di ogni cartella si ordinano PRIMA di scendere — `readdir` non promette
55
+ * nessun ordine (su ext4 è per hash del nome), quindi ordinare solo alla fine
56
+ * renderebbe deterministico l'elenco COMPLETO ma non quello TRONCATO: il tetto
57
+ * taglierebbe file diversi a ogni giro;
58
+ * · si confronta per unità di codice (`<`), MAI con `localeCompare`: l'ordine locale
59
+ * dipende dai dati ICU compilati dentro Node, e su un ambiente senza ICU completo
60
+ * cambia. È la stessa trappola già scritta in `src/workspace-info.mjs:29`;
61
+ * · si cammina in AMPIEZZA (un livello alla volta): se il tetto morde, ciò che
62
+ * sopravvive è la roba vicina alla radice — `package.json`, `src/`, `tests/` — non il
63
+ * primo sottoalbero incontrato fino in fondo.
64
+ *
65
+ * (4) IL TETTO — 1.500, non 4.000. Il 4.000 di partenza non era una misura. La misura, fatta
66
+ * con questo stesso modulo su questo repo il 10/09/2026:
67
+ * · albero intero, sola potatura di serie ...... 1.700 file · 4.718 esclusi per estensione
68
+ * · togliendo ciò che `git` già ignora ......... 717 file · 27.640 caratteri · ~7.900 token
69
+ * · costo per percorso .......................... ~11 token (per il codice il rapporto
70
+ * carattere/token scende a ~3-3,5 contro i ~4 della prosa, regola confermata il
71
+ * 10/09/2026: nomi lunghi e separatori pagano più della prosa)
72
+ * A ~11-17 token per riga, 4.000 file sono 44.000-67.000 token di sola lista dentro un
73
+ * prompt che si paga a ogni giro (scontato dalla cache, non gratis). 1.500 file sono
74
+ * ~16.000-25.000 token nel caso peggiore misurato: abbastanza per mostrare per intero un
75
+ * repo grande, poco abbastanza da non mangiare il contesto.
76
+ * ⛔ Misurato lo stesso giorno, e vale per chi aggancia: SENZA il filtro del `.gitignore`
77
+ * il tetto morde già qui, e non per colpa del codice — 944 dei 1.700 file sono artefatti di
78
+ * una cartella di esiti che `git` ignora. Col filtro attaccato restano 717 file e il tetto
79
+ * non si avvicina nemmeno. Il filtro non è un abbellimento: è ciò che rende il tetto raro.
80
+ * ⛔ E resta un tetto GREZZO: la moneta vera sono i token, e il contatore vero è già in
81
+ * `src/context-token-counters.mjs`. Chi aggancia questo modulo decida se tagliare lì.
82
+ */
83
+ import { readdir, realpath, stat } from 'node:fs/promises';
84
+ import { basename, join } from 'node:path';
85
+
86
+ export class ElencoProfondoError extends Error {
87
+ constructor(message) {
88
+ super(message);
89
+ this.name = 'ElencoProfondoError';
90
+ this.code = 'ELENCO_INVALIDO';
91
+ }
92
+ }
93
+
94
+ /** Vedi decisione (1). Unione di `workspace-info.mjs:40` + `workspace-context.mjs:25` + il default di VS Code. */
95
+ export const CARTELLE_ESCLUSE_PREDEFINITE = Object.freeze([
96
+ // controllo di versione
97
+ '.git', '.hg', '.svn',
98
+ // dipendenze scaricate
99
+ 'node_modules', 'bower_components', 'vendor', '.pnpm-store', '.yarn',
100
+ // prodotti della compilazione
101
+ 'dist', 'build', 'out', 'target', '.next', '.nuxt', '.svelte-kit', '.output',
102
+ // cache e prodotti degli strumenti
103
+ '.cache', '.turbo', '.parcel-cache', '.gradle', 'coverage', '.nyc_output',
104
+ '__pycache__', '.pytest_cache', '.mypy_cache', '.ruff_cache', '.tox',
105
+ // ambienti virtuali
106
+ '.venv', 'venv', 'virtualenv',
107
+ ]);
108
+
109
+ /** Vedi decisione (2): denylist corta e sostituibile, mai una allowlist. */
110
+ export const ESTENSIONI_ESCLUSE_PREDEFINITE = Object.freeze([
111
+ // immagini e media (`.svg` NO: è testo, e dice com'è fatta la UI)
112
+ '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.avif', '.tiff', '.psd',
113
+ '.mp3', '.wav', '.ogg', '.flac', '.m4a', '.aac', '.mp4', '.mov', '.avi', '.mkv', '.webm',
114
+ // archivi e pacchetti
115
+ '.zip', '.gz', '.tgz', '.bz2', '.xz', '.7z', '.rar', '.tar', '.jar', '.war', '.apk', '.aab',
116
+ '.iso', '.dmg',
117
+ // eseguibili e oggetti
118
+ '.exe', '.dll', '.so', '.dylib', '.bin', '.o', '.a', '.lib', '.pdb', '.obj', '.class',
119
+ '.pyc', '.pyo', '.node', '.wasm',
120
+ // caratteri tipografici
121
+ '.woff', '.woff2', '.ttf', '.otf', '.eot',
122
+ // pesi dei modelli: questo repo ne scarica, e sono i file più grandi del disco
123
+ '.gguf', '.safetensors', '.onnx', '.pt', '.pth', '.npz', '.h5', '.ckpt',
124
+ // basi di dati e mappe sorgente generate
125
+ '.db', '.sqlite', '.sqlite3', '.mdb', '.map',
126
+ ]);
127
+
128
+ /** File senza estensione che nessuno ha scritto: li lascia il sistema operativo. */
129
+ const NOMI_ESCLUSI = new Set(['.ds_store', 'thumbs.db', 'desktop.ini']);
130
+
131
+ export const TETTO_FILE_PREDEFINITO = 1500;
132
+ export const PROFONDITA_PREDEFINITA = 8;
133
+
134
+ /**
135
+ * Ordine totale, stabile e indipendente dall'ambiente (decisione 3).
136
+ * Si confronta segmento per segmento — così un sottoalbero resta unito invece di essere
137
+ * spezzato dal separatore — e a parità di cartella i file vengono prima delle sottocartelle:
138
+ * chi legge vede cosa c'è QUI prima di scendere.
139
+ * ⛔ Mai `localeCompare`: dipende da ICU, e un ordine che cambia da una macchina all'altra
140
+ * rompe il prefisso della cache.
141
+ */
142
+ export function confrontaPercorsi(a, b) {
143
+ const sa = String(a).split('/');
144
+ const sb = String(b).split('/');
145
+ const comuni = Math.min(sa.length, sb.length);
146
+ for (let i = 0; i < comuni; i += 1) {
147
+ if (sa[i] === sb[i]) continue;
148
+ const ultimoA = i === sa.length - 1;
149
+ const ultimoB = i === sb.length - 1;
150
+ if (ultimoA !== ultimoB) return ultimoA ? -1 : 1;
151
+ return sa[i] < sb[i] ? -1 : 1;
152
+ }
153
+ return sa.length - sb.length;
154
+ }
155
+
156
+ function insiemeMinuscolo(valore, predefinito) {
157
+ const grezzo = valore === undefined || valore === null ? predefinito : valore;
158
+ const voci = grezzo instanceof Set ? [...grezzo] : Array.isArray(grezzo) ? grezzo : [grezzo];
159
+ return new Set(voci.filter((v) => typeof v === 'string').map((v) => v.toLowerCase()));
160
+ }
161
+
162
+ function estensioneDi(nome) {
163
+ const punto = nome.lastIndexOf('.');
164
+ // `.gitignore` non ha estensione: è un nome che comincia col punto
165
+ return punto > 0 ? nome.slice(punto).toLowerCase() : '';
166
+ }
167
+
168
+ function ordinaVoci(voci) {
169
+ return [...voci].sort((a, b) => (a.name === b.name ? 0 : a.name < b.name ? -1 : 1));
170
+ }
171
+
172
+ /**
173
+ * Cammina l'albero e restituisce i percorsi dei file, potati e ordinati.
174
+ *
175
+ * @param {object} input
176
+ * @param {string} input.radice — cartella da cui partire (assoluta o relativa a chi chiama)
177
+ * @param {number} [input.profonditaMax=8] — quanti segmenti può avere il percorso di un file:
178
+ * `README.md` è 1, `src/a.mjs` è 2, `a/b/c/d/e/f.txt` è 6. Con 2 si riproduce esattamente la
179
+ * vista di oggi, quella che non vede nessuno dei 106 percorsi del corpus.
180
+ * @param {number} [input.tettoFile=1500] — vedi decisione (4)
181
+ * @param {string[]|Set<string>} [input.escludiCartelle] — nomi di cartella da non aprire
182
+ * @param {string[]|Set<string>} [input.escludiEstensioni] — estensioni da non elencare
183
+ * @param {object} [input.fs] — `{readdir, realpath, stat}`; si inietta, come negli altri moduli
184
+ * @param {(percorsoRelativo: string, info?: {cartella: boolean}) => boolean} [input.filtro]
185
+ * true = tieni. Riceve SIA i file SIA le cartelle (una cartella negata non si apre: è così
186
+ * che un `.gitignore` pota un sottoalbero intero senza camminarlo). Il secondo argomento
187
+ * dice quale dei due è; una funzione a un solo argomento continua a funzionare.
188
+ * @returns {Promise<{percorsi: string[], troncato: boolean, cartelleSaltate: number,
189
+ * fileEsclusi: number, dettaglio: object}>}
190
+ */
191
+ export async function costruisciElencoProfondo({
192
+ radice,
193
+ profonditaMax = PROFONDITA_PREDEFINITA,
194
+ tettoFile = TETTO_FILE_PREDEFINITO,
195
+ escludiCartelle,
196
+ escludiEstensioni,
197
+ fs,
198
+ filtro,
199
+ } = {}) {
200
+ if (typeof radice !== 'string' || radice.length === 0 || radice.includes('\0')) {
201
+ throw new ElencoProfondoError('La cartella di partenza non è valida');
202
+ }
203
+ if (filtro !== undefined && typeof filtro !== 'function') {
204
+ throw new ElencoProfondoError('Il filtro deve essere una funzione');
205
+ }
206
+
207
+ const disco = { readdir, realpath, stat, ...(fs ?? {}) };
208
+ const cartelleEscluse = insiemeMinuscolo(escludiCartelle, CARTELLE_ESCLUSE_PREDEFINITE);
209
+ const estensioniEscluse = insiemeMinuscolo(escludiEstensioni, ESTENSIONI_ESCLUSE_PREDEFINITE);
210
+ const tetto = Number.isFinite(tettoFile) ? Math.max(0, Math.trunc(tettoFile)) : TETTO_FILE_PREDEFINITO;
211
+ const profondita = Number.isFinite(profonditaMax) ? Math.trunc(profonditaMax) : PROFONDITA_PREDEFINITA;
212
+ const tieni = (percorso, cartella) => (filtro ? filtro(percorso, { cartella }) !== false : true);
213
+
214
+ const percorsi = [];
215
+ const dettaglio = { perNome: 0, perProfondita: 0, perFiltro: 0, perCiclo: 0, illeggibili: 0, perEstensione: 0, fileFiltrati: 0 };
216
+ let troncato = false;
217
+ let fileEsclusi = 0;
218
+
219
+ /*
220
+ * ⛔ Anello simbolico: seguire un link a una cartella genitore fa camminare per sempre — è un
221
+ * modo noto di piantare un processo (nodejs/node #51749 sulla `readdir` ricorsiva; la cura
222
+ * standard è confrontare i percorsi REALI, non quelli costruiti, letto il 10/09/2026). Qui si
223
+ * paga una `realpath` per cartella: una syscall contro una `readdir` intera, e l'unica
224
+ * alternativa — fidarsi del percorso costruito — non regge un link.
225
+ *
226
+ * ⛔ Ma si guarda la CATENA DEGLI ANTENATI, non un insieme globale di «già viste». La prima
227
+ * versione teneva un insieme globale, e la prova `ELENCO-ANELLO-05` l'ha bocciata: con due
228
+ * strade diverse verso la stessa cartella (un collegamento `scorciatoia` → `vera`) vinceva
229
+ * quella incontrata per prima in ordine alfabetico, e `vera/dentro.txt` — un percorso che sul
230
+ * disco funziona — spariva dall'elenco. Cioè esattamente la bugia che questo modulo esiste per
231
+ * non dire, prodotta dalla guardia contro i cicli. Un ciclo è tornare su un ANTENATO del
232
+ * proprio cammino; due rami che finiscono nello stesso posto non lo sono, e il cammino resta
233
+ * finito. Il prezzo è che una cartella raggiungibile per due strade si elenca due volte: è la
234
+ * verità sul disco, e costa qualche riga invece di un file invisibile.
235
+ */
236
+ const radiceReale = await disco.realpath(radice).catch(() => radice);
237
+
238
+ const coda = [{ assoluto: radice, relativo: '', livello: 0, antenati: [radiceReale] }];
239
+ while (coda.length > 0 && !troncato) {
240
+ const corrente = coda.shift();
241
+ let voci;
242
+ try {
243
+ voci = await disco.readdir(corrente.assoluto, { withFileTypes: true });
244
+ } catch {
245
+ // Una cartella che non si lascia leggere (permessi, sparita nel frattempo) si salta e si
246
+ // CONTA: tacere qui vorrebbe dire dichiarare completo un elenco che non lo è.
247
+ dettaglio.illeggibili += 1;
248
+ continue;
249
+ }
250
+
251
+ for (const voce of ordinaVoci(voci)) {
252
+ const nome = typeof voce === 'string' ? voce : voce.name;
253
+ const relativo = corrente.relativo ? `${corrente.relativo}/${nome}` : nome;
254
+ const assoluto = join(corrente.assoluto, nome);
255
+
256
+ let eCartella = Boolean(voce.isDirectory?.());
257
+ let eFile = Boolean(voce.isFile?.());
258
+ if (voce.isSymbolicLink?.()) {
259
+ // Un link non dice da solo cosa c'è dall'altra parte: si guarda.
260
+ const informazioni = await disco.stat(assoluto).catch(() => null);
261
+ if (!informazioni) { dettaglio.illeggibili += 1; continue; } // link rotto: onesto, non fatale
262
+ eCartella = Boolean(informazioni.isDirectory?.());
263
+ eFile = Boolean(informazioni.isFile?.());
264
+ }
265
+
266
+ if (eCartella) {
267
+ if (cartelleEscluse.has(nome.toLowerCase())) { dettaglio.perNome += 1; continue; }
268
+ if (!tieni(relativo, true)) { dettaglio.perFiltro += 1; continue; }
269
+ // Si apre solo se i file che contiene starebbero dentro la profondità chiesta.
270
+ if (corrente.livello + 2 > profondita) { dettaglio.perProfondita += 1; continue; }
271
+ const reale = await disco.realpath(assoluto).catch(() => assoluto);
272
+ if (corrente.antenati.includes(reale)) { dettaglio.perCiclo += 1; continue; }
273
+ coda.push({ assoluto, relativo, livello: corrente.livello + 1, antenati: [...corrente.antenati, reale] });
274
+ continue;
275
+ }
276
+
277
+ if (!eFile) continue; // socket, fifo, dispositivo: non è un file da nominare a nessuno
278
+
279
+ if (NOMI_ESCLUSI.has(nome.toLowerCase())) { fileEsclusi += 1; dettaglio.perEstensione += 1; continue; }
280
+ if (estensioniEscluse.has(estensioneDi(nome))) { fileEsclusi += 1; dettaglio.perEstensione += 1; continue; }
281
+ if (!tieni(relativo, false)) { fileEsclusi += 1; dettaglio.fileFiltrati += 1; continue; }
282
+
283
+ if (percorsi.length >= tetto) { troncato = true; break; }
284
+ percorsi.push(relativo);
285
+ }
286
+ }
287
+
288
+ percorsi.sort(confrontaPercorsi);
289
+ const cartelleSaltate = dettaglio.perNome + dettaglio.perProfondita + dettaglio.perFiltro
290
+ + dettaglio.perCiclo + dettaglio.illeggibili;
291
+ return { percorsi, troncato, cartelleSaltate, fileEsclusi, dettaglio };
292
+ }
293
+
294
+ /**
295
+ * Il testo da mettere nel prompt, con la sua intestazione.
296
+ *
297
+ * ⛔ Se il tetto ha morso, lo DICE — in testa e in coda. In testa perché sia la prima cosa
298
+ * letta; in coda perché dopo millecinquecento righe la prima riga è lontana, e l'ultima cosa
299
+ * letta prima della domanda è quella che pesa. Un elenco tagliato che sembra completo è
300
+ * peggio di nessun elenco: porta a concludere che un file non esiste.
301
+ *
302
+ * ⛔ Nell'intestazione va il NOME della cartella, mai il percorso intero: un percorso assoluto
303
+ * contiene il nome della persona e questo testo finisce dentro un prompt, cioè fuori dalla
304
+ * macchina. È la lezione `cancello-4-non-guardava-tutto-mobile` — un percorso personale
305
+ * pubblicato in chiaro perché nessuno aveva guardato dove finiva.
306
+ *
307
+ * @param {string[]} percorsi
308
+ * @param {{troncato?: boolean, radice?: string, fileEsclusi?: number}} [stato] — accetta
309
+ * direttamente il risultato di `costruisciElencoProfondo`
310
+ * @returns {string}
311
+ */
312
+ export function testoElenco(percorsi = [], { troncato = false, radice = '', fileEsclusi = 0 } = {}) {
313
+ const elenco = Array.isArray(percorsi) ? percorsi.filter((p) => typeof p === 'string') : [];
314
+ const nome = basename(String(radice ?? '').replace(/[\\/]+$/, '')) || 'la cartella di lavoro';
315
+
316
+ if (elenco.length === 0) {
317
+ return troncato
318
+ ? `Nessun file da mostrare per «${nome}»: l'elenco si è fermato prima di raccoglierne uno.`
319
+ : `Nessun file da mostrare per «${nome}».`;
320
+ }
321
+
322
+ const righe = [];
323
+ if (troncato) {
324
+ righe.push(`⚠ ELENCO INCOMPLETO — mi sono fermato a ${elenco.length} percorsi, l'albero ne ha altri.`);
325
+ }
326
+ righe.push(`File di «${nome}» — ${elenco.length} percorsi, dalla cartella principale in giù.`);
327
+ if (fileEsclusi > 0) {
328
+ righe.push(`(${fileEsclusi} file non compaiono: immagini, archivi, pesi dei modelli e simili.)`);
329
+ }
330
+ righe.push('');
331
+ righe.push(...elenco);
332
+ if (troncato) {
333
+ righe.push('');
334
+ righe.push('⚠ Fine di un elenco INCOMPLETO: se un file non compare qui sopra non vuol dire che non esista — cercalo prima di dire che manca.');
335
+ }
336
+ return righe.join('\n');
337
+ }
@@ -0,0 +1,113 @@
1
+ /**
2
+ * ⭐⭐⭐ LE FAVICON DELLE FONTI — prese dal server, una volta, e tenute su disco.
3
+ *
4
+ * Owner, 10/09: «i favicon dei siti delle fonti non ci sono (il mobile l'ha già fatto)».
5
+ *
6
+ * ## ⛔ Perché non si scaricano dal browser, ed è il mobile stesso a dirlo
7
+ *
8
+ * `mobile/src/composables/useTalosSourceCardIcons.ts`, letto oggi: «fetching a favicon at display
9
+ * time is a request to every site every time the surface is opened» — ed è la ragione per cui là la
10
+ * pillola mostrava solo le lettere finché non è esistito un archivio di card su disco. Aprire una
11
+ * conversazione di mesi fa non deve bussare a ogni sito che quella conversazione ha citato.
12
+ *
13
+ * Sul desktop quell'archivio non c'è. Ma c'è una cosa che il telefono non ha: **un server nostro**.
14
+ *
15
+ * ⇒ Il browser chiede l'icona SOLO a noi (`/api/v1/favicon?dominio=…`); il server la prende una
16
+ * volta e la tiene su disco. Da quel momento nessuno esce più. È l'architettura di SearXNG,
17
+ * verificata prima di scriverla (ricerca 10/09/2026, docs.searxng.org «Favicons»): «To protect the
18
+ * privacy of users, favicons are provided via a proxy… A cache has been developed to massively
19
+ * reduce both incoming and outgoing requests.»
20
+ *
21
+ * ⛔ E ciò che il server NON fa, per scelta: non segue redirect fuori dal dominio chiesto, non
22
+ * accetta percorsi arbitrari (solo un dominio), non serve niente che non sia un'immagine piccola.
23
+ * Un proxy che scarica quello che gli dici è una porta aperta, non una comodità.
24
+ */
25
+
26
+ import { createHash } from 'node:crypto';
27
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
28
+ import { join } from 'node:path';
29
+
30
+ /** Un'icona è piccola per definizione: oltre questo non è un'icona, è qualcos'altro. */
31
+ export const MAX_BYTE_ICONA = 100 * 1024;
32
+
33
+ /** Quanto a lungo vale un'icona presa. Un mese: i siti cambiano logo di rado. */
34
+ export const VALIDITA_MS = 30 * 24 * 60 * 60 * 1000;
35
+
36
+ const TIMEOUT_MS = 6000;
37
+
38
+ /**
39
+ * ⛔ Solo un dominio, e con una forma dichiarata: niente porte, niente percorsi, niente utente:password.
40
+ * Ciò che non combacia non viene «ripulito» — viene rifiutato. Una sanificazione permissiva è il
41
+ * modo classico di trasformare un proxy in una porta aperta.
42
+ */
43
+ const DOMINIO_VALIDO = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)+$/i;
44
+
45
+ export function dominioAmmesso(raw) {
46
+ const d = String(raw ?? '').trim().toLowerCase();
47
+ if (!d || d.length > 253 || !DOMINIO_VALIDO.test(d)) return null;
48
+ /* ⛔ Niente rete interna: un proxy che accetta «localhost» fa bussare il server a sé stesso. */
49
+ if (/^(localhost|127\.|10\.|192\.168\.|169\.254\.|0\.)/.test(d) || d.endsWith('.local') || d.endsWith('.internal')) return null;
50
+ return d;
51
+ }
52
+
53
+ const TIPI = new Map([
54
+ ['image/png', '.png'], ['image/x-icon', '.ico'], ['image/vnd.microsoft.icon', '.ico'],
55
+ ['image/svg+xml', '.svg'], ['image/jpeg', '.jpg'], ['image/webp', '.webp'], ['image/gif', '.gif'],
56
+ ]);
57
+
58
+ const nomeDiCache = (dominio) => `${createHash('sha256').update(dominio).digest('hex').slice(0, 24)}.json`;
59
+
60
+ /**
61
+ * Prende l'icona di un dominio: prima dalla cache su disco, poi dalla rete, una volta sola.
62
+ *
63
+ * @returns {Promise<{tipo: string, byte: Buffer, daCache: boolean}|null>} `null` se non c'è
64
+ */
65
+ export async function iconaDelDominio(dominioGrezzo, { cartellaCache, fetchFn = globalThis.fetch, adesso = () => Date.now() } = {}) {
66
+ const dominio = dominioAmmesso(dominioGrezzo);
67
+ if (!dominio || !cartellaCache) return null;
68
+
69
+ const file = join(cartellaCache, nomeDiCache(dominio));
70
+ try {
71
+ const salvato = JSON.parse(await readFile(file, 'utf8'));
72
+ if (salvato && adesso() - salvato.quando < VALIDITA_MS) {
73
+ /* ⛔ Anche un «non ce l'ha» si tiene: senza, ogni apertura riprova per ogni sito senza icona. */
74
+ if (!salvato.base64) return null;
75
+ return { tipo: salvato.tipo, byte: Buffer.from(salvato.base64, 'base64'), daCache: true };
76
+ }
77
+ } catch { /* niente in cache: si prende */ }
78
+
79
+ let trovata = null;
80
+ try {
81
+ const controller = new AbortController();
82
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
83
+ try {
84
+ const risposta = await fetchFn(`https://${dominio}/favicon.ico`, {
85
+ signal: controller.signal,
86
+ /* ⛔ `redirect: 'error'`: un redirect porterebbe altrove, e «altrove» è esattamente ciò che un
87
+ proxy non deve seguire per conto di chi lo chiama. */
88
+ redirect: 'error',
89
+ headers: { accept: 'image/*' },
90
+ });
91
+ if (risposta.ok) {
92
+ const tipo = String(risposta.headers?.get?.('content-type') ?? '').split(';')[0].trim().toLowerCase();
93
+ if (TIPI.has(tipo)) {
94
+ const byte = Buffer.from(await risposta.arrayBuffer());
95
+ if (byte.length > 0 && byte.length <= MAX_BYTE_ICONA) trovata = { tipo, byte };
96
+ }
97
+ }
98
+ } finally {
99
+ clearTimeout(timer);
100
+ }
101
+ } catch { /* il sito non risponde, non ha l'icona, o il redirect esce: nessuna icona, e si ricorda */ }
102
+
103
+ try {
104
+ await mkdir(cartellaCache, { recursive: true });
105
+ await writeFile(file, JSON.stringify({
106
+ quando: adesso(),
107
+ tipo: trovata?.tipo ?? null,
108
+ base64: trovata ? trovata.byte.toString('base64') : null,
109
+ }), 'utf8');
110
+ } catch { /* cache non scrivibile: si serve lo stesso, si riproverà */ }
111
+
112
+ return trovata ? { ...trovata, daCache: false } : null;
113
+ }