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,97 @@
1
+ /**
2
+ * document-filename.mjs — porto di mobile/src/lib/fileNamePolicy.ts
3
+ * (letto per intero il 28/8), la parte che serve qui:
4
+ * `talosSafeFileStem`. Stessa libreria (`unicode-segmenter`), stessa
5
+ * logica: una stringa JS è indicizzata in unità UTF-16, ma i
6
+ * fornitori di storage ricevono nomi codificati e le persone vedono
7
+ * grafemi — un `slice()` grezzo sbaglia il confine due volte (può
8
+ * creare UTF-16 malformato E spezzare un glifo visibile a metà).
9
+ */
10
+ import { splitGraphemes } from 'unicode-segmenter/grapheme';
11
+
12
+ const UTF8 = new TextEncoder();
13
+ const FORBIDDEN_FILE_CHARACTERS = new Set(['/', '\\', ':', '"', '*', '?', '<', '>', '|']);
14
+
15
+ function unsafeFileNameCodePoint(code) {
16
+ if (code <= 0x1f || (code >= 0x7f && code <= 0x9f)) return true;
17
+ if (code >= 0xd800 && code <= 0xdfff) return true;
18
+ if (code === 0x00ad || code === 0x200b || code === 0xfeff) return true;
19
+ if (code === 0x200e || code === 0x200f) return true;
20
+ if (code >= 0x202a && code <= 0x202e) return true;
21
+ return code >= 0x2066 && code <= 0x2069;
22
+ }
23
+
24
+ function sanitizeFileStem(value) {
25
+ let safe = '';
26
+ for (const character of value.normalize('NFKC')) {
27
+ const code = character.codePointAt(0) ?? 0;
28
+ if (unsafeFileNameCodePoint(code)) continue;
29
+ safe += FORBIDDEN_FILE_CHARACTERS.has(character) ? ' ' : character;
30
+ }
31
+ return safe.replace(/\s+/gu, ' ').trim();
32
+ }
33
+
34
+ function appendWithinUtf8Budget(current, next, budget) {
35
+ const candidate = `${current}${next}`;
36
+ return UTF8.encode(candidate).byteLength <= budget ? candidate : null;
37
+ }
38
+
39
+ /** Torna uno STEM di nome file sicuro sotto un tetto di byte UTF-8. L'estensione è del chiamante, aggiunta dopo questo confine. */
40
+ /*
41
+ * ⛔ 10/09/2026 — visto nella FOTO di un giro vero: il modello ha generato un'immagine e il file si
42
+ * chiamava «Un gatto rosso (arancione) che dorme, rannicchiato e tranquillo, in un comodo an.jpg».
43
+ * Il taglio cadeva a metà di «angolo», perché qui si aggiungeva un carattere alla volta finché il
44
+ * budget di byte reggeva e poi ci si fermava, ovunque si fosse arrivati.
45
+ *
46
+ * Ricerca 10/09/2026, prima di scrivere: PatternFly (design system di Red Hat), pagina «Truncation» —
47
+ * si sceglie DOVE tagliare in base a dove sta la parte che distingue, e ⛔ «distinct but lengthy names
48
+ * may become identical after truncation if distinguishing features are located toward the end» (è lo
49
+ * stesso difetto che ieri rendeva indistinguibili due righe di delega nella barra); sadiqbd.com,
50
+ * «Text Truncation Edge Cases» — si arretra all'ultimo confine di parola prima del limite, con un
51
+ * ripiego per quando un confine NON esiste (un indirizzo web, una parola composta lunghissima);
52
+ * PatternFly, ancora: si evita di tagliare a ridosso della punteggiatura.
53
+ *
54
+ * ⇒ Qui si arretra all'ultimo spazio — ma solo se resta abbastanza nome. Un titolo di una parola sola
55
+ * più lunga del budget non ha nessun confine a cui arretrare: in quel caso il taglio duro è giusto,
56
+ * e togliere metà nome per eleganza sarebbe peggio del taglio.
57
+ */
58
+ const QUOTA_MINIMA_DOPO_IL_TAGLIO = 0.6;
59
+
60
+ /** Arretra all'ultimo confine di parola, se ne resta abbastanza. Puro, e non tocca la punteggiatura in coda. */
61
+ export function tagliaSuConfineDiParola(troncato, budget = null) {
62
+ /* ⛔ Il budget si calcola DOPO aver normalizzato: come valore di default leggeva `.length` su
63
+ ciò che arriva, e con `null` lanciava prima ancora di entrare nella funzione. */
64
+ const testo = String(troncato ?? '');
65
+ const tetto = Number.isSafeInteger(budget) && budget > 0 ? budget : testo.length;
66
+ const ultimoSpazio = testo.lastIndexOf(' ');
67
+ if (ultimoSpazio <= 0) return testo; // nessun confine: il taglio duro resta l'unica scelta onesta
68
+ if (ultimoSpazio < Math.floor(tetto * QUOTA_MINIMA_DOPO_IL_TAGLIO)) return testo; // arretrare costerebbe troppo nome
69
+ return testo.slice(0, ultimoSpazio).replace(/[\s.,;:!?\-–—]+$/u, '');
70
+ }
71
+
72
+ export function talosSafeFileStem(value, maxUtf8Bytes, fallback) {
73
+ if (!Number.isSafeInteger(maxUtf8Bytes) || maxUtf8Bytes < 1) {
74
+ throw new RangeError('TALOS_FILENAME_BUDGET_INVALID');
75
+ }
76
+
77
+ const safeFallback = sanitizeFileStem(fallback) || 'file';
78
+ const source = sanitizeFileStem(value) || safeFallback;
79
+ let bounded = '';
80
+
81
+ for (const grapheme of splitGraphemes(source)) {
82
+ const candidate = appendWithinUtf8Budget(bounded, grapheme, maxUtf8Bytes);
83
+ if (candidate === null) break;
84
+ bounded = candidate;
85
+ }
86
+ bounded = bounded.trimEnd();
87
+ /* ⛔ Solo se abbiamo davvero tagliato: un nome che ci stava tutto non si tocca. */
88
+ if (bounded && bounded.length < source.length) bounded = tagliaSuConfineDiParola(bounded, bounded.length);
89
+ if (bounded) return bounded;
90
+
91
+ for (const grapheme of splitGraphemes(safeFallback)) {
92
+ const candidate = appendWithinUtf8Budget(bounded, grapheme, maxUtf8Bytes);
93
+ if (candidate === null) break;
94
+ bounded = candidate;
95
+ }
96
+ return bounded.trimEnd() || 'f';
97
+ }
@@ -0,0 +1,493 @@
1
+ /**
2
+ * document-generator.mjs — porto di mobile/src/lib/documents/documentGenerator.ts
3
+ * (letto per intero il 28/8, piano elegant-spinning-dongarra.md,
4
+ * "document_create") — adattato al backend Node, non incollato
5
+ * verbatim: la pipeline `generate → verify` è identica, cambia SOLO
6
+ * dove il mobile doveva evitare il peso del bundle browser/webview
7
+ * (D11 mobile, "il primo paint della chat non deve mai portare
8
+ * queste librerie") — un vincolo che qui non esiste per costruzione:
9
+ * questo è un processo server locale, senza bundle, senza first
10
+ * paint, mai spedito a un utente. Owner, 28/8: "sì, aggiungile" alle
11
+ * sette dipendenze npm SOLO in questo backend, mai nel bundle
12
+ * frontend — vedi `harness-ui/package.json`.
13
+ *
14
+ * ⛔⛔ Differenza deliberata dal mobile, non un buco: qui NON esiste
15
+ * "salva nella Libreria" (il desktop non ha oggi un sistema Libreria
16
+ * — colonna C dell'inventario 74 attrezzi). Questo modulo si ferma a
17
+ * `{format, fileName, mediaType, bytes}` + la verifica — DOVE il file
18
+ * finisce (il workspace vero, stesso trattamento di `scrivi`) è
19
+ * deciso da chi chiama, non qui — stessa separazione già presente sul
20
+ * mobile fra `documentGenerator.ts` (genera) e `documentTools.ts`
21
+ * (salva), solo con una destinazione diversa.
22
+ *
23
+ * ⛔ pdfmake: la doc ufficiale (ctx7, 28/8) conferma che il pacchetto
24
+ * Node (`import pdfmake from 'pdfmake'`, non il build browser
25
+ * `pdfmake/build/pdfmake.min.js` che usa il mobile) accetta `setFonts`
26
+ * con PERCORSI FILE VERI — mai serve una VFS base64 lato server, e
27
+ * quindi mai serve il sub-setting Python (`build-pdf-fonts.mjs`
28
+ * mobile) che esiste solo per il peso di un APK distribuito. I
29
+ * quattro Roboto già dentro `node_modules/pdfmake` bastano.
30
+ */
31
+ import { createRequire } from 'node:module';
32
+ import { dirname, join } from 'node:path';
33
+
34
+ import { talosSafeFileStem } from './document-filename.mjs';
35
+ import {
36
+ analizzaInline, analizzaMarkdown, hrefSicuro, inlineInTestoSemplice,
37
+ PROFONDITA_MASSIMA_ELENCHI,
38
+ } from './research/markdown-server.mjs';
39
+
40
+ const require = createRequire(import.meta.url);
41
+
42
+ export const TALOS_SOURCE_TEXT_FORMATS = [
43
+ 'txt', 'json', 'xml',
44
+ 'js', 'jsx', 'ts', 'tsx', 'vue',
45
+ 'css', 'scss', 'php', 'py', 'rb', 'go', 'rs', 'java', 'kt', 'kts',
46
+ 'swift', 'c', 'h', 'cpp', 'hpp', 'cs',
47
+ 'sh', 'bash', 'zsh', 'ps1', 'sql',
48
+ 'yaml', 'yml', 'toml', 'ini',
49
+ ];
50
+
51
+ export const TALOS_DOCUMENT_FORMATS = [
52
+ 'md', 'csv', 'html', 'docx', 'xlsx', 'pptx', 'pdf',
53
+ ...TALOS_SOURCE_TEXT_FORMATS,
54
+ ];
55
+
56
+ const SOURCE_MEDIA_TYPES = {
57
+ txt: 'text/plain', json: 'application/json', xml: 'application/xml',
58
+ js: 'text/javascript', jsx: 'text/javascript', ts: 'text/plain', tsx: 'text/plain', vue: 'text/plain',
59
+ css: 'text/css', scss: 'text/plain', php: 'text/plain', py: 'text/plain', rb: 'text/plain',
60
+ go: 'text/plain', rs: 'text/plain', java: 'text/plain', kt: 'text/plain', kts: 'text/plain',
61
+ swift: 'text/plain', c: 'text/plain', h: 'text/plain', cpp: 'text/plain', hpp: 'text/plain', cs: 'text/plain',
62
+ sh: 'text/plain', bash: 'text/plain', zsh: 'text/plain', ps1: 'text/plain', sql: 'application/sql',
63
+ yaml: 'application/yaml', yml: 'application/yaml', toml: 'application/toml', ini: 'text/plain',
64
+ };
65
+
66
+ const MEDIA_TYPES = {
67
+ md: 'text/markdown',
68
+ csv: 'text/csv',
69
+ html: 'text/html',
70
+ docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
71
+ xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
72
+ pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
73
+ pdf: 'application/pdf',
74
+ ...SOURCE_MEDIA_TYPES,
75
+ };
76
+
77
+ const SOURCE_TEXT_FORMAT_SET = new Set(TALOS_SOURCE_TEXT_FORMATS);
78
+
79
+ function isTalosSourceTextFormat(format) {
80
+ return SOURCE_TEXT_FORMAT_SET.has(format);
81
+ }
82
+
83
+ function encode(text) {
84
+ return new TextEncoder().encode(text);
85
+ }
86
+
87
+ function verifyUtf8Text(bytes) {
88
+ let text;
89
+ try {
90
+ text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
91
+ } catch (errore) {
92
+ return { ok: false, detail: `non è UTF-8 valido: ${errore instanceof Error ? errore.message : String(errore)}` };
93
+ }
94
+ if (text.trim() === '') return { ok: false, detail: 'il file è vuoto' };
95
+ return { ok: true, detail: `${text.length} caratteri, ${text.split('\n').length} righe` };
96
+ }
97
+
98
+ function escapeHtml(value) {
99
+ return value.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
100
+ }
101
+
102
+ /** RFC 4180: un campo con virgola, apice o a-capo va fra virgolette. */
103
+ function csvField(value) {
104
+ return /[",\n\r]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value;
105
+ }
106
+
107
+ function rowsOf(spec) {
108
+ return spec.rows?.length ? spec.rows : [['Content'], [spec.body ?? '']];
109
+ }
110
+
111
+ /** Un titolo non è un nome file: può contenere qualunque cosa una persona digiti. */
112
+ function safeFileName(title, format) {
113
+ const trimmed = title.trim();
114
+ const suffix = `.${format}`;
115
+ const stemSource = trimmed.toLowerCase().endsWith(suffix) ? trimmed.slice(0, -suffix.length) : title;
116
+ const base = talosSafeFileStem(stemSource, 60, 'document');
117
+ return `${base}.${format}`;
118
+ }
119
+
120
+ /**
121
+ * Lo spec semplice `body`/`rows`, promosso a report — porta VERBATIM
122
+ * la stessa conversione riga-per-riga del mobile (documentGenerator.ts,
123
+ * `specToReport`): titoli `#`/`##`/`###`, elenchi `-`/`*`, paragrafi
124
+ * separati da riga vuota, `rows` come tabella vera.
125
+ */
126
+ function specToReport(spec) {
127
+ const blocks = [{ t: 'h', lvl: 1, x: spec.title }];
128
+ const body = (spec.body ?? '').replace(/\r\n/g, '\n');
129
+
130
+ let paragraph = [];
131
+ let bullets = [];
132
+ const flush = () => {
133
+ if (bullets.length) { blocks.push({ t: 'list', items: bullets }); bullets = []; }
134
+ if (paragraph.length) { blocks.push({ t: 'p', x: paragraph.join(' ') }); paragraph = []; }
135
+ };
136
+
137
+ for (const line of body.split('\n')) {
138
+ const text = line.trim();
139
+ if (text === '') { flush(); continue; }
140
+ const heading = /^(#{1,3})\s+(.*)$/.exec(text);
141
+ if (heading) {
142
+ flush();
143
+ blocks.push({ t: 'h', lvl: heading[1].length, x: heading[2].trim() });
144
+ continue;
145
+ }
146
+ const bullet = /^[-*]\s+(.*)$/.exec(text);
147
+ if (bullet) {
148
+ if (paragraph.length) { blocks.push({ t: 'p', x: paragraph.join(' ') }); paragraph = []; }
149
+ bullets.push(bullet[1]);
150
+ continue;
151
+ }
152
+ if (bullets.length) { blocks.push({ t: 'list', items: bullets }); bullets = []; }
153
+ paragraph.push(text);
154
+ }
155
+ flush();
156
+
157
+ if (spec.rows?.length) {
158
+ const [head, ...rest] = spec.rows;
159
+ blocks.push({ t: 'table', head, rows: rest });
160
+ }
161
+
162
+ return { meta: { title: spec.title }, theme: 'report', blocks };
163
+ }
164
+
165
+ let pdfMakeInstance = null;
166
+ /**
167
+ * pdfmake è un singleton stateful (`setFonts` muta un'istanza
168
+ * condivisa) — caricato pigramente una sola volta, riusato da ogni
169
+ * chiamata successiva, esattamente come il mobile lo carica una sola
170
+ * volta per sessione di chat (lì per il peso, qui per non rifare il
171
+ * lookup dei percorsi font ad ogni PDF).
172
+ */
173
+ async function pdfMakeConSuoiFont() {
174
+ if (pdfMakeInstance) return pdfMakeInstance;
175
+ const { default: pdfmake } = await import('pdfmake');
176
+ const roboto = dirname(require.resolve('pdfmake/build/fonts/Roboto/Roboto-Regular.ttf'));
177
+ /*
178
+ * ⛔⛔ Trovato dal vivo (smoke test, non ipotizzato): la policy locale
179
+ * di pdfmake governa OGNI accesso a file — anche il caricamento dei
180
+ * font stessi, non solo un'eventuale immagine referenziata dal
181
+ * contenuto. Un `() => false` totale rompeva il caricamento di
182
+ * Roboto. ⇒ Allowlist scoped alla SOLA cartella dei font: nessun
183
+ * blocco del nostro schema (cover/h/p/note/list/kpi/table/chart/
184
+ * spacer/pb — document-report.mjs) referenzia mai un'immagine per
185
+ * URL o percorso, quindi tutto il resto resta negato per davvero.
186
+ */
187
+ pdfmake.setUrlAccessPolicy(() => false);
188
+ pdfmake.setLocalAccessPolicy((percorso) => percorso.startsWith(roboto));
189
+ pdfmake.setFonts({
190
+ Roboto: {
191
+ normal: join(roboto, 'Roboto-Regular.ttf'),
192
+ bold: join(roboto, 'Roboto-Medium.ttf'),
193
+ italics: join(roboto, 'Roboto-Italic.ttf'),
194
+ // Nessun peso bold-italic dedicato spedito qui — puntarlo al
195
+ // medium evita un lancio se uno stile lo chiedesse mai, stessa
196
+ // scelta del mobile.
197
+ bolditalics: join(roboto, 'Roboto-Medium.ttf'),
198
+ },
199
+ });
200
+ pdfMakeInstance = pdfmake;
201
+ return pdfmake;
202
+ }
203
+
204
+ /** BC35: un solo adattatore DOCX per document_create e ricerca, con docx@9.5.1. */
205
+ async function generaDocx(spec) {
206
+ const { Document, Packer, Paragraph, TextRun, ExternalHyperlink, HeadingLevel, LevelFormat } = await import('docx');
207
+ const numerazioni = [];
208
+ const paragrafi = [new Paragraph({ text: spec.title, heading: HeadingLevel.HEADING_1 })];
209
+ const runs = testo => analizzaInline(testo).map(f => {
210
+ const run = new TextRun({ text: f.v, bold: f.forte, italics: f.corsivo,
211
+ ...(f.k === 'codice' ? { font: 'Consolas' } : {}) });
212
+ const link = f.k === 'link' ? hrefSicuro(f.href) : null;
213
+ return link ? new ExternalHyperlink({ link, children: [run] }) : run;
214
+ });
215
+ const elenco = (blocco, livello = 0) => {
216
+ const reference = `elenco-${numerazioni.length + 1}`;
217
+ numerazioni.push({ reference, levels: Array.from({ length: PROFONDITA_MASSIMA_ELENCHI }, (_, i) => ({
218
+ level: i, format: blocco.ordinata ? LevelFormat.DECIMAL : LevelFormat.BULLET,
219
+ text: blocco.ordinata ? `%${i + 1}.` : '•', start: blocco.inizio ?? 1,
220
+ style: { paragraph: { indent: { left: 720 * (i + 1), hanging: 360 } } },
221
+ })) });
222
+ for (const voce of blocco.voci) {
223
+ paragrafi.push(new Paragraph({
224
+ children: runs(typeof voce === 'string' ? voce : voce.x),
225
+ numbering: { reference, level: livello },
226
+ }));
227
+ if (typeof voce !== 'string') for (const figlio of voce.figli) {
228
+ if (figlio.t === 'lista') elenco(figlio, livello + 1);
229
+ else paragrafi.push(new Paragraph({ children: runs(figlio.x), indent: { left: 720 * (livello + 1) } }));
230
+ }
231
+ }
232
+ };
233
+ for (const blocco of analizzaMarkdown(spec.body ?? '')) {
234
+ switch (blocco.t) {
235
+ case 'h': paragrafi.push(new Paragraph({ children: runs(blocco.x), heading: HeadingLevel[`HEADING_${blocco.lvl}`] })); break;
236
+ case 'lista': elenco(blocco); break;
237
+ case 'codice':
238
+ for (const riga of blocco.x.split('\n')) paragrafi.push(new Paragraph({ children: [new TextRun({ text: riga, font: 'Consolas' })] }));
239
+ break;
240
+ case 'riga': paragrafi.push(new Paragraph({ text: '———' })); break;
241
+ case 'tabella':
242
+ for (const riga of [blocco.intestazione, ...blocco.righe]) {
243
+ paragrafi.push(new Paragraph({ text: riga.map(inlineInTestoSemplice).join(' · ') }));
244
+ }
245
+ break;
246
+ case 'citazione': paragrafi.push(new Paragraph({ children: [new TextRun('« '), ...runs(blocco.x), new TextRun(' »')] })); break;
247
+ case 'p': paragrafi.push(new Paragraph({ children: runs(blocco.x) })); break;
248
+ default: break;
249
+ }
250
+ }
251
+ // docx unisce gli stili superficialmente: il colore da solo cancellerebbe la dimensione.
252
+ // Il livello esplicito rende la struttura leggibile anche senza inferenze sui nomi Word.
253
+ const document = new Document({
254
+ styles: { default: Object.fromEntries([32, 26, 24, 22, 22, 22].map((size, i) => [`heading${i + 1}`, {
255
+ run: { color: '000000', size, bold: true },
256
+ paragraph: { outlineLevel: i, keepNext: true, spacing: { before: 240, after: 120 } },
257
+ }])) },
258
+ numbering: { config: numerazioni }, sections: [{ children: paragrafi }],
259
+ });
260
+ return new Uint8Array(await Packer.toBuffer(document));
261
+ }
262
+
263
+ /**
264
+ * document-report avvolge ogni voce in `text`. Promuove SOLO le voci composte del
265
+ * parser a stack/liste native pdfmake; stringhe e array di run restano identici.
266
+ */
267
+ function adattaElenchiPdf(definition) {
268
+ const vocePdf = voce => {
269
+ if (!voce || typeof voce !== 'object' || !Array.isArray(voce.figli)) return { text: voce, style: 'body' };
270
+ return { stack: [{ text: voce.x, style: 'body' }, ...voce.figli.map(figlio => figlio.t === 'list'
271
+ ? { [figlio.ordered ? 'ol' : 'ul']: figlio.items.map(vocePdf), ...(figlio.start !== undefined ? { start: figlio.start } : {}) }
272
+ : { text: figlio.x, style: 'body' })] };
273
+ };
274
+ for (const blocco of definition.content) {
275
+ const voci = blocco.ul ?? blocco.ol;
276
+ if (!Array.isArray(voci)) continue;
277
+ for (let i = 0; i < voci.length; i++) {
278
+ if (Array.isArray(voci[i].text?.figli)) voci[i] = vocePdf(voci[i].text);
279
+ }
280
+ }
281
+ return definition;
282
+ }
283
+
284
+ export async function generateTalosDocument(spec) {
285
+ if (spec.report && spec.format !== 'pdf') {
286
+ throw new Error(
287
+ `TALOS_DOCUMENT_REPORT_PDF_ONLY: \`report\` descrive un PDF impaginato. Per ${spec.format}, usa `
288
+ + '`body` (prosa) o `rows` (una tabella).',
289
+ );
290
+ }
291
+
292
+ if (isTalosSourceTextFormat(spec.format) && (spec.body ?? '').trim() === '') {
293
+ throw new Error('TALOS_DOCUMENT_SOURCE_BODY_REQUIRED: un file sorgente richiede `body` non vuoto.');
294
+ }
295
+
296
+ const hasContent = (spec.body ?? '').trim() !== ''
297
+ || (spec.rows?.length ?? 0) > 0
298
+ || (spec.slides?.length ?? 0) > 0
299
+ || (spec.format === 'pdf' && (spec.report?.blocks.length ?? 0) > 0);
300
+ if (!hasContent) {
301
+ throw new Error('TALOS_DOCUMENT_EMPTY: non c\'è niente da scrivere.');
302
+ }
303
+
304
+ const fileName = safeFileName(spec.title, spec.format);
305
+ const common = { format: spec.format, fileName, mediaType: MEDIA_TYPES[spec.format] };
306
+
307
+ if (isTalosSourceTextFormat(spec.format)) {
308
+ return { ...common, bytes: encode(spec.body) };
309
+ }
310
+
311
+ switch (spec.format) {
312
+ case 'md':
313
+ return { ...common, bytes: encode(spec.body ?? spec.title) };
314
+
315
+ case 'csv':
316
+ return { ...common, bytes: encode(rowsOf(spec).map((row) => row.map(csvField).join(',')).join('\r\n')) };
317
+
318
+ /*
319
+ * ⛔⛔⛔ BC-11, 11/09/2026 — `format:'html'` NON POTEVA SCRIVERE UNA PAGINA HTML.
320
+ *
321
+ * Riprodotto: `html` non e' nei `TALOS_SOURCE_TEXT_FORMATS` (`:39`), quindi finiva SEMPRE qui,
322
+ * e qui ogni blocco passa da `escapeHtml`. Un `<section id="x">` scritto dal modello arrivava
323
+ * sul disco come `&lt;section id="x"&gt;` dentro un `<p>`. ⇒ Per il compito dell'owner
324
+ * «genera un file html di almeno 1000 righe» non esisteva NESSUN attrezzo capace: restavano
325
+ * `scrivi` (che allora voleva tutto in una risposta) e la shell (che ha il tetto della riga di
326
+ * comando, misurato: 23.941 caratteri → «La riga di comando e' troppo lunga»). Il «giro
327
+ * assurdo» del modello — `_p2.html`, `_p3.html`, `_p4.html`, `_p5.html` — non era una sua
328
+ * bizzarria: era l'unica strada rimasta.
329
+ *
330
+ * ⛔ PERCHE' NON UN CAMPO `raw:true`. Era l'altra forma possibile, ed e' stata scartata per due
331
+ * ragioni, non per gusto:
332
+ * 1. un parametro in piu' e' un parametro che il modello deve SCOPRIRE. arXiv:2608.26130
333
+ * «Agents Don't Paginate: First-Chunk Selection for LLM Tool Responses» (letto
334
+ * 11/09/2026) misura su log di produzione di un middleware MCP **zero** richieste del
335
+ * secondo pezzo, pur essendo la paginazione disponibile in tutti i protocolli: un agente
336
+ * non va a cercare l'opzione giusta, usa la prima strada che gli riesce. Un `raw:true`
337
+ * dimenticato riporta esattamente al difetto di oggi.
338
+ * 2. avvolgere un documento GIA' COMPLETO non e' mai la risposta giusta: `<!doctype html>`
339
+ * dentro un `<p>` dentro un altro `<!doctype html>` non e' un caso limite discutibile, e'
340
+ * sempre un file rotto. Non c'e' un falso positivo da temere nella direzione che conta.
341
+ * ⇒ Nessun campo nuovo: se il `body` E' GIA' un documento, si scrive com'e'; se e' prosa,
342
+ * resta avvolto esattamente come prima. E il modello lo sa DALLO SCHEMA, non sbattendoci:
343
+ * la descrizione di `format` nel kernel (`talosHarness.mjs`, attrezzo `document_create`) lo
344
+ * dice in una riga.
345
+ *
346
+ * ⛔ Cosa fanno gli altri, letto nel codice dei cloni: nessuno dei nove ha un generatore che
347
+ * riformatta — il loro attrezzo di scrittura mette sul disco i byte che riceve
348
+ * (opencode `tool/write.ts`: solo `content` e `filePath`; Hermes `file_tools.py:2729`:
349
+ * solo `path` e `content`). L'idea di un «generatore di documenti» che conosce il formato e'
350
+ * nostra, ed e' un vantaggio per `docx`/`xlsx`/`pptx`/`pdf`; su `html` era diventata una
351
+ * gabbia, perche' l'HTML e' l'unico di quei formati che il modello sa scrivere da solo.
352
+ */
353
+ case 'html': {
354
+ const corpo = spec.body ?? '';
355
+ // Un BOM o spazi davanti non cambiano la risposta: e' comunque un documento completo.
356
+ if (/^?\s*<(!doctype\s+html|html[\s>])/i.test(corpo)) return { ...common, bytes: encode(corpo) };
357
+ return {
358
+ ...common,
359
+ bytes: encode([
360
+ '<!doctype html>',
361
+ '<html><head><meta charset="utf-8">',
362
+ `<title>${escapeHtml(spec.title)}</title></head><body>`,
363
+ `<h1>${escapeHtml(spec.title)}</h1>`,
364
+ ...corpo.split('\n\n').map((block) => `<p>${escapeHtml(block)}</p>`),
365
+ '</body></html>',
366
+ ].join('\n')),
367
+ };
368
+ }
369
+
370
+ case 'docx': {
371
+ return { ...common, bytes: await generaDocx(spec) };
372
+ }
373
+
374
+ case 'xlsx': {
375
+ const xlsx = await import('xlsx');
376
+ const sheet = xlsx.utils.aoa_to_sheet(rowsOf(spec));
377
+ const book = xlsx.utils.book_new();
378
+ xlsx.utils.book_append_sheet(book, sheet, 'Sheet1');
379
+ const written = xlsx.write(book, { type: 'array', bookType: 'xlsx' });
380
+ return { ...common, bytes: new Uint8Array(written) };
381
+ }
382
+
383
+ case 'pptx': {
384
+ const { default: PptxGenJS } = await import('pptxgenjs');
385
+ const deck = new PptxGenJS();
386
+ const slides = spec.slides?.length
387
+ ? spec.slides
388
+ : [{ title: spec.title, bullets: (spec.body ?? '').split('\n').filter(Boolean) }];
389
+ for (const entry of slides) {
390
+ const slide = deck.addSlide();
391
+ slide.addText(entry.title, { x: 0.5, y: 0.4, w: 9, h: 0.8, fontSize: 28, bold: true });
392
+ if (entry.bullets.length) {
393
+ slide.addText(
394
+ entry.bullets.map((text) => ({ text, options: { bullet: true } })),
395
+ { x: 0.6, y: 1.4, w: 8.8, h: 4, fontSize: 16 },
396
+ );
397
+ }
398
+ }
399
+ const written = await deck.write({ outputType: 'arraybuffer' });
400
+ return { ...common, bytes: new Uint8Array(written) };
401
+ }
402
+
403
+ case 'pdf': {
404
+ const [pdfmake, { buildTalosReportDefinition }] = await Promise.all([
405
+ pdfMakeConSuoiFont(),
406
+ import('./document-report.mjs'),
407
+ ]);
408
+ const definition = adattaElenchiPdf(buildTalosReportDefinition(
409
+ spec.report ? { ...spec.report, meta: { title: spec.title } } : specToReport(spec),
410
+ ));
411
+ const buffer = await pdfmake.createPdf(definition).getBuffer();
412
+ return { ...common, bytes: new Uint8Array(buffer) };
413
+ }
414
+
415
+ default:
416
+ throw new Error(`TALOS_DOCUMENT_FORMAT_UNKNOWN: ${spec.format}`);
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Riapre il file e riporta cosa c'è VERAMENTE dentro — non un
422
+ * checksum (prova che abbiamo scritto quello che intendevamo, non la
423
+ * domanda giusta): la domanda è se il file che una persona apre è
424
+ * integro.
425
+ */
426
+ export async function verifyTalosDocument(document) {
427
+ try {
428
+ if (isTalosSourceTextFormat(document.format)) return verifyUtf8Text(document.bytes);
429
+
430
+ switch (document.format) {
431
+ case 'md':
432
+ case 'csv':
433
+ case 'html':
434
+ return verifyUtf8Text(document.bytes);
435
+
436
+ case 'docx': {
437
+ const parts = await openOoxml(document.bytes);
438
+ const body = parts['word/document.xml'];
439
+ if (!body) return { ok: false, detail: 'nessuna parte documento dentro il file' };
440
+ const paragraphs = (body.match(/<w:p[ >]/g) ?? []).length;
441
+ if (paragraphs === 0) return { ok: false, detail: 'il documento non ha paragrafi' };
442
+ return { ok: true, detail: `riaperto: ${paragraphs} paragrafi` };
443
+ }
444
+
445
+ case 'xlsx': {
446
+ const xlsx = await import('xlsx');
447
+ const book = xlsx.read(document.bytes, { type: 'array' });
448
+ const names = book.SheetNames;
449
+ if (!names.length) return { ok: false, detail: 'nessun foglio dentro il file' };
450
+ const first = book.Sheets[names[0]];
451
+ const rows = xlsx.utils.sheet_to_json(first, { header: 1 });
452
+ return { ok: rows.length > 0, detail: `riaperto: ${names.length} fogli, ${rows.length} righe` };
453
+ }
454
+
455
+ case 'pptx': {
456
+ const parts = await openOoxml(document.bytes);
457
+ const slides = Object.keys(parts).filter((name) => /^ppt\/slides\/slide\d+\.xml$/.test(name));
458
+ if (slides.length === 0) return { ok: false, detail: 'nessuna slide dentro il file' };
459
+ return { ok: true, detail: `riaperto: ${slides.length} slide` };
460
+ }
461
+
462
+ case 'pdf': {
463
+ const { PDFDocument } = await import('pdf-lib');
464
+ const pdf = await PDFDocument.load(document.bytes);
465
+ const pages = pdf.getPageCount();
466
+ if (pages === 0) return { ok: false, detail: 'il pdf non ha pagine' };
467
+ return { ok: true, detail: `riaperto: ${pages} pagine` };
468
+ }
469
+
470
+ default:
471
+ return { ok: false, detail: `formato sconosciuto: ${document.format}` };
472
+ }
473
+ } catch (errore) {
474
+ const detail = errore instanceof Error ? errore.message : String(errore);
475
+ return { ok: false, detail: `non si è potuto riaprire: ${detail}` };
476
+ }
477
+ }
478
+
479
+ /** Rilegge il contenitore OOXML, così un archivio troncato fallisce qui. */
480
+ async function openOoxml(bytes) {
481
+ const { default: JSZip } = await import('jszip');
482
+ const archive = await JSZip.loadAsync(bytes);
483
+ const parts = {};
484
+ for (const [name, entry] of Object.entries(archive.files)) {
485
+ if (entry.dir) continue;
486
+ // Solo le parti ISPEZIONATE vengono decompresse — una presentazione
487
+ // piena di immagini non va decodificata in memoria solo per contare le slide.
488
+ parts[name] = /\.xml$/.test(name) && /document\.xml$|presentation\.xml$/.test(name)
489
+ ? await entry.async('string')
490
+ : '';
491
+ }
492
+ return parts;
493
+ }