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,332 @@
1
+ /**
2
+ * ⛔⛔⛔ P0 · PUNTO 7 — IL FAILSAFE DELLA GENERAZIONE, IN UNA PORTA SOLA (16/09/2026).
3
+ *
4
+ * ## Il difetto che questo file chiude
5
+ *
6
+ * TALOS aveva TRE tetti sulla DURATA di una risposta del modello, tutti e tre deadline TOTALI —
7
+ * cioè misurate dall'inizio della chiamata, indifferenti al fatto che il modello stesse parlando:
8
+ *
9
+ * 1. `talosHarness.mjs` · `AbortSignal.timeout(180_000)` su ogni giro, streaming compreso;
10
+ * 2. `runtime-owner-adapter.mjs` · `AbortSignal.timeout(timeoutSeconds * 1000)`, default 60 s;
11
+ * 3. `undici` sotto `fetch` · `headersTimeout` e `bodyTimeout` a 300 s DI SERIE, mai dichiarati.
12
+ *
13
+ * Misurato il 16/09/2026 con un fornitore finto non-OpenRouter che emette un token ogni 2 s per
14
+ * 90 s, col default di 60 s: la risposta veniva **tagliata a 60 s mentre i token arrivavano**, e
15
+ * l'errore diceva «Il fornitore ha superato il tempo massimo» — una bugia, il fornitore stava
16
+ * rispondendo benissimo.
17
+ *
18
+ * ⛔ È la stessa forma del tetto sui GIRI, tolto l'11/09/2026 (`giri-senza-tetto.test.mjs`): *un
19
+ * tetto sulla durata non è una guardia contro il guasto — è una guardia contro il TEMPO, e il
20
+ * primo a incontrarla è il compito lungo ma SANO*.
21
+ *
22
+ * ## Perché un'INATTIVITÀ e non un numero più grande
23
+ *
24
+ * Alzare 60 a 600 sposta il muro, non lo toglie: il giorno del prompt da 200k token su CPU si
25
+ * ripresenta identico. La domanda giusta non è «quanto può durare» ma «da quanto tempo è MORTO».
26
+ *
27
+ * Ricerca fatta PRIMA di scrivere, 16/09/2026:
28
+ * · **OpenAI Codex CLI** — `DEFAULT_STREAM_IDLE_TIMEOUT_MS = 300_000`
29
+ * (`codex-rs/model-provider-info/src/lib.rs`): un'inattività dello stream, non un tetto totale.
30
+ * · **openai/codex#39771**, «High-reasoning Responses WebSocket idle timeout is a false
31
+ * dead-stream (model resumed after 8.5 min of no text frames)»: con ragionamento alto perfino
32
+ * CINQUE minuti di silenzio sono normali, e l'issue conclude che «lowering the stream idle
33
+ * timeout globally is not a safe fix because healthy reasoning can produce no response frames
34
+ * for that length of time».
35
+ * · **openai/codex#23807** e **#31376**: stalli di ESATTAMENTE 300 s fra un risultato di attrezzo
36
+ * e la richiesta successiva — la firma inconfondibile di un tetto di trasporto che nessuno ha
37
+ * dichiarato (i nostri 300 s di undici).
38
+ * · **ggml-org/llama.cpp#22997** e `tools/server/README.md`: durante il *prefill* di un prompt
39
+ * lungo llama-server non manda **nessun byte** per minuti; da lì `sse_ping_interval`, i commenti
40
+ * SSE emessi apposta per rendere osservabile un canale vivo che tace.
41
+ * · **OpenRouter · Streaming**: durante l'elaborazione il canale porta commenti SSE
42
+ * (`: OPENROUTER PROCESSING`) come keep-alive.
43
+ * · **Anthropic** (anthropics/anthropic-sdk-python#1698): sopra i ~10 minuti lo streaming diventa
44
+ * obbligatorio — cioè il primo fornitore al mondo mette la sua soglia di «operazione lunga ma
45
+ * sana» a dieci minuti, non a uno.
46
+ *
47
+ * ⇒ **30 minuti.** Il numero non è tondo per caso: è ~6× l'inattività di Codex e ~3× la soglia
48
+ * Anthropic, cioè ampiamente oltre il peggior silenzio SANO documentato (8,5 minuti misurati
49
+ * nell'issue #39771), e comunque un ordine di grandezza sotto «per sempre». Chi ha un caso
50
+ * peggiore lo dice con `TALOS_GENERATION_IDLE_MS`.
51
+ *
52
+ * ## Cosa NON è questo failsafe
53
+ *
54
+ * ⛔ Non è un timeout di generazione, e l'errore che produce non deve dirlo: quando scatta, il
55
+ * canale è **morto** — nessun byte, nessun commento, per mezz'ora. La classe è `rete`
56
+ * («la connessione con il fornitore del modello è caduta»), ed è TRANSITORIA: una ricerca caduta
57
+ * così si può riprendere. Chiamarlo «timeout» manderebbe a studiare il modello invece del cavo.
58
+ *
59
+ * ⛔ Non sostituisce lo STOP della persona, che resta la prima e più veloce via d'uscita: qui il
60
+ * `userSignal` è sempre onorato per primo, e la prova `P0-D-08`/`P0-D-09` misura che chiude in
61
+ * meno di un secondo anche su un fornitore che tace.
62
+ */
63
+
64
+ /**
65
+ * Trenta minuti. Vedi la motivazione in testa al file: è oltre il peggior silenzio SANO
66
+ * documentato (8,5 min, openai/codex#39771), non un numero scelto perché suona grande.
67
+ */
68
+ export const INATTIVITA_GENERAZIONE_MS_PREDEFINITA = 1_800_000;
69
+
70
+ /**
71
+ * ⛔ Sotto il minuto non è un failsafe, è il tetto di prima con un altro nome: un prefill lungo su
72
+ * CPU tace molto più a lungo. Chi scrive un valore più basso lo sta usando per il verso sbagliato.
73
+ */
74
+ export const INATTIVITA_GENERAZIONE_MS_MINIMA = 60_000;
75
+
76
+ /** Il nome della variabile, in un posto solo: lo cita il README e lo leggono i test. */
77
+ export const VARIABILE_INATTIVITA_GENERAZIONE = 'TALOS_GENERATION_IDLE_MS';
78
+
79
+ /**
80
+ * Il silenzio del fornitore: nessun byte E nessun commento SSE per tutto il limite.
81
+ *
82
+ * ⛔ `classe: 'rete'` e non `'timeout-fornitore'`: `classificaGuasto` in `runtime-owner-adapter.mjs`
83
+ * legge questo `code` ESPLICITAMENTE, invece di lasciare che la tabella dei segni indovini dal
84
+ * testo del messaggio — un filtro che riconosce la MENZIONE di una parola non riconosce la cosa.
85
+ */
86
+ export class SilenzioDelFornitoreError extends Error {
87
+ constructor(limiteMs) {
88
+ const minuti = Math.max(1, Math.round(limiteMs / 60_000));
89
+ super(`Connessione con il fornitore interrotta: nessun dato per ${minuti} minuti.`);
90
+ this.name = 'SilenzioDelFornitoreError';
91
+ this.code = 'PROVIDER_SILENCE';
92
+ this.classe = 'rete';
93
+ this.transitorio = true;
94
+ this.limiteMs = limiteMs;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Il limite di inattività, letto dall'ambiente.
100
+ *
101
+ * ⛔ Un valore illeggibile NON spegne la guardia in silenzio: si torna al default e si va avanti.
102
+ * Lo ZERO invece la spegne davvero, ed è l'unico modo DICHIARATO per farlo — chi lo scrive sa
103
+ * cosa sta facendo, e la differenza fra «non ho capito il tuo valore» e «hai chiesto niente
104
+ * guardia» deve restare leggibile.
105
+ * ⛔ Sotto il minimo si SALE al minimo invece di obbedire: vedi `INATTIVITA_GENERAZIONE_MS_MINIMA`.
106
+ *
107
+ * @param {Record<string, string|undefined>} [env]
108
+ * @returns {number} millisecondi; `0` significa nessun failsafe
109
+ */
110
+ export function leggiInattivitaGenerazioneMs(env = process.env) {
111
+ const grezzo = env?.[VARIABILE_INATTIVITA_GENERAZIONE];
112
+ if (grezzo === undefined || grezzo === null || String(grezzo).trim() === '') return INATTIVITA_GENERAZIONE_MS_PREDEFINITA;
113
+ const valore = Number(grezzo);
114
+ if (!Number.isFinite(valore) || valore < 0) return INATTIVITA_GENERAZIONE_MS_PREDEFINITA;
115
+ if (valore === 0) return 0;
116
+ return Math.max(INATTIVITA_GENERAZIONE_MS_MINIMA, Math.round(valore));
117
+ }
118
+
119
+ /**
120
+ * Il guardiano dell'inattività, in una funzione sola.
121
+ *
122
+ * Aspetta `promessa`; se non si conclude entro `limiteMs` aborta il `controller` e rifiuta con
123
+ * l'errore che `creaErrore` costruisce. Lo `userSignal` vince sempre e subito.
124
+ *
125
+ * ⛔ Nessun timer nuovo «per far quadrare una corsa»: questo È il timer del failsafe, uno solo, e
126
+ * viene CANCELLATO da ogni via d'uscita (`pulisci`). Il listener sullo stop è `{once:true}` e si
127
+ * toglie comunque: un ascoltatore lasciato su un segnale che vive quanto la sessione è una
128
+ * perdita che cresce a ogni giro.
129
+ * ⭐ `programmaTimer`/`annullaTimer` sono iniettabili perché una prova non deve aspettare mezz'ora
130
+ * per provare la mezz'ora.
131
+ *
132
+ * @template T
133
+ * @param {Promise<T>|T} promessa
134
+ * @param {{limiteMs:number, controller:AbortController, userSignal?:AbortSignal|null,
135
+ * creaErrore?:(ms:number)=>Error, programmaTimer?:Function, annullaTimer?:Function}} opzioni
136
+ * @returns {Promise<T>}
137
+ */
138
+ export function sorvegliaInattivita(promessa, {
139
+ limiteMs,
140
+ controller,
141
+ userSignal = null,
142
+ creaErrore = (ms) => new SilenzioDelFornitoreError(ms),
143
+ programmaTimer = setTimeout,
144
+ annullaTimer = clearTimeout,
145
+ } = {}) {
146
+ return new Promise((risolvi, rifiuta) => {
147
+ let conclusa = false;
148
+ let timer = null;
149
+ const pulisci = () => {
150
+ if (timer !== null) annullaTimer(timer);
151
+ userSignal?.removeEventListener?.('abort', fermaUtente);
152
+ };
153
+ const chiudi = (azione, valore) => {
154
+ if (conclusa) return;
155
+ conclusa = true;
156
+ pulisci();
157
+ azione(valore);
158
+ };
159
+ function fermaUtente() {
160
+ const motivo = userSignal.reason ?? new DOMException('Fermato dall’utente', 'AbortError');
161
+ controller.abort(motivo);
162
+ chiudi(rifiuta, motivo);
163
+ }
164
+ /*
165
+ * ⛔⛔⛔ 16/09/2026 — IL GUINZAGLIO SULLA PROMESSA SI ATTACCA PER PRIMO, E SEMPRE.
166
+ *
167
+ * Stava in fondo, dopo un `return` anticipato: se lo Stop era GIÀ arrivato quando questa
168
+ * funzione viene chiamata, si usciva subito e `lettore.read()` restava **senza un solo
169
+ * gestore**. Quando poi il corpo veniva demolito, quella promessa rifiutava nel vuoto: un
170
+ * `unhandledRejection` che esplode dopo, addosso a chi non c'entra.
171
+ * ⛔ Misurato: il difetto è comparso solo quando il guardiano è stato DAVVERO agganciato —
172
+ * `PH-FALLBACK-20` («stop DOPO che la richiesta è partita») ha cominciato a segnalare
173
+ * «asynchronous activity after the test ended ... AbortError: This operation was aborted»
174
+ * pur restando verde come singola prova, e a fallire era il FILE. Finché il guardiano era
175
+ * inerte non si vedeva niente, perché non c'era nessuna lettura da orfanare.
176
+ * ⭐ `chiudi` è idempotente: chi arriva primo decide, e chi arriva dopo è un no-op. Quindi
177
+ * attaccare prima non cambia nessun esito — cambia solo che nessuna rejection resta sola.
178
+ */
179
+ Promise.resolve(promessa).then((v) => chiudi(risolvi, v), (e) => chiudi(rifiuta, e));
180
+ if (userSignal?.aborted) { fermaUtente(); return; }
181
+ userSignal?.addEventListener?.('abort', fermaUtente, { once: true });
182
+ /* ⛔ `limiteMs` a zero (o non numerico) = nessun failsafe: si aspetta, e basta. */
183
+ if (Number.isFinite(limiteMs) && limiteMs > 0) {
184
+ timer = programmaTimer(() => {
185
+ const errore = creaErrore(limiteMs);
186
+ controller.abort(errore);
187
+ chiudi(rifiuta, errore);
188
+ }, limiteMs);
189
+ timer?.unref?.();
190
+ }
191
+ });
192
+ }
193
+
194
+ /**
195
+ * Lo stesso guardiano, messo addosso al CORPO di una risposta già arrivata.
196
+ *
197
+ * ⛔ Il conteggio si azzera a ogni **byte**, non a ogni `data:` — quindi i commenti SSE
198
+ * (`: OPENROUTER PROCESSING`, i ping di llama-server) contano come VITA. Un failsafe che
199
+ * guardasse solo i pacchetti con contenuto scarterebbe muto proprio ciò che il fornitore manda
200
+ * apposta per dire «sono vivo», e ucciderebbe la connessione più sana che c'è.
201
+ *
202
+ * ⭐ Perché qui e non solo nel dispatcher: `bodyTimeout` di undici copre il trasporto HTTP, ma non
203
+ * il motore locale servito da un ponte, né gli SDK nativi, né l'agente ACP. Questo confine li
204
+ * vede tutti, perché parla di `Response`, e produce un errore NOSTRO e classificato invece di un
205
+ * `UND_ERR_BODY_TIMEOUT` che arriverebbe in chat come «terminated».
206
+ *
207
+ * @param {Response} risposta
208
+ * @param {{limiteMs:number, userSignal?:AbortSignal|null, programmaTimer?:Function, annullaTimer?:Function}} opzioni
209
+ * @returns {Response} la stessa risposta, col corpo sorvegliato (o intatta se non c'è corpo)
210
+ */
211
+ export function sorvegliaCorpoDiGenerazione(risposta, {
212
+ limiteMs,
213
+ userSignal = null,
214
+ programmaTimer = setTimeout,
215
+ annullaTimer = clearTimeout,
216
+ } = {}) {
217
+ /*
218
+ * ⛔⛔⛔ 16/09/2026 — QUESTA GUARDIA È NATA DA UN DIFETTO CHE È VISSUTO INVISIBILE PER UN GIRO.
219
+ *
220
+ * L'aggancio scriveva `sorveglia(conCacheDichiarata(await fetch(...)))` e `conCacheDichiarata` è
221
+ * `async`: qui arrivava una **Promise**, non una `Response`. Una Promise non ha `.body`, quindi
222
+ * la riga sotto la restituiva **intatta** — e il failsafe risultava attaccato a niente su
223
+ * deepseek, z.ai, openai e su tutto il percorso cloud, in streaming e non. Nessun errore, nessun
224
+ * avviso: il chiamante riceveva esattamente ciò che aveva passato, e sembrava che funzionasse.
225
+ * A fermare i silenzi era il `bodyTimeout` del dispatcher, 1,2× più tardi e con un altro nome.
226
+ *
227
+ * ⇒ Un contratto violato si DICE. È la stessa lezione di [[il-catch-giusto-nasconde-il-bug-sbagliato]]:
228
+ * chi degrada in silenzio deve dichiarare quale guasto copre e rilanciare gli errori di
229
+ * contratto. Qui «non ho un corpo da sorvegliare» è un caso legittimo (una 204, una risposta
230
+ * già materializzata); «mi hai dato una Promise» non lo è mai.
231
+ */
232
+ if (typeof risposta?.then === 'function') {
233
+ throw new TypeError('sorvegliaCorpoDiGenerazione vuole una Response già risolta, non una Promise: senza `await` il failsafe resterebbe attaccato a niente.');
234
+ }
235
+ if (!Number.isFinite(limiteMs) || limiteMs <= 0) return risposta;
236
+ const corpo = risposta?.body;
237
+ if (!corpo || typeof corpo.getReader !== 'function') return risposta;
238
+
239
+ const lettore = corpo.getReader();
240
+ const controller = new AbortController();
241
+ const sorvegliato = new ReadableStream({
242
+ async pull(uscita) {
243
+ try {
244
+ const { done, value } = await sorvegliaInattivita(lettore.read(), {
245
+ limiteMs, controller, userSignal, programmaTimer, annullaTimer,
246
+ });
247
+ if (done) { uscita.close(); return; }
248
+ uscita.enqueue(value);
249
+ } catch (errore) {
250
+ await lettore.cancel(errore).catch(() => {});
251
+ uscita.error(userSignal?.aborted ? (userSignal.reason ?? errore) : errore);
252
+ }
253
+ },
254
+ cancel(motivo) { return lettore.cancel(motivo); },
255
+ });
256
+ return new Response(sorvegliato, {
257
+ status: risposta.status,
258
+ statusText: risposta.statusText,
259
+ headers: risposta.headers,
260
+ });
261
+ }
262
+
263
+ /*
264
+ * ⛔⛔ IL DISPATCHER — e perché NON è `setGlobalDispatcher`.
265
+ *
266
+ * Sotto `fetch` c'è undici, con `headersTimeout` e `bodyTimeout` a **300 s di serie**: se non li
267
+ * si tocca, il tetto che si è appena tolto dal codice resta là sotto, tacito. È il caso di
268
+ * openai/codex#23807 (stalli di esattamente 300 s) e del post di raphael.badia.cc
269
+ * («Fixing Headers Timeout Error with Vercel AI SDK», letto 16/09/2026).
270
+ *
271
+ * La ricetta che circola è `setGlobalDispatcher(new Agent({...}))` dal pacchetto npm `undici`.
272
+ * Qui NON si può e NON si deve:
273
+ *
274
+ * · `undici` **non è una dipendenza dichiarata** di harness-ui (misurato: non compare in
275
+ * `package.json`; sul disco c'è solo come dipendenza transitiva di qualcun altro). Aggiungerla
276
+ * è una dipendenza nuova, e non serve.
277
+ * · È **globale**: cambierebbe i tetti di OGNI fetch dell'applicazione — ricerca web, hub dei
278
+ * modelli, proxy delle immagini, MCP. Quelle chiamate devono restare impazienti. Il punto 7
279
+ * parla della durata del RAGIONAMENTO, non della pazienza di tutto il prodotto.
280
+ * · Ed è **fragile in modo silenzioso**: misurato il 16/09/2026 su Node v24.18.0, il `fetch`
281
+ * globale legge `Symbol.for('undici.globalDispatcher.1')`; scrivere solo su `.2` non ha alcun
282
+ * effetto (prova: 5003 ms con un tetto da 1200 ms, cioè il tetto non ha morso). Una cura che
283
+ * arriva inerte e non protesta è peggio di nessuna cura.
284
+ *
285
+ * ⇒ Si costruisce un dispatcher **per richiesta**, dalla classe che Node stesso usa già — misurato
286
+ * funzionante: `fetch(url, { dispatcher })` con tetto 1200 ms su header a 5000 ms fallisce con
287
+ * `UND_ERR_HEADERS_TIMEOUT` in 1498 ms. Nessuna dipendenza nuova, nessun effetto fuori dalle
288
+ * chiamate ai fornitori.
289
+ */
290
+
291
+ /** Costruito una volta sola per limite: un Agent tiene un pool di socket, non se ne fa uno a giro. */
292
+ const dispatcherPerLimite = new Map();
293
+
294
+ /**
295
+ * Il dispatcher undici per le chiamate ai fornitori, con i tetti ALLINEATI al failsafe.
296
+ *
297
+ * ⛔ I tetti del trasporto stanno un po' SOPRA il failsafe (× `MARGINE`), mai sotto: così a
298
+ * scadere per primo è sempre la NOSTRA guardia, che produce un errore classificato e leggibile,
299
+ * e mai `UND_ERR_BODY_TIMEOUT`, che in chat arriverebbe come «terminated». Il trasporto è la
300
+ * rete di sicurezza della rete di sicurezza.
301
+ * ⛔ `limiteMs` a 0 (failsafe spento) ⇒ tetti a 0, che per undici significa **disabilitato**
302
+ * (docs undici, `Client`/`Dispatcher`): chi spegne la guardia spegne anche i 300 s taciti,
303
+ * altrimenti spegnerebbe solo quella che si vede.
304
+ *
305
+ * @param {{limiteMs:number, dispatcherGlobale?:any}} opzioni
306
+ * @returns {any|null} il dispatcher, o `null` se questo Node non permette di costruirlo (e allora
307
+ * restano i 300 s di serie: il chiamante non deve fingere che vada bene, deve dirlo)
308
+ */
309
+ export function dispatcherDiGenerazione({ limiteMs, dispatcherGlobale = undefined } = {}) {
310
+ const MARGINE = 1.2;
311
+ const tetto = Number.isFinite(limiteMs) && limiteMs > 0 ? Math.round(limiteMs * MARGINE) : 0;
312
+ if (dispatcherPerLimite.has(tetto)) return dispatcherPerLimite.get(tetto);
313
+ /*
314
+ * La classe arriva dal dispatcher che Node ha già costruito per sé: è lo stesso `Agent` del suo
315
+ * undici interno, senza importarne una seconda copia. Se l'ambiente ha un proxy, quella classe è
316
+ * un `EnvHttpProxyAgent` e accetta le stesse opzioni: si eredita la forma giusta invece di
317
+ * imporne una.
318
+ */
319
+ const attuale = dispatcherGlobale ?? globalThis[Symbol.for('undici.globalDispatcher.1')];
320
+ const Costruttore = attuale?.constructor;
321
+ if (typeof Costruttore !== 'function') { dispatcherPerLimite.set(tetto, null); return null; }
322
+ let costruito = null;
323
+ try {
324
+ costruito = new Costruttore({ headersTimeout: tetto, bodyTimeout: tetto });
325
+ /* ⛔ Una prova che l'oggetto sia davvero un dispatcher: senza `dispatch` non lo è. */
326
+ if (typeof costruito?.dispatch !== 'function') costruito = null;
327
+ } catch {
328
+ costruito = null;
329
+ }
330
+ dispatcherPerLimite.set(tetto, costruito);
331
+ return costruito;
332
+ }
@@ -0,0 +1,207 @@
1
+ import { open } from 'node:fs/promises';
2
+
3
+ /*
4
+ * Fase 5, punto 4 del piano — parser minimo del formato binario GGUF.
5
+ * Spec ufficiale letta fresca prima di scrivere (regola vincolante del
6
+ * progetto), verificata byte per byte contro un file REALE già presente
7
+ * in `.local-models/` (Qwen3-0.6B.Q2_K.gguf — magic/version/tensor_count/
8
+ * metadata_kv_count/prima chiave "general.architecture" tutti confermati
9
+ * a mano con un hex dump prima di scrivere una riga di codice):
10
+ * https://github.com/ggml-org/ggml/blob/master/docs/gguf.md
11
+ *
12
+ * Layout: uint32 magic ("GGUF") · uint32 version · uint64 tensor_count ·
13
+ * uint64 metadata_kv_count · metadata_kv_count coppie (chiave stringa
14
+ * length-prefixed, uint32 tipo, valore tipato). Tutto little-endian (il
15
+ * caso reale osservato — la spec permette anche big-endian dalla v3 in
16
+ * poi, non supportato qui: un parser "minimo" dichiarato tale, non un
17
+ * mockup che finge di coprire tutto).
18
+ */
19
+
20
+ export class GgufHeaderError extends Error {
21
+ constructor(message, code = 'GGUF_HEADER_INVALID') { super(message); this.name = 'GgufHeaderError'; this.code = code; }
22
+ }
23
+ function fail(message, code = 'GGUF_HEADER_INVALID') { throw new GgufHeaderError(message, code); }
24
+
25
+ // La metadata reale osservata (inclusi vocabolari tokenizer di centinaia di
26
+ // migliaia di voci) sta ben sotto questa soglia — i TENSORI, che seguono la
27
+ // metadata e pesano i GB veri del file, restano fuori: non li leggiamo mai.
28
+ const MAX_PREFIX_BYTES = 32 * 1024 * 1024;
29
+
30
+ const VALUE_TYPE = Object.freeze({
31
+ UINT8: 0, INT8: 1, UINT16: 2, INT16: 3, UINT32: 4, INT32: 5, FLOAT32: 6,
32
+ BOOL: 7, STRING: 8, ARRAY: 9, UINT64: 10, INT64: 11, FLOAT64: 12,
33
+ });
34
+
35
+ class Cursor {
36
+ constructor(buffer) { this.buffer = buffer; this.offset = 0; }
37
+ need(bytes) { if (this.offset + bytes > this.buffer.length) fail('metadata truncated, or exceeds the read prefix', 'GGUF_HEADER_TRUNCATED'); }
38
+ bytes(count) { this.need(count); const v = this.buffer.subarray(this.offset, this.offset + count); this.offset += count; return v; }
39
+ u8() { this.need(1); const v = this.buffer.readUInt8(this.offset); this.offset += 1; return v; }
40
+ i8() { this.need(1); const v = this.buffer.readInt8(this.offset); this.offset += 1; return v; }
41
+ u16() { this.need(2); const v = this.buffer.readUInt16LE(this.offset); this.offset += 2; return v; }
42
+ i16() { this.need(2); const v = this.buffer.readInt16LE(this.offset); this.offset += 2; return v; }
43
+ u32() { this.need(4); const v = this.buffer.readUInt32LE(this.offset); this.offset += 4; return v; }
44
+ i32() { this.need(4); const v = this.buffer.readInt32LE(this.offset); this.offset += 4; return v; }
45
+ f32() { this.need(4); const v = this.buffer.readFloatLE(this.offset); this.offset += 4; return v; }
46
+ f64() { this.need(8); const v = this.buffer.readDoubleLE(this.offset); this.offset += 8; return v; }
47
+ u64() { this.need(8); const v = this.buffer.readBigUInt64LE(this.offset); this.offset += 8; return v; }
48
+ i64() { this.need(8); const v = this.buffer.readBigInt64LE(this.offset); this.offset += 8; return v; }
49
+ string() {
50
+ const len = this.u64();
51
+ if (len > BigInt(this.buffer.length)) fail('metadata string length exceeds the read prefix', 'GGUF_HEADER_TRUNCATED');
52
+ return this.bytes(Number(len)).toString('utf8');
53
+ }
54
+ }
55
+
56
+ function readTypedValue(cursor, type) {
57
+ switch (type) {
58
+ case VALUE_TYPE.UINT8: return cursor.u8();
59
+ case VALUE_TYPE.INT8: return cursor.i8();
60
+ case VALUE_TYPE.UINT16: return cursor.u16();
61
+ case VALUE_TYPE.INT16: return cursor.i16();
62
+ case VALUE_TYPE.UINT32: return cursor.u32();
63
+ case VALUE_TYPE.INT32: return cursor.i32();
64
+ case VALUE_TYPE.FLOAT32: return cursor.f32();
65
+ case VALUE_TYPE.BOOL: return cursor.u8() !== 0;
66
+ case VALUE_TYPE.STRING: return cursor.string();
67
+ case VALUE_TYPE.UINT64: return cursor.u64();
68
+ case VALUE_TYPE.INT64: return cursor.i64();
69
+ case VALUE_TYPE.FLOAT64: return cursor.f64();
70
+ case VALUE_TYPE.ARRAY: {
71
+ const elementType = cursor.u32();
72
+ const count = cursor.u64();
73
+ if (count > BigInt(cursor.buffer.length)) fail('metadata array length exceeds the read prefix', 'GGUF_HEADER_TRUNCATED');
74
+ const values = new Array(Number(count));
75
+ for (let i = 0; i < values.length; i += 1) values[i] = readTypedValue(cursor, elementType);
76
+ return values;
77
+ }
78
+ default: fail(`unknown GGUF metadata value type ${type}`, 'GGUF_HEADER_UNKNOWN_TYPE');
79
+ }
80
+ return undefined; // istruzione morta, solo per lint: fail() lancia sempre
81
+ }
82
+
83
+ function positiveSafeInteger(value, label) {
84
+ const n = typeof value === 'bigint' ? Number(value) : value;
85
+ if (!Number.isSafeInteger(n) || n <= 0) fail(`${label} is missing or not a positive integer`, 'GGUF_HEADER_METADATA_MISSING');
86
+ return n;
87
+ }
88
+
89
+ /*
90
+ * Stima dichiarata, non misurata (coerente con lo stato 'declared' che
91
+ * local-runtime-probe.mjs assegna a estimatedWorkingBytes): pesi caricati
92
+ * (≈ dimensione del file) + KV-cache stimata con la formula standard
93
+ * (2 per K+V · strati · contesto · embedding · 2 byte per valore fp16).
94
+ * ⛔ Semplificazione dichiarata: usa embedding_length INTERO anche per
95
+ * architetture con grouped-query attention (meno teste KV della
96
+ * dimensione piena) — sovrastima invece di sottostimare, appropriato per
97
+ * un controllo "ci sta?" prima di caricare: meglio dire "forse non ci
98
+ * sta" che far girare un modello che poi va in OOM davvero.
99
+ */
100
+ /**
101
+ * ⛔⛔⛔ 02/9 (sera) — la versione precedente moltiplicava per
102
+ * `embeddingLength` INTERO, cioè come se ogni testa di attenzione avesse
103
+ * la sua coppia K/V. Sulle architetture moderne non è così: la
104
+ * **grouped-query attention** condivide le teste KV fra più teste di
105
+ * query, e la cache si riduce dello stesso rapporto. Sul Qwen3 27B
106
+ * dell'owner la stima usciva **340 GB** contro 15,6 GB liberi — un
107
+ * verdetto "non compatibile" che non dice niente di utile, perché
108
+ * sbagliato di un ordine di grandezza.
109
+ *
110
+ * ⭐ Ricerca 02/9 — la formula standard è
111
+ * `2 × strati × teste_KV × head_dim × token × byte_per_valore`
112
+ * (github.com/ggml-org/llama.cpp discussion #7949,
113
+ * omrimallis.com/posts/techniques-for-kv-cache-optimization): il `2` sono
114
+ * K e V, e le teste sono quelle **KV**, non quelle di query. Esempio
115
+ * citato: Llama 3 ha 8 teste KV contro 64 di query — **8× di cache in
116
+ * meno**.
117
+ *
118
+ * Qui `head_dim = embedding_length / head_count` e la dimensione KV è
119
+ * `head_count_kv × head_dim`. ⛔ Se il file non dichiara i conteggi delle
120
+ * teste si ricade sull'`embedding_length` intero: resta una
121
+ * sovrastima, ma dichiarata — meglio "forse non ci sta" che un OOM vero.
122
+ */
123
+ function kvBytesPerToken({ blockCount, embeddingLength, headCount, headCountKv }) {
124
+ const dimensioneKv = (Number.isSafeInteger(headCount) && headCount > 0 && Number.isSafeInteger(headCountKv) && headCountKv > 0)
125
+ ? (embeddingLength / headCount) * headCountKv
126
+ : embeddingLength;
127
+ /*
128
+ * ⛔ 02/9 — `Math.ceil` non è cosmetico: su un modello reale (il Qwen3 27B
129
+ * dell'owner) `embedding_length / head_count` NON è esatto e il prodotto
130
+ * usciva frazionario — 221.866,67 byte per token. `validateHeader` in
131
+ * `local-runtime-probe.mjs` pretende interi positivi, quindi il file
132
+ * veniva rifiutato come `MODEL_HEADER_INVALID`: un modello leggibile
133
+ * dichiarato illeggibile. Si arrotonda PER ECCESSO, nella stessa
134
+ * direzione conservativa del resto della stima.
135
+ */
136
+ return Math.ceil(2 * blockCount * dimensioneKv * 2); // K+V, e 2 byte per valore (fp16)
137
+ }
138
+
139
+ /**
140
+ * Stima dichiarata, non misurata: pesi caricati (≈ dimensione del file) +
141
+ * KV-cache per il numero di token indicato.
142
+ * ⛔ `contextLength` è quello per cui si vuole la stima: chi chiama passa
143
+ * il contesto RICHIESTO dal profilo, non quello addestrato. La versione
144
+ * precedente usava sempre quello addestrato — sul 27B, 262.144 token
145
+ * invece dei 65.536 richiesti: un fattore 4 di sovrastima sopra a quello
146
+ * della GQA.
147
+ */
148
+ function estimateWorkingBytes({ fileBytes, blockCount, contextLength, embeddingLength, headCount, headCountKv }) {
149
+ return fileBytes + kvBytesPerToken({ blockCount, embeddingLength, headCount, headCountKv }) * contextLength;
150
+ }
151
+
152
+
153
+ export async function readGgufHeader(path) {
154
+ if (typeof path !== 'string' || !path) fail('path is required', 'GGUF_HEADER_MISCONFIGURED');
155
+ const handle = await open(path, 'r');
156
+ try {
157
+ const { size: fileBytes } = await handle.stat();
158
+ const prefixSize = Math.min(fileBytes, MAX_PREFIX_BYTES);
159
+ const buffer = Buffer.alloc(prefixSize);
160
+ const { bytesRead } = await handle.read(buffer, 0, prefixSize, 0);
161
+ const cursor = new Cursor(buffer.subarray(0, bytesRead));
162
+
163
+ const magic = cursor.bytes(4).toString('ascii');
164
+ const version = cursor.u32();
165
+ if (magic !== 'GGUF') fail('not a GGUF file (magic mismatch)', 'GGUF_HEADER_MAGIC_MISMATCH');
166
+ if (version !== 3) fail(`unsupported GGUF version ${version} (only 3 is supported)`, 'GGUF_HEADER_VERSION_UNSUPPORTED');
167
+ cursor.u64(); // tensor_count — non serve per questa stima, letto solo per avanzare correttamente
168
+ const metadataCount = cursor.u64();
169
+ if (metadataCount > 1_000_000n) fail('metadata_kv_count is implausibly large', 'GGUF_HEADER_INVALID');
170
+
171
+ const metadata = new Map();
172
+ for (let i = 0n; i < metadataCount; i += 1n) {
173
+ const key = cursor.string();
174
+ const type = cursor.u32();
175
+ metadata.set(key, readTypedValue(cursor, type));
176
+ }
177
+
178
+ const architecture = metadata.get('general.architecture');
179
+ if (typeof architecture !== 'string' || !architecture) fail('general.architecture metadata key is missing', 'GGUF_HEADER_ARCHITECTURE_MISSING');
180
+ const contextLength = positiveSafeInteger(metadata.get(`${architecture}.context_length`), `${architecture}.context_length`);
181
+ const embeddingLength = positiveSafeInteger(metadata.get(`${architecture}.embedding_length`), `${architecture}.embedding_length`);
182
+ const blockCount = positiveSafeInteger(metadata.get(`${architecture}.block_count`), `${architecture}.block_count`);
183
+ /*
184
+ * ⛔ I conteggi delle teste sono OPZIONALI, a differenza dei tre
185
+ * sopra: un file che non li dichiara resta leggibile e si stima con
186
+ * l'embedding intero (sovrastima dichiarata, vedi kvBytesPerToken).
187
+ * Per questo NON passano da `positiveSafeInteger`, che lancerebbe.
188
+ */
189
+ const numeroInteroOpzionale = (valore) => {
190
+ const n = typeof valore === 'bigint' ? Number(valore) : valore;
191
+ return Number.isSafeInteger(n) && n > 0 ? n : undefined;
192
+ };
193
+ const headCount = numeroInteroOpzionale(metadata.get(`${architecture}.attention.head_count`));
194
+ const headCountKv = numeroInteroOpzionale(metadata.get(`${architecture}.attention.head_count_kv`));
195
+
196
+ return {
197
+ magic,
198
+ version,
199
+ trainedContext: contextLength,
200
+ /** ⭐ 02/9 — il costo per TOKEN, così chi chiama può stimare sul contesto che gli serve invece che su quello addestrato. */
201
+ kvCacheBytesPerToken: kvBytesPerToken({ blockCount, embeddingLength, headCount, headCountKv }),
202
+ estimatedWorkingBytes: estimateWorkingBytes({ fileBytes, blockCount, contextLength, embeddingLength, headCount, headCountKv }),
203
+ };
204
+ } finally {
205
+ await handle.close();
206
+ }
207
+ }