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,142 @@
1
+ import { talosResearchRegistrableHost } from './independence.mjs'
2
+
3
+ /*
4
+ * PORTO FEDELE di AVM/mobile/src/lib/research/researchCitationExport.ts (118 righe, 11/09/2026).
5
+ *
6
+ * ⛔⛔ EXPORT-06 — le fonti in un formato che un gestore di bibliografie legge.
7
+ *
8
+ * Il rapporto esce già in Markdown e PDF, che sono per una persona. Chi fa un
9
+ * lavoro serio con delle fonti però le mette in Zotero, Mendeley o EndNote, e
10
+ * quei programmi parlano **BibTeX** e **RIS**. Senza, ogni riferimento va
11
+ * ricopiato a mano — ed è esattamente il punto in cui una bibliografia si
12
+ * sporca.
13
+ *
14
+ * ## ⛔ Cosa NON esce, e perché è una decisione di privacy
15
+ *
16
+ * Una citazione descrive una PAGINA, non chi l'ha letta. Da qui non escono la
17
+ * query che l'ha trovata, il modello che l'ha giudicata, l'identificativo della
18
+ * ricerca né la chat da cui viene. Un file di bibliografia finisce in una
19
+ * cartella condivisa, in un allegato, in un repository: è il posto meno
20
+ * controllato in cui un dato personale possa arrivare.
21
+ *
22
+ * ⇒ La forma in ingresso dichiara i quattro campi che servono e **basta**: ciò
23
+ * che il chiamante passa in più non viene nemmeno letto.
24
+ */
25
+
26
+ /**
27
+ * @typedef {object} TalosResearchCitation
28
+ * @property {string} url
29
+ * @property {string} title
30
+ * @property {string | null} publishedAt ISO, o `null` quando la pagina non la dichiara. Non si inventa.
31
+ * @property {string} accessedAt Quando l'abbiamo letta: per una pagina web è il campo che conta di più.
32
+ */
33
+
34
+ /**
35
+ * L'anno, solo se la data c'è ed è leggibile.
36
+ * @param {string | null} iso
37
+ * @returns {string | null}
38
+ */
39
+ function anno(iso) {
40
+ if (!iso) return null
41
+ const trovato = /^(\d{4})/.exec(iso.trim())
42
+ return trovato ? trovato[1] : null
43
+ }
44
+
45
+ /**
46
+ * ⛔ Le graffe e le barre rovesce nel titolo ROMPONO il file.
47
+ *
48
+ * In BibTeX una graffa apre un gruppo: un titolo che ne contiene una spaiata fa
49
+ * fallire il parsing dell'intera voce — e i gestori non lo dicono, saltano la
50
+ * voce. Si tolgono, invece di scappare: un titolo è testo da leggere, non
51
+ * codice, e nessun lettore ha bisogno delle graffe originali.
52
+ *
53
+ * @param {string} testo
54
+ * @returns {string}
55
+ */
56
+ function bibtexSicuro(testo) {
57
+ return testo
58
+ .replace(/[{}\\]/g, '')
59
+ .replace(/\s+/g, ' ')
60
+ .trim()
61
+ }
62
+
63
+ /**
64
+ * In RIS ogni riga è un campo: un a capo dentro un valore spezza il record.
65
+ * @param {string} testo
66
+ * @returns {string}
67
+ */
68
+ function risSicuro(testo) {
69
+ return testo.replace(/\s+/g, ' ').trim()
70
+ }
71
+
72
+ /**
73
+ * La chiave della voce: dominio + anno, e un suffisso quando serve.
74
+ *
75
+ * ⛔ Due fonti dello stesso dominio nello stesso anno darebbero la stessa
76
+ * chiave, e un file con due chiavi uguali non è valido: il gestore ne butta una
77
+ * **in silenzio**. Chi esporta se ne accorge quando gli manca una citazione in
78
+ * fondo a un lavoro finito.
79
+ *
80
+ * @param {readonly TalosResearchCitation[]} citations
81
+ * @returns {string[]}
82
+ */
83
+ function chiaviDistinte(citations) {
84
+ /** @type {Map<string, number>} */
85
+ const viste = new Map()
86
+ return citations.map((citation) => {
87
+ const host = talosResearchRegistrableHost(citation.url) ?? 'fonte'
88
+ const base = `${host.replace(/[^a-z0-9]/gi, '')}${anno(citation.publishedAt) ?? ''}`
89
+ const quante = viste.get(base) ?? 0
90
+ viste.set(base, quante + 1)
91
+ // La prima resta pulita: `example2026`, poi `example2026b`, `example2026c`.
92
+ return quante === 0 ? base : `${base}${String.fromCharCode(97 + quante)}`
93
+ })
94
+ }
95
+
96
+ /**
97
+ * @param {readonly TalosResearchCitation[]} citations
98
+ * @returns {string}
99
+ */
100
+ export function talosResearchBibtex(citations) {
101
+ if (citations.length === 0) return ''
102
+ const chiavi = chiaviDistinte(citations)
103
+
104
+ return citations.map((citation, indice) => {
105
+ const righe = [
106
+ ` title = {${bibtexSicuro(citation.title)}}`,
107
+ ` url = {${citation.url.trim()}}`,
108
+ ` urldate = {${citation.accessedAt.trim()}}`,
109
+ ]
110
+ const quando = anno(citation.publishedAt)
111
+ // ⛔ Un anno inventato è peggio di un anno mancante: chi cita si fida.
112
+ if (quando) righe.splice(1, 0, ` year = {${quando}}`)
113
+ // `@misc` è il tipo per una risorsa online: `@article` prometterebbe una
114
+ // rivista, e una pagina web non lo è.
115
+ return `@misc{${chiavi[indice]},\n${righe.join(',\n')},\n}`
116
+ }).join('\n\n')
117
+ }
118
+
119
+ /**
120
+ * @param {readonly TalosResearchCitation[]} citations
121
+ * @returns {string}
122
+ */
123
+ export function talosResearchRis(citations) {
124
+ if (citations.length === 0) return ''
125
+
126
+ return citations.map((citation) => {
127
+ const righe = [
128
+ // ELEC = risorsa elettronica, il tipo giusto per una pagina web.
129
+ 'TY - ELEC',
130
+ `TI - ${risSicuro(citation.title)}`,
131
+ `UR - ${citation.url.trim()}`,
132
+ ]
133
+ const quando = anno(citation.publishedAt)
134
+ if (quando) righe.push(`PY - ${quando}`)
135
+ // Y2 è la data di consultazione, e RIS la vuole con le barre.
136
+ righe.push(`Y2 - ${citation.accessedAt.trim().replace(/-/g, '/')}`)
137
+ // ⛔ `ER` chiude il record e la sua riga finisce con uno spazio: è la
138
+ // forma che i gestori si aspettano, e senza non riconoscono la fine.
139
+ righe.push('ER - ')
140
+ return righe.join('\n')
141
+ }).join('\n\n')
142
+ }
@@ -0,0 +1,275 @@
1
+ /*
2
+ * PORTO FEDELE di AVM/mobile/src/lib/research/researchCollector.ts (155 righe, 11/09/2026).
3
+ *
4
+ * Raccogliere quanto vale una linea d'indagine, e tenerlo.
5
+ *
6
+ * R-3. La raccolta in sé non ha niente di notevole — cerca, leggi i primi
7
+ * risultati, scrivi cos'hanno detto. La decisione che conta è che il TESTO
8
+ * ESTRATTO SI TIENE, ed è l'unica cosa in questa fase che nessun altro fa.
9
+ *
10
+ * Ogni prodotto di ricerca sul mercato conserva link. Un link è una promessa su
11
+ * una pagina, e le pagine marciscono: vengono modificate, messe dietro un
12
+ * paywall, spostate, o semplicemente spente. Un dossier fatto di link degrada
13
+ * in silenzio — le citazioni sembrano a posto finché qualcuno non ne clicca una
14
+ * — e non si può ricontrollare affatto, perché la cosa che citava non c'è più.
15
+ * Tenere il passaggio rende il dossier interrogabile un anno dopo, rende
16
+ * possibile la ri-verifica di R12, ed è impossibile da copiare per chiunque il
17
+ * cui prodotto sia un elenco di URL.
18
+ *
19
+ * L'altra regola qui è quella che questo progetto ha imparato a caro prezzo:
20
+ * una fonte che non si è potuta leggere si REGISTRA, non si butta mai. Una
21
+ * raccolta che torna in silenzio con quattro fonti invece di sei sembra un tema
22
+ * povero invece di uno scaricamento rotto, e la differenza è tutto ciò che
23
+ * decide se la risposta si possa credere.
24
+ *
25
+ * ⛔ L'I/O entra da `deps` (`search`, `read`): i test usano finti, mai la rete.
26
+ *
27
+ * ## L6 (11/09/2026) — la cache e il budget entrano dalla STESSA porta
28
+ *
29
+ * Due cose nuove, e nessuna delle due è logica di raccolta: sono I/O, quindi
30
+ * arrivano da `deps` come `search` e `read`, e senza di loro questo file si
31
+ * comporta ESATTAMENTE come il porto fedele dal mobile.
32
+ *
33
+ * - `deps.cache` (`fetch-cache.mjs`): le linee d'indagine di una ricerca
34
+ * profonda partono dalla stessa domanda e aprono le stesse pagine. Con la
35
+ * cache la pagina si apre una volta per corsa; senza, ogni ramo ripaga.
36
+ * - `deps.budget` (`page-budget.mjs`): quanto di una pagina vede il modello,
37
+ * con il resto CONSERVATO e un riferimento leggibile.
38
+ *
39
+ * ⛔ Il testo INTERO resta in `source.text`, ed è deliberato: è il dossier, ed è
40
+ * ciò che `talosResearchLocate` (`verification.mjs:201`) legge per decidere se
41
+ * un passaggio citato esiste davvero. Cercare nella finestra farebbe risultare
42
+ * INVENTATA una citazione onesta caduta nel mezzo tagliato — il verso peggiore.
43
+ * Al modello va `source.window`, che è una cosa diversa e si chiama diversamente.
44
+ */
45
+
46
+ /**
47
+ * @typedef {import('./run.mjs').TalosResearchBranch} TalosResearchBranch
48
+ * @typedef {import('./run.mjs').TalosResearchSpend} TalosResearchSpend
49
+ * @typedef {import('./page-budget.mjs').TalosResearchPageReference} TalosResearchPageReference
50
+ */
51
+
52
+ import { TALOS_RESEARCH_PAGE_BUDGET, TALOS_RESEARCH_HEAD_SHARE, talosResearchPageBudget } from './page-budget.mjs';
53
+
54
+ /**
55
+ * @typedef {object} TalosResearchSource
56
+ * @property {string} url
57
+ * @property {string} title
58
+ * @property {string | null} publishedAt Quello che dice la FONTE, o null. Mai la data di oggi, mai un'ipotesi.
59
+ * @property {string} text
60
+ * Il testo leggibile, com'era il giorno in cui è stato letto. Questo È il
61
+ * dossier. Tutto quello che viene dopo — la sintesi, il controllo delle
62
+ * citazioni, la ri-verifica un anno dopo — legge questo e non la rete.
63
+ * @property {'page' | 'snippet'} obtained
64
+ * Quanta parte della fonte è davvero qui. Uno snippet è quello che il motore
65
+ * di ricerca ha mostrato; una pagina è quello che la pagina ha detto.
66
+ * Dichiarato perché un'affermazione sostenuta solo da uno snippet è una prova
67
+ * più debole, e nasconderlo farebbe sembrare uguali le due cose.
68
+ * @property {string} [window]
69
+ * L6. Quanto di `text` va al modello: testa + marcatore + coda quando la
70
+ * pagina supera il budget, `text` identico quando non lo supera o quando
71
+ * nessun budget è configurato. ⛔ Mai la fonte della verità: quella è `text`.
72
+ * @property {number} [omitted] Caratteri che la finestra non mostra. Contati, mai stimati.
73
+ * @property {boolean} [fromCache]
74
+ * L6. Questa pagina NON è stata riaperta: l'aveva già pagata un altro ramo
75
+ * della stessa corsa. Il registro la conta a parte da quelle aperte davvero —
76
+ * «9 pagine, 4 servite dalla cache» dice una cosa che «9 pagine» non dice.
77
+ */
78
+
79
+ /**
80
+ * @typedef {object} TalosResearchUnreachable
81
+ * @property {string} url
82
+ * @property {string} reason
83
+ */
84
+
85
+ /**
86
+ * @typedef {object} TalosResearchCollection
87
+ * @property {string} branchId
88
+ * @property {string} query
89
+ * @property {readonly TalosResearchSource[]} sources
90
+ * @property {readonly TalosResearchUnreachable[]} unreachable Mai vuota in silenzio: quello che non si è potuto leggere ha un nome.
91
+ * @property {TalosResearchSpend} spend
92
+ * MISURATA, non stimata. Il piano indovina; il giro conta.
93
+ * ⛔ L6: è la SPESA, quindi ciò che la cache ha servito NON è qui dentro —
94
+ * una pagina non riaperta non è stata pagata, e contarla renderebbe invisibile
95
+ * il guadagno proprio nel numero fatto per mostrare il costo. Quanto è arrivato
96
+ * gratis sta in `fromCache`, così chi legge ha tutte e due le cifre e nessuna
97
+ * delle due mente.
98
+ * @property {{searches: number, pages: number}} fromCache
99
+ * L6. Quante ricerche e quante pagine sono arrivate dalla cache della corsa.
100
+ * `spend.pages + fromCache.pages` è il numero di fonti ottenute.
101
+ */
102
+
103
+ /**
104
+ * @typedef {object} TalosResearchCollectorDeps
105
+ * @property {(query: string, maxResults: number) => Promise<readonly {url:string, title:string, snippet:string, publishedAt:string|null}[]>} search
106
+ * @property {(url: string) => Promise<{title:string, text:string, publishedAt:string|null} | null>} read
107
+ * @property {import('./fetch-cache.mjs').TalosResearchFetchCache} [cache]
108
+ * L6. Se c'è, una ricerca o una pagina già viste in questa corsa non si
109
+ * ripagano. Se non c'è, questo file fa esattamente quello che faceva prima.
110
+ * @property {string} [provider]
111
+ * Chi risponde. Entra nella chiave: due fornitori non danno la stessa pagina,
112
+ * e servire l'una per l'altra sarebbe una bugia silenziosa.
113
+ * @property {object} [budget] L6. Se assente, nessuna finestra: `window === text`.
114
+ * @property {number} [budget.cap] Caratteri mostrati al modello. Predefinito `TALOS_RESEARCH_PAGE_BUDGET`.
115
+ * @property {number} [budget.headShare] Quota in testa. Predefinita `TALOS_RESEARCH_HEAD_SHARE`.
116
+ * @property {number} [budget.keep]
117
+ * Quanto testo si CONSERVA per fonte. Predefinito `MAX_CHARS_PER_SOURCE`
118
+ * (20.000, il tetto del telefono). Sul desktop il dossier sta su disco e non
119
+ * in un database solo: chi lo sa alza questo, e il budget resta sotto.
120
+ * @property {(url: string) => (TalosResearchPageReference | null)} [budget.reference]
121
+ * Dove il chiamante ha depositato il testo intero di quella pagina, e la
122
+ * chiamata esatta per sfogliarlo. ⛔ Non si inventa: chi non ha un deposito
123
+ * non passa niente e il marcatore tace invece di promettere un percorso.
124
+ */
125
+
126
+ /**
127
+ * Quanto si tiene di una pagina.
128
+ *
129
+ * Un articolo lungo sono decine di migliaia di caratteri e un telefono tiene
130
+ * l'intero dossier in un database solo. Si taglia qui invece che al momento
131
+ * della sintesi: quello che si tiene è quello che si potrà ri-verificare più
132
+ * tardi, quindi il confine appartiene a dove la prova è CONSERVATA e non a dove
133
+ * capita che venga letta.
134
+ */
135
+ const MAX_CHARS_PER_SOURCE = 20_000;
136
+
137
+ /** Quattro caratteri per token è la regola pratica consueta per la prosa. */
138
+ const CHARS_PER_TOKEN = 4;
139
+
140
+ /**
141
+ * @param {string} text
142
+ * @param {number} [tetto]
143
+ * @returns {string}
144
+ */
145
+ function trim(text, tetto = MAX_CHARS_PER_SOURCE) {
146
+ const squeezed = text.replace(/\s+/g, ' ').trim();
147
+ return squeezed.length > tetto ? squeezed.slice(0, tetto) : squeezed;
148
+ }
149
+
150
+ /**
151
+ * @param {TalosResearchCollectorDeps} deps
152
+ * @param {TalosResearchBranch} branch
153
+ * @returns {Promise<TalosResearchCollection>}
154
+ */
155
+ export async function talosResearchCollect(deps, branch) {
156
+ const wanted = Math.max(1, branch.estimate.pages);
157
+
158
+ /*
159
+ * ⛔ Senza cache questa è la funzione identità sul produttore: la porta è
160
+ * sempre la stessa, quindi non ci sono due percorsi da tenere allineati — e
161
+ * un ramo che esiste solo quando una dipendenza c'è è un ramo che nessun test
162
+ * predefinito attraversa.
163
+ */
164
+ const cache = deps.cache ?? null;
165
+ /**
166
+ * @template T
167
+ * @param {import('./fetch-cache.mjs').TalosResearchFetchDescriptor} descrittore
168
+ * @param {() => Promise<T>} produttore
169
+ * @returns {Promise<{value: T, fromCache: boolean}>}
170
+ */
171
+ const attorno = async (descrittore, produttore) => (
172
+ cache ? cache.around(descrittore, produttore) : { value: await produttore(), fromCache: false }
173
+ );
174
+
175
+ const conserva = Math.max(1, Math.trunc(deps.budget?.keep ?? MAX_CHARS_PER_SOURCE));
176
+ /** @param {string} testo @param {string} url */
177
+ const finestra = (testo, url) => {
178
+ if (!deps.budget) return { window: testo, omitted: 0 };
179
+ const tagliata = talosResearchPageBudget(testo, {
180
+ cap: deps.budget.cap ?? TALOS_RESEARCH_PAGE_BUDGET,
181
+ headShare: deps.budget.headShare ?? TALOS_RESEARCH_HEAD_SHARE,
182
+ reference: deps.budget.reference ? deps.budget.reference(url) : null,
183
+ });
184
+ return { window: tagliata.window, omitted: tagliata.omitted };
185
+ };
186
+
187
+ const ricerca = await attorno(
188
+ { kind: 'search', query: branch.question, limit: wanted, provider: deps.provider },
189
+ () => deps.search(branch.question, wanted),
190
+ );
191
+ const found = ricerca.value;
192
+
193
+ /** @type {TalosResearchSource[]} */
194
+ const sources = [];
195
+ /** @type {TalosResearchUnreachable[]} */
196
+ const unreachable = [];
197
+ let pages = 0;
198
+ let pagineDallaCache = 0;
199
+
200
+ for (const result of found) {
201
+ /** @type {{title:string, text:string, publishedAt:string|null} | null} */
202
+ let extracted = null;
203
+ let dallaCache = false;
204
+ try {
205
+ const letta = await attorno(
206
+ { kind: 'extract', url: result.url, provider: deps.provider },
207
+ () => deps.read(result.url),
208
+ );
209
+ extracted = letta.value;
210
+ dallaCache = letta.fromCache;
211
+ } catch (failure) {
212
+ unreachable.push({
213
+ url: result.url,
214
+ reason: failure instanceof Error ? failure.message : 'unknown',
215
+ });
216
+ }
217
+
218
+ if (extracted && extracted.text.trim().length > 0) {
219
+ if (dallaCache) pagineDallaCache += 1;
220
+ else pages += 1;
221
+ const testo = trim(extracted.text, conserva);
222
+ sources.push({
223
+ url: result.url,
224
+ title: extracted.title || result.title,
225
+ // La data della pagina vince su quella del motore di ricerca: una è
226
+ // l'editore che parla, l'altra è un indice che indovina.
227
+ publishedAt: extracted.publishedAt ?? result.publishedAt,
228
+ text: testo,
229
+ obtained: 'page',
230
+ ...finestra(testo, result.url),
231
+ fromCache: dallaCache,
232
+ });
233
+ continue;
234
+ }
235
+
236
+ // Si ripiega sullo snippet invece di buttare la fonte. Un risultato di
237
+ // ricerca che non si è potuto aprire dice comunque qualcosa, e dirlo con
238
+ // `obtained: 'snippet'` è onesto in un modo in cui il silenzio non è.
239
+ if (!extracted && unreachable.every((entry) => entry.url !== result.url)) {
240
+ unreachable.push({ url: result.url, reason: 'unreadable' });
241
+ }
242
+ if (result.snippet.trim().length > 0) {
243
+ const testo = trim(result.snippet, conserva);
244
+ sources.push({
245
+ url: result.url,
246
+ title: result.title,
247
+ publishedAt: result.publishedAt,
248
+ text: testo,
249
+ obtained: 'snippet',
250
+ ...finestra(testo, result.url),
251
+ fromCache: dallaCache,
252
+ });
253
+ }
254
+ }
255
+
256
+ /*
257
+ * ⛔ Si conta la FINESTRA, non il testo conservato: i token si pagano su ciò
258
+ * che arriva al modello, e conservare di più non costa un token. Senza budget
259
+ * la finestra È il testo, quindi questo numero è identico a prima.
260
+ */
261
+ const characters = sources.reduce((total, source) => total + (source.window ?? source.text).length, 0);
262
+ return {
263
+ branchId: branch.id,
264
+ query: branch.question,
265
+ sources,
266
+ unreachable,
267
+ spend: {
268
+ searches: ricerca.fromCache ? 0 : 1,
269
+ pages,
270
+ // Quello che è stato davvero assorbito, contato dal testo che è qui.
271
+ tokens: Math.ceil(characters / CHARS_PER_TOKEN),
272
+ },
273
+ fromCache: { searches: ricerca.fromCache ? 1 : 0, pages: pagineDallaCache },
274
+ };
275
+ }
@@ -0,0 +1,139 @@
1
+ /** BC-49, contratto v1. Fonti e decisioni nel rapporto del 12/09/2026.
2
+ * Checkpoint applicativi completi, mai frammenti di JSON del fornitore.
3
+ * Il tetto riguarda gli argomenti, non promette una durata del ragionamento.
4
+ */
5
+ import { createHash } from 'node:crypto';
6
+ import { resolve } from 'node:path';
7
+ import { percorsoGiornale, scriviRapporto } from '../research-store.mjs';
8
+
9
+ export const LIMITE_PARTE_RAPPORTO_BYTE = 4096;
10
+ const MASSIMO_PARTI = 512;
11
+ const code = new Map();
12
+ const impronta = testo => createHash('sha256').update(testo, 'utf8').digest('hex');
13
+ const ordinato = valore => Array.isArray(valore) ? valore.map(ordinato)
14
+ : valore && typeof valore === 'object' ? Object.fromEntries(Object.keys(valore).sort().map(k => [k, ordinato(valore[k])])) : valore;
15
+ const serializza = valore => JSON.stringify(ordinato(valore));
16
+
17
+ function controlla(contenuto) {
18
+ const { parte, testo, affermazioni, fonti } = contenuto;
19
+ if (!parte || !Number.isSafeInteger(parte.indice) || parte.indice < 1 || parte.indice > MASSIMO_PARTI || typeof parte.ultima !== 'boolean') {
20
+ return 'La parte richiede un indice intero da 1 a 512 e ultima vero o falso.';
21
+ }
22
+ if (typeof testo !== 'string' || !Array.isArray(affermazioni) || !Array.isArray(fonti)) return 'Ogni parte richiede testo e gli elenchi affermazioni e fonti, anche vuoti.';
23
+ if (affermazioni.some(a => !a || typeof a !== 'object' || Array.isArray(a) || typeof a.testo !== 'string' || !a.testo.trim()
24
+ || !((typeof a.fonte === 'string' && a.fonte.trim()) || Number.isSafeInteger(a.fonte)))) return 'Ogni affermazione richiede testo e fonte; usa il suo URL completo.';
25
+ if (fonti.some(f => !f || typeof f.url !== 'string' || !/^https?:\/\//i.test(f.url.trim()))) return 'Ogni fonte richiede un URL HTTP(S) completo.';
26
+ if (Buffer.byteLength(JSON.stringify(contenuto), 'utf8') > LIMITE_PARTE_RAPPORTO_BYTE) return `La parte supera ${LIMITE_PARTE_RAPPORTO_BYTE} byte: dividila prima di riprovare con lo stesso indice.`;
27
+ return null;
28
+ }
29
+
30
+ /** Replay rigoroso delle parti; gli altri eventi restano proprietà del motore L9/BC-44. */
31
+ export function rileggiPartiRapporto(eventi) {
32
+ const parti = [];
33
+ let conclusione = null;
34
+ for (const evento of eventi) {
35
+ if (evento?.kind === 'deposit_part') {
36
+ const contenuto = evento.contenuto;
37
+ if (!contenuto || controlla(contenuto) || evento.versione !== 1 || evento.indice !== contenuto.parte.indice
38
+ || evento.impronta !== impronta(serializza(contenuto))) throw new Error('Integrità delle parti non verificabile: impronta o contenuto del giornale non valido.');
39
+ const precedente = parti[evento.indice - 1];
40
+ if (precedente) {
41
+ if (precedente.impronta !== evento.impronta) throw new Error('Integrità del giornale: due contenuti diversi per la stessa parte.');
42
+ continue;
43
+ }
44
+ if (evento.indice !== parti.length + 1 || parti.at(-1)?.contenuto.parte.ultima || conclusione) throw new Error('Integrità del giornale: manca una parte oppure il deposito è già chiuso.');
45
+ parti.push(evento);
46
+ } else if (evento?.kind === 'deposit_finished') {
47
+ if (!parti.at(-1)?.contenuto.parte.ultima || evento.ultimaImpronta !== parti.at(-1).impronta
48
+ || !/^[a-f0-9]{64}$/.test(evento.impronta ?? '') || !evento.risultato?.ok) throw new Error('Integrità della chiusura del deposito non verificabile.');
49
+ conclusione = evento;
50
+ }
51
+ }
52
+ return { parti, conclusione, prossimaParte: parti.length + 1 };
53
+ }
54
+
55
+ function assembla(parti) {
56
+ return {
57
+ testo: parti.map(p => p.contenuto.testo).join(''),
58
+ affermazioni: parti.flatMap(p => p.contenuto.affermazioni),
59
+ fonti: parti.flatMap(p => p.contenuto.fonti),
60
+ };
61
+ }
62
+
63
+ /** Un solo scrittore per ricerca nel processo che possiede la corsa; conferma dopo fsync. */
64
+ export async function depositaParteRapporto(argomenti, { leggiGiornaleFn, accodaEventoFn, leggiRapportoFn, valida, finalizza, clock }) {
65
+ const { cartella, id, testo, affermazioni, fonti, parte, byteArgomenti } = argomenti;
66
+ const contenuto = structuredClone({ testo, affermazioni, fonti, parte });
67
+ const motivo = controlla(contenuto);
68
+ if (motivo) return { ok: false, motivo };
69
+ if (byteArgomenti !== undefined && (!Number.isSafeInteger(byteArgomenti) || byteArgomenti > LIMITE_PARTE_RAPPORTO_BYTE)) {
70
+ return { ok: false, motivo: `Gli argomenti generati superano ${LIMITE_PARTE_RAPPORTO_BYTE} byte. Dividi testo e prove; riprova lo stesso indice.` };
71
+ }
72
+ const chiave = `${resolve(cartella)}\0${id}`;
73
+ const precedente = code.get(chiave) ?? Promise.resolve();
74
+ const corrente = precedente.catch(() => {}).then(async () => {
75
+ const { eventi } = await leggiGiornaleFn({ cartella, id, rigoroso: true });
76
+ const stato = rileggiPartiRapporto(eventi);
77
+ const hash = impronta(serializza(contenuto));
78
+ const registrata = stato.parti[parte.indice - 1];
79
+ if (registrata && registrata.impronta !== hash) return { ok: false, motivo: `La parte ${parte.indice} è già registrata con un altro contenuto. Non riscriverla; prossima parte: ${stato.prossimaParte}.` };
80
+ if (!registrata && (parte.indice !== stato.prossimaParte || stato.parti.at(-1)?.contenuto.parte.ultima)) {
81
+ return { ok: false, motivo: `Ordine non valido: prossima parte ${stato.prossimaParte}. Una chiusura già registrata si ritenta identica.` };
82
+ }
83
+ const evento = registrata ?? {
84
+ kind: 'deposit_part', versione: 1, at: clock().toISOString(), indice: parte.indice,
85
+ impronta: hash, byte: byteArgomenti ?? Buffer.byteLength(JSON.stringify(contenuto), 'utf8'), contenuto,
86
+ };
87
+ const tutte = registrata ? stato.parti : [...stato.parti, evento];
88
+ const unito = parte.ultima ? assembla(tutte) : null;
89
+ if (unito) {
90
+ const controllo = valida(unito);
91
+ if (!controllo.ok) return controllo;
92
+ }
93
+ if (!registrata) await accodaEventoFn({ cartella, id, evento, separaRiga: true });
94
+ if (!parte.ultima) return {
95
+ ok: true, parziale: true, prossimaParte: tutte.length + 1,
96
+ messaggio: `Parte ${parte.indice} registrata (${evento.byte} byte). Prossima parte: ${tutte.length + 1}. Attendi questa conferma prima di proseguire; il rapporto sarà pronto soltanto dopo l'ultima parte.`,
97
+ contenutoRegistrato: `\n${JSON.stringify(evento)}\n`, percorsoRegistrato: percorsoGiornale(cartella, id),
98
+ };
99
+ if (stato.conclusione) {
100
+ const documento = await leggiRapportoFn({ cartella, id });
101
+ if (typeof documento !== 'string' || impronta(documento) !== stato.conclusione.impronta) throw new Error('Il rapporto concluso non coincide con l’impronta nel giornale: ripristinare il file prima di riprovare.');
102
+ return { ...stato.conclusione.risultato, documento, giaScritto: true };
103
+ }
104
+ const composto = await finalizza(unito);
105
+ if (!composto.ok) return composto;
106
+ await scriviRapporto({ cartella, id, testo: composto.documento });
107
+ const risultato = Object.fromEntries(['ok', 'affermazioni', 'fonti', 'senzaPassaggio', 'bilancio', 'giudice'].filter(k => composto[k] !== undefined).map(k => [k, composto[k]]));
108
+ await accodaEventoFn({ cartella, id, separaRiga: true, evento: {
109
+ kind: 'deposit_finished', versione: 1, at: clock().toISOString(), ultimaImpronta: hash,
110
+ impronta: impronta(composto.documento), byte: Buffer.byteLength(composto.documento, 'utf8'), risultato,
111
+ } });
112
+ return { ...composto, giaScritto: true };
113
+ });
114
+ code.set(chiave, corrente);
115
+ try { return await corrente; }
116
+ finally { if (code.get(chiave) === corrente) code.delete(chiave); }
117
+ }
118
+
119
+ /** La parte statica resta identica fra i giri; il progresso compare solo alla ripresa. */
120
+ export function consegnaPartiRapporto(eventi = []) {
121
+ const stato = rileggiPartiRapporto(eventi);
122
+ const istruzioni = [
123
+ 'Deposita il rapporto progressivamente con research_deposit dopo ogni ramo del piano, una sezione per volta, senza aspettare la fine della ricerca.',
124
+ 'Aggiungi sempre parte:{indice:1,ultima:false}, poi aumenta indice di uno dopo ogni conferma. Questa istruzione sostituisce eventuali vecchie richieste di deposito unico.',
125
+ `Ogni chiamata deve contenere al massimo ${LIMITE_PARTE_RAPPORTO_BYTE} byte UTF-8 di argomenti JSON complessivi; punta a 3000 byte, circa 400 parole fra prosa e prove. Una sola chiamata di deposito per risposta: attendi la conferma prima di generare la successiva.`,
126
+ 'testo contiene solo la sezione corrente, con gli a capo necessari: il server concatena i pezzi senza aggiungere separatori. Il primo pezzo porta il titolo; gli ultimi completano conclusioni e fonti.',
127
+ 'Distribuisci anche affermazioni e fonti tra le parti, senza ripetere quelle già inviate; usa URL esatti in fonte. Gli elenchi possono essere vuoti. Non accumulare tutte le prove in una chiamata finale enorme.',
128
+ 'Per chiudere usa ultima:true; se tutto è già registrato, invia testo:"", affermazioni:[], fonti:[] con il prossimo indice e ultima:true. Soltanto allora il server assembla, verifica e pubblica il rapporto.',
129
+ 'Il messaggio finale in chat è solo una breve conferma, mai il rapporto: questa regola sostituisce anche eventuali vecchie istruzioni di scriverlo come ultimo messaggio.',
130
+ 'Non rigenerare i pezzi confermati e non ricopiare l’intero rapporto. In caso di conferma persa, ripeti lo stesso indice con gli stessi contenuti. Scrivi rapporto e messaggi per la persona in italiano, senza nomi tecnici degli attrezzi.',
131
+ ];
132
+ if (stato.parti.length) {
133
+ istruzioni.push(`Parti già registrate: ${stato.parti.length}. ${stato.parti.at(-1).contenuto.parte.ultima ? 'Non aggiungere altre parti.' : `Prossima parte: ${stato.prossimaParte}.`} I contenuti confermati sono nel giornale della ricerca; leggili solo se ti servono per proseguire, senza rispedirli.`);
134
+ if (stato.parti.at(-1).contenuto.parte.ultima) {
135
+ istruzioni.push('L’ultima parte è già registrata: completa o recupera la consegna ripetendo ESATTAMENTE questa chiamata, senza altra prosa:', JSON.stringify(stato.parti.at(-1).contenuto));
136
+ }
137
+ }
138
+ return istruzioni.join('\n');
139
+ }
@@ -0,0 +1,114 @@
1
+ /*
2
+ * PORTO FEDELE di AVM/mobile/src/lib/research/researchDossier.ts (94 righe, 11/09/2026).
3
+ *
4
+ * ⛔ `collector.mjs` (porto di `researchCollector.ts`) lo cura un'altra sessione:
5
+ * qui ne servono solo i TIPI, che in JSDoc non producono nessun import a runtime.
6
+ * Questo file quindi gira da solo.
7
+ *
8
+ * Il dossier come UNA cosa che due lettori sanno usare.
9
+ *
10
+ * La raccolta di un ramo deve servire una persona che sfoglia la Libreria e un
11
+ * processo che riprende ore dopo, in un'istanza nuova, senza memoria di che
12
+ * cosa aveva raccolto. Le due risposte ovvie sono sbagliate entrambe. Due file
13
+ * — uno di prosa, uno di dati — sono due verità da tenere allineate, che è il
14
+ * difetto che questo progetto ha già pagato più volte. Rileggere la prosa è
15
+ * peggio: funziona finché qualcuno non cambia un titolo di sezione.
16
+ *
17
+ * Quindi è un documento solo: testo leggibile per la persona e per l'indice di
18
+ * ricerca, col record strutturato recintato in fondo. Tutti e due scritti dallo
19
+ * stesso oggetto nello stesso respiro, così non possono divergere, e il lettore
20
+ * che ha bisogno dei dati non legge mai la prosa.
21
+ *
22
+ * Un dossier senza il recinto non si indovina. Si dichiara illeggibile — un
23
+ * file vecchio, o uno che qualcuno ha modificato — perché una sintesi costruita
24
+ * su una lettura speranzosa citerebbe passaggi che non sono mai stati controllati.
25
+ */
26
+
27
+ /**
28
+ * @import { TalosResearchCollection, TalosResearchSource } from './collector.mjs'
29
+ */
30
+
31
+ const FENCE_OPEN = '```talos-research-json'
32
+ const FENCE_CLOSE = '```'
33
+
34
+ /**
35
+ * Che cosa porta il recinto. Con la versione, così una forma successiva si riconosce.
36
+ *
37
+ * @typedef {object} TalosResearchDossierRecord
38
+ * @property {1} version
39
+ * @property {string} branchId
40
+ * @property {string} query
41
+ * @property {readonly TalosResearchSource[]} sources
42
+ * @property {readonly { url: string, reason: string }[]} unreachable
43
+ */
44
+
45
+ /**
46
+ * @param {TalosResearchCollection} collection
47
+ * @returns {string}
48
+ */
49
+ export function talosResearchDossierDocument(collection) {
50
+ /** @type {TalosResearchDossierRecord} */
51
+ const record = {
52
+ version: 1,
53
+ branchId: collection.branchId,
54
+ query: collection.query,
55
+ sources: collection.sources,
56
+ unreachable: collection.unreachable,
57
+ }
58
+
59
+ const prose = [
60
+ `# ${collection.query}`,
61
+ ...collection.sources.map((source) => [
62
+ `## ${source.title}`,
63
+ source.url,
64
+ source.publishedAt ? `data dichiarata: ${source.publishedAt}` : 'data non dichiarata',
65
+ source.obtained === 'snippet' ? '(solo estratto dal motore di ricerca)' : '',
66
+ '',
67
+ source.text,
68
+ ].filter(Boolean).join('\n')),
69
+ ...(collection.unreachable.length > 0
70
+ ? ['## Non raggiungibili', ...collection.unreachable.map((entry) => `${entry.url} — ${entry.reason}`)]
71
+ : []),
72
+ ].join('\n\n')
73
+
74
+ // Il recinto va per ULTIMO, così un lettore che si ferma prima — un'anteprima,
75
+ // un estratto nei risultati di ricerca — vede la prosa e non un muro di JSON.
76
+ return `${prose}\n\n${FENCE_OPEN}\n${JSON.stringify(record)}\n${FENCE_CLOSE}\n`
77
+ }
78
+
79
+ /**
80
+ * Rilegge un dossier, o ammette di non poterlo fare.
81
+ *
82
+ * Torna `null` invece di un tentativo. Chi chiama sta per costruire un rapporto
83
+ * le cui citazioni verranno controllate contro questi passaggi: un dossier
84
+ * recuperato a metà produrrebbe affermazioni verificate contro un testo che non
85
+ * è quello che è stato letto.
86
+ *
87
+ * @param {string} document
88
+ * @returns {TalosResearchCollection | null}
89
+ */
90
+ export function talosResearchParseDossier(document) {
91
+ const start = document.indexOf(FENCE_OPEN)
92
+ if (start < 0) return null
93
+ const from = start + FENCE_OPEN.length
94
+ const end = document.indexOf(FENCE_CLOSE, from)
95
+ if (end < 0) return null
96
+
97
+ try {
98
+ /** @type {TalosResearchDossierRecord} */
99
+ const parsed = JSON.parse(document.slice(from, end))
100
+ if (parsed?.version !== 1 || !Array.isArray(parsed.sources)) return null
101
+ return {
102
+ branchId: String(parsed.branchId ?? ''),
103
+ query: String(parsed.query ?? ''),
104
+ sources: parsed.sources,
105
+ unreachable: Array.isArray(parsed.unreachable) ? parsed.unreachable : [],
106
+ // La spesa non viaggia qui: sta già nel giornale, che è l'unico
107
+ // posto in cui si può contare. Una seconda copia prima o poi
108
+ // verrebbe sommata alla prima.
109
+ spend: { searches: 0, pages: 0, tokens: 0 },
110
+ }
111
+ } catch {
112
+ return null
113
+ }
114
+ }