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,629 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { EventEmitter } from 'node:events';
3
+ import { spawnSync } from 'node:child_process';
4
+ import { createServer } from 'node:net';
5
+ import { basename, dirname, isAbsolute, join } from 'node:path';
6
+ import { createProcessPolicy } from './process-policy.mjs';
7
+ import { statSync } from 'node:fs';
8
+
9
+ const LOOPBACK = '127.0.0.1';
10
+ const DEFAULT_TIMEOUT_MS = 15_000;
11
+ /*
12
+ * ⛔⛔⛔ 03/9 — «PERCHE' CAZZO NE DEVI USARE UNO DA 600 MILIONI?».
13
+ *
14
+ * L'owner aveva scaricato un 27B da 15,7 GB e io usavo un 0.6B, perche' il
15
+ * 27B «non partiva»: la rotta di caricamento rispondeva INTERNAL_ERROR dopo
16
+ * 15 secondi netti. Avevo creduto alla mia stima di memoria e non ho chiesto
17
+ * il motivo vero.
18
+ *
19
+ * Il motivo vero, misurato lanciando llama-server a mano: il modello STA
20
+ * CARICANDO — oltre due minuti per leggere 15,7 GB dal disco — e siamo noi ad
21
+ * abbandonarlo dopo 15 secondi. Non era «troppo grande»: era un'attesa
22
+ * tarata su modelli piccoli.
23
+ *
24
+ * ⇒ L'attesa si commisura ai BYTE del modello: quindici secondi di base piu'
25
+ * quindici per gigabyte. Sul 27B fanno ~4 minuti, sul 0.6B restano i 15
26
+ * secondi di sempre. ⛔ Non una costante piu' grande per tutti: un modello
27
+ * piccolo che non parte deve continuare a dirlo subito, non far aspettare
28
+ * quattro minuti per scoprire che il binario e' rotto.
29
+ */
30
+ const ATTESA_PER_GIGABYTE_MS = 15_000;
31
+ export function attesaSaluteMs(byteModello, base = DEFAULT_TIMEOUT_MS) {
32
+ const gb = Number.isFinite(byteModello) && byteModello > 0 ? byteModello / 1_000_000_000 : 0;
33
+ return Math.round(base + gb * ATTESA_PER_GIGABYTE_MS);
34
+ }
35
+ const DEFAULT_POLL_MS = 100;
36
+
37
+ /*
38
+ * ⭐⭐⭐ BC-13, 12/09/2026 — LE LEVE DI VELOCITÀ NON SI SCRIVONO A MANO: SI
39
+ * CHIEDONO AL BINARIO.
40
+ *
41
+ * Owner 12/09: «bisogna fare una ricerca delle ultime tecnologie e metodi
42
+ * all'avanguardia, dobbiamo rendere il motore di llm locale estremamente
43
+ * rapido e meglio dei competitor». E vincolo dell'11/09, che vale su tutto
44
+ * quello che segue: «NESSUN MODELLO PREDEFINITO: motore ottimizzato a
45
+ * livello UNIVERSALE, non forziamo nulla, sarà l'utente a decidere».
46
+ *
47
+ * ⇒ Nessuna delle due leve qui sotto nomina un modello, una scheda o un
48
+ * numero magico: ognuna fa una DOMANDA al binario che l'utente ha, e usa la
49
+ * risposta. Se il binario non sa rispondere, si resta esattamente al
50
+ * comportamento di prima.
51
+ */
52
+ const TIMEOUT_SONDA_MS = 30_000;
53
+
54
+ /**
55
+ * Il fitter ufficiale di llama.cpp (`llama-fit-params`, lo stesso codice che
56
+ * il server usa con `--fit on`) stampa su stdout gli argomenti che ENTRANO
57
+ * nella memoria del dispositivo, per esempio `-c 16384 -ngl -1` oppure
58
+ * `-c 16384 -ngl 56`.
59
+ *
60
+ * ⛔ `-1` (o `all`) è l'unico esito che dice «ci sta TUTTO». Un numero è già
61
+ * una resa parziale: qualche livello resterebbe fuori dalla scheda.
62
+ */
63
+ export function leggiNglDalFitter(stdout) {
64
+ if (typeof stdout !== 'string') return null;
65
+ const trovato = /-ngl\s+(-?\d+|all|auto)/u.exec(stdout);
66
+ if (!trovato) return null;
67
+ const valore = trovato[1];
68
+ if (valore === 'all' || valore === '-1') return 'tutto';
69
+ if (valore === 'auto') return null;
70
+ const numero = Number(valore);
71
+ return Number.isInteger(numero) ? numero : null;
72
+ }
73
+
74
+ /**
75
+ * ⭐⭐⭐ MISURATO 12/09/2026 sul banco (Qwen3-4B-Q4_K_M, RX 9070 XT Vulkan,
76
+ * preambolo di 11.065 token, 3 ripetizioni, mediane):
77
+ *
78
+ * KV cache q8_0 (quello che passavamo SEMPRE): prompt 3.964 ms · gen 127,6 t/s
79
+ * KV cache f16 (il predefinito del binario): prompt 2.849 ms · gen 117,5 t/s
80
+ *
81
+ * Cioè: quantizzare la KV cache costa **+39% sul tempo al primo token** e
82
+ * regala +8,6% in generazione. Su un preambolo d'agente da 11.000 token il
83
+ * primo token è quello che la persona aspetta — 1,1 secondi in più, ogni
84
+ * volta che la cache non prende. Sul compito «ricopia» la forbice è ancora
85
+ * più larga: 4.212 ms contro 2.969 (−29,5%), e il turno successivo 289 ms
86
+ * contro 179 (−38,1%).
87
+ *
88
+ * ⛔ La misura del 03/09 che aveva introdotto q8_0 («+408% in elaborazione
89
+ * del prompt») era stata presa sul modello giocattolo da 0,6B Q2_K, dove la
90
+ * KV cache è minuscola: su un modello vero il verso si ROVESCIA. Non è che
91
+ * quella misura fosse sbagliata — è che non parlava di questo caso.
92
+ *
93
+ * ⛔ E q8_0 NON è inutile: dimezza la cache. Sullo stesso 4B a 131.072 token
94
+ * di contesto il fitter risponde `-ngl 24` con f16 (24 livelli su GPU, il
95
+ * resto sul processore = disastro) e `-ngl -1` con q8_0 (tutto sulla
96
+ * scheda). ⇒ La scelta NON è una preferenza, è una MISURA che cambia da
97
+ * modello a modello e da contesto a contesto.
98
+ *
99
+ * ⇒ Regola universale: f16 se con f16 ci sta tutto; altrimenti q8_0.
100
+ * Se non si riesce a chiedere, q8_0 — cioè il comportamento di ieri.
101
+ *
102
+ * @returns {{tipo: 'f16'|'q8_0', perche: string}}
103
+ */
104
+ export function decidiTipoKvCache({ nglConF16, nglConQ8 } = {}) {
105
+ if (nglConF16 === 'tutto') {
106
+ return { tipo: 'f16', perche: 'con KV f16 il fitter del binario dichiara che TUTTI i livelli entrano nel dispositivo' };
107
+ }
108
+ if (nglConF16 === null || nglConF16 === undefined) {
109
+ return { tipo: 'q8_0', perche: 'il fitter del binario non ha risposto: si resta sulla KV quantizzata di prima' };
110
+ }
111
+ if (nglConQ8 === 'tutto') {
112
+ return { tipo: 'q8_0', perche: `con KV f16 entrerebbero solo ${nglConF16} livelli, con q8_0 entrano tutti` };
113
+ }
114
+ return { tipo: 'q8_0', perche: `con KV f16 entrerebbero solo ${nglConF16} livelli: la cache dimezzata ne fa entrare di più` };
115
+ }
116
+
117
+ /**
118
+ * ⭐⭐⭐ MISURATO 12/09/2026, stesso banco, compito «ricopia alla lettera un
119
+ * passaggio del contesto» — cioè il lavoro vero di un agente: rimettere
120
+ * fuori un risultato d'attrezzo, riscrivere un file che ha appena letto.
121
+ * Generazione, mediane su 3 ripetizioni:
122
+ *
123
+ * senza 123,5 token/s
124
+ * --spec-type ngram-simple 158,4 token/s (+28%)
125
+ * --spec-type ngram-cache 202,3 token/s (+64%)
126
+ * --spec-type ngram-mod 353,2 token/s (+186%, cioè 2,9×)
127
+ * ngram-mod + KV f16 445,5 token/s (+261% sulla riga di oggi)
128
+ *
129
+ * ⭐ Perché conta più di quanto sembri: la decodifica speculativa classica
130
+ * vuole un SECONDO modello (il «draft»). LM Studio la offre solo così — la
131
+ * sua documentazione dice che «relies on the collaboration of two models»
132
+ * (lmstudio.ai/docs/app/advanced/speculative-decoding, letta il 12/09/2026);
133
+ * Ollama non la offre affatto (docs.ollama.com/faq, 12/09/2026). Le varianti
134
+ * `ngram-*` di llama.cpp NON vogliono nessun secondo modello: pescano i
135
+ * candidati dal contesto già presente. ⇒ È l'unica forma compatibile col
136
+ * vincolo «nessun modello predefinito», e vale per un GGUF qualunque.
137
+ *
138
+ * ⛔ IL VERSO CONTRARIO, misurato e non supposto: quando nel contesto non
139
+ * c'è niente da pescare la leva COSTA. Prima chiamata di un server appena
140
+ * acceso, caso peggiore osservato 110,7 token/s contro 123,7 (−10,5%); sul
141
+ * compito «riassumi» (testo nuovo) prima chiamata 124,7 contro 127,6
142
+ * (−2,3%). Il costo si paga una volta e si ripaga dalla seconda chiamata in
143
+ * poi: nella stessa sessione la mediana sale a 353. Un harness fa decine di
144
+ * chiamate per giro, non una.
145
+ *
146
+ * ⛔ Si accende SOLO se questo binario la offre davvero: `--spec-type` è
147
+ * comparso da poco e un binario più vecchio morirebbe all'avvio con
148
+ * «unknown argument». Non si suppone: si legge il suo `--help`.
149
+ */
150
+ export function supportaSpeculativaNgram(testoAiuto) {
151
+ if (typeof testoAiuto !== 'string' || testoAiuto === '') return false;
152
+ const riga = /--spec-type[^\n]*/u.exec(testoAiuto);
153
+ return Boolean(riga && riga[0].includes('ngram-mod'));
154
+ }
155
+
156
+ /**
157
+ * Sonda sincrona e volutamente povera: un eseguibile ACCANTO al binario del
158
+ * server, nessuna shell, un tetto di tempo, e un `catch` che non nasconde
159
+ * niente perché chi chiama tratta `null` come «non lo so».
160
+ */
161
+ function creaSondaBinario(spawnSyncImpl = spawnSync) {
162
+ return function sonda(eseguibile, argomenti, timeoutMs = TIMEOUT_SONDA_MS) {
163
+ try {
164
+ const esito = spawnSyncImpl(eseguibile, argomenti, {
165
+ shell: false, windowsHide: true, encoding: 'utf8', timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024,
166
+ });
167
+ if (!esito || esito.error) return null;
168
+ return `${esito.stdout ?? ''}\n${esito.stderr ?? ''}`;
169
+ } catch {
170
+ return null;
171
+ }
172
+ };
173
+ }
174
+
175
+ /** `llama-fit-params` sta nella stessa cartella di `llama-server`, con la stessa estensione. */
176
+ export function percorsoFitter(binaryPath) {
177
+ if (typeof binaryPath !== 'string' || binaryPath.trim() === '') return null;
178
+ const nome = basename(binaryPath);
179
+ const sostituito = nome.replace(/llama-server/iu, 'llama-fit-params');
180
+ if (sostituito === nome) return null;
181
+ return join(dirname(binaryPath), sostituito);
182
+ }
183
+
184
+ export class LlamaServerSupervisorError extends Error {
185
+ constructor(message, code = 'RUNTIME_FAILED') {
186
+ super(message);
187
+ this.name = 'LlamaServerSupervisorError';
188
+ this.code = code;
189
+ }
190
+ }
191
+
192
+ function invalid(message) {
193
+ return new LlamaServerSupervisorError(message, 'RUNTIME_INVALID');
194
+ }
195
+
196
+ async function allocatePort() {
197
+ const server = createServer();
198
+ await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, LOOPBACK, resolve); });
199
+ const port = server.address().port;
200
+ await new Promise((resolve) => server.close(resolve));
201
+ return port;
202
+ }
203
+
204
+ function wait(ms) {
205
+ return new Promise((resolve) => setTimeout(resolve, ms));
206
+ }
207
+
208
+ export function createLlamaServerSupervisor({
209
+ binaryPath,
210
+ // R-03: il binario CPU di riserva e la descrizione del motore scelto dal guscio.
211
+ fallbackBinaryPath = null,
212
+ motore: motoreDichiarato = null,
213
+ modelStore = null,
214
+ spawnImpl,
215
+ fetchImpl = fetch,
216
+ portAllocator = allocatePort,
217
+ now = () => new Date(),
218
+ healthTimeoutMs = DEFAULT_TIMEOUT_MS,
219
+ /**
220
+ * Quanti livelli mandare sulla GPU. ⛔ `0` (predefinito) = nessuno, cioè il
221
+ * comportamento di sempre: una build senza backend accetterebbe `-ngl` e lo
222
+ * ignorerebbe in silenzio, facendoci credere di usare una scheda che non
223
+ * stiamo toccando.
224
+ */
225
+ gpuLayers = 0,
226
+ pollIntervalMs = DEFAULT_POLL_MS,
227
+ /**
228
+ * ⭐ BC-13 — come si INTERROGA il binario (il suo `--help`, il suo fitter).
229
+ * Iniettabile perché nessun test debba avviare un processo vero, e perché
230
+ * la prova al verso contrario («il binario NON offre la leva») si possa
231
+ * scrivere senza procurarsi un binario vecchio.
232
+ */
233
+ sondaBinario = creaSondaBinario(),
234
+ /**
235
+ * ⛔ L'interruttore della speculativa, e il motivo per cui esiste: il
236
+ * difetto aperto ggml-org/llama.cpp#25819 — «server : add stuck-loop escape
237
+ * for ngram-mod (WIP)», aperto il 17/07/2026 e ancora aperto al 12/09/2026
238
+ * — descrive un ciclo che non esce quando la verifica dei candidati
239
+ * fallisce ripetutamente. Sul banco del 12/09 non si è mai presentato (3
240
+ * ripetizioni × 2 compiti × 2 modelli di cache, uscite identiche byte per
241
+ * byte alla riga senza speculativa su 3 domande su 3), ma un difetto
242
+ * aperto a monte si spegne da UN posto, non riscrivendo il codice.
243
+ * `'auto'` = si accende se il binario la offre; `'off'` = mai.
244
+ */
245
+ speculativaNgram = 'auto',
246
+ } = {}) {
247
+ if (typeof binaryPath !== 'string' || binaryPath.trim() === '') throw new LlamaServerSupervisorError('binaryPath is required', 'RUNTIME_MISCONFIGURED');
248
+ if (fallbackBinaryPath !== null && (typeof fallbackBinaryPath !== 'string' || fallbackBinaryPath.trim() === '')) throw new LlamaServerSupervisorError('fallbackBinaryPath must be a non-empty string', 'RUNTIME_MISCONFIGURED');
249
+ const processPolicy = createProcessPolicy({ allowedExecutables: [binaryPath, ...(fallbackBinaryPath ? [fallbackBinaryPath] : [])], spawnFn: spawnImpl });
250
+ let current = null;
251
+ let state = 'unavailable';
252
+ const listeners = new Set();
253
+ /* Le risposte del binario non cambiano fra un avvio e l'altro: si pagano
254
+ * una volta sola. Il fitter dipende anche dal modello e dal contesto.
255
+ * R-03: si ricordano PER BINARIO — la riserva CPU non offre le stesse cose. */
256
+ const aiutoPerBinario = new Map();
257
+ const leveMemorizzate = new Map();
258
+ const motoreIniziale = Object.freeze({
259
+ variante: motoreDichiarato?.variante ?? (/vulkan/iu.test(basename(binaryPath)) ? 'vulkan' : 'cpu'),
260
+ dispositivi: Array.isArray(motoreDichiarato?.dispositivi) ? [...motoreDichiarato.dispositivi] : [],
261
+ });
262
+ let motore = { variante: motoreIniziale.variante, dispositivi: [...motoreIniziale.dispositivi], ripiego: null, proposta: null };
263
+
264
+ function speculativaDisponibile(binario = binaryPath) {
265
+ if (speculativaNgram === 'off') return false;
266
+ if (!aiutoPerBinario.has(binario)) aiutoPerBinario.set(binario, sondaBinario(binario, ['--help'], 10_000));
267
+ return supportaSpeculativaNgram(aiutoPerBinario.get(binario) ?? '');
268
+ }
269
+
270
+ /**
271
+ * ⛔ Si chiede al fitter SOLO quando stiamo davvero offloadando: su una
272
+ * build senza backend la domanda «ci sta nella scheda?» non ha oggetto, e
273
+ * nessuno dei due argomenti verrebbe passato comunque.
274
+ */
275
+ function leveVelocita(binario, modelPath, contextLength) {
276
+ const chiave = `${binario}|${modelPath}|${contextLength ?? ''}`;
277
+ if (leveMemorizzate.has(chiave)) return leveMemorizzate.get(chiave);
278
+ const fitter = percorsoFitter(binario);
279
+ const contesto = Number.isInteger(contextLength) && contextLength > 0 ? ['-c', String(contextLength)] : [];
280
+ let kv = { tipo: 'q8_0', perche: 'il fitter del binario non è stato trovato: si resta sulla KV quantizzata di prima' };
281
+ if (fitter) {
282
+ const conF16 = leggiNglDalFitter(sondaBinario(fitter, ['-m', modelPath, ...contesto, '-fa', '1']) ?? '');
283
+ const conQ8 = conF16 === 'tutto'
284
+ ? null
285
+ : leggiNglDalFitter(sondaBinario(fitter, ['-m', modelPath, ...contesto, '-fa', '1', '-ctk', 'q8_0', '-ctv', 'q8_0']) ?? '');
286
+ kv = decidiTipoKvCache({ nglConF16: conF16, nglConQ8: conQ8 });
287
+ }
288
+ const leve = { kv, speculativa: speculativaDisponibile(binario) };
289
+ leveMemorizzate.set(chiave, leve);
290
+ return leve;
291
+ }
292
+
293
+ function status() {
294
+ // R-03: `motore` esce sempre (anche a riposo o dopo un guasto): variante, dispositivi,
295
+ // l'eventuale ripiego avvenuto in questo caricamento e la proposta per la persona.
296
+ const fotoMotore = { variante: motore.variante, dispositivi: [...motore.dispositivi], ripiego: motore.ripiego ? { ...motore.ripiego } : null, proposta: motore.proposta ? { ...motore.proposta } : null };
297
+ if (!current) return { state, runtimeId: 'llama.cpp', motore: fotoMotore, observedAt: now().toISOString() };
298
+ return {
299
+ state: current.state,
300
+ runtimeId: 'llama.cpp',
301
+ motore: fotoMotore,
302
+ port: current.port,
303
+ baseUrl: current.baseUrl,
304
+ /*
305
+ * ⛔ 02/9 — QUALE modello è caricato, non solo CHE ce n'è uno.
306
+ * `current.modelId` era già tracciato dall'avvio (è lo stesso valore
307
+ * che finisce in `--alias`), ma non usciva da qui: chi chiedeva lo
308
+ * stato sapeva che un runtime era pronto e non di chi fosse. Da lì
309
+ * nasceva il difetto curato in `local-runtime-probe.mjs` — l'`n_ctx`
310
+ * del modello caricato attribuito a QUALUNQUE modello ispezionato.
311
+ */
312
+ modelId: current.modelId ?? null,
313
+ observedAt: now().toISOString(),
314
+ };
315
+ }
316
+
317
+ function emitLog(stream, chunk) {
318
+ const text = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk);
319
+ for (const listener of listeners) {
320
+ try { listener({ stream, text }); } catch { /* observer failure must not affect the process */ }
321
+ }
322
+ }
323
+
324
+ function attachProcess(entry) {
325
+ const onClose = () => {
326
+ // R-03: l'ultima riga senza a-capo (il motore muore a metà frase) si tiene lo stesso.
327
+ if (entry.residuoStderr && entry.residuoStderr.trim() !== '') {
328
+ entry.ultimeRighe.push(entry.residuoStderr.trim());
329
+ if (entry.ultimeRighe.length > 12) entry.ultimeRighe.shift();
330
+ }
331
+ entry.residuoStderr = '';
332
+ entry.closed = true;
333
+ if (current === entry && entry.state !== 'stopping') {
334
+ entry.state = 'failed';
335
+ state = 'failed';
336
+ }
337
+ };
338
+ const onError = (error) => {
339
+ entry.failure = error;
340
+ entry.state = 'failed';
341
+ state = 'failed';
342
+ };
343
+ entry.child.once('close', onClose);
344
+ entry.child.once('error', onError);
345
+ entry.child.stdout?.on('data', (chunk) => emitLog('stdout', chunk));
346
+ entry.child.stderr?.on('data', (chunk) => {
347
+ /*
348
+ * ⛔ BC-13 — le ultime righe si TENGONO, non solo si trasmettono. Sono
349
+ * l'unica cosa che spiega perché un motore non è partito, e finora
350
+ * uscivano solo verso chi si era iscritto ai log: chi riceveva
351
+ * l'eccezione leggeva «timeout» e andava a cercare un guasto che non
352
+ * c'era.
353
+ */
354
+ const testo = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk);
355
+ /*
356
+ * ⛔ R-03, 13/09 — i pezzi arrivano SPEZZATI («No d» + «evices found.»): una
357
+ * riga si chiude solo all'a-capo, altrimenti la firma del guasto non si
358
+ * riconosce. Il residuo senza a-capo si tiene fino al prossimo pezzo o alla
359
+ * chiusura del processo (vedi onClose).
360
+ */
361
+ entry.residuoStderr = (entry.residuoStderr ?? '') + testo;
362
+ const parti = entry.residuoStderr.split('\n');
363
+ entry.residuoStderr = parti.pop();
364
+ for (const riga of parti) {
365
+ if (riga.trim() === '') continue;
366
+ entry.ultimeRighe.push(riga.trim());
367
+ if (entry.ultimeRighe.length > 12) entry.ultimeRighe.shift();
368
+ }
369
+ emitLog('stderr', chunk);
370
+ });
371
+ }
372
+
373
+ async function health() {
374
+ if (!current) return { ok: false, status: 0, code: 'RUNTIME_UNREACHABLE' };
375
+ try {
376
+ const response = await fetchImpl(`${current.baseUrl}/health`, {
377
+ headers: { Accept: 'application/json', Authorization: `Bearer ${current.apiKey}` },
378
+ });
379
+ return { ok: response.ok, status: response.status };
380
+ } catch {
381
+ return { ok: false, status: 0, code: 'RUNTIME_UNREACHABLE' };
382
+ }
383
+ }
384
+
385
+ // Internal authenticated transport. The bearer token never leaves this
386
+ // module through status() or logs; runtimes receive only this capability.
387
+ async function request(path, options = {}) {
388
+ if (typeof path !== 'string' || !path.startsWith('/') || path.includes('://')) {
389
+ throw invalid('runtime request path must be relative');
390
+ }
391
+ if (!current || current.state !== 'ready') throw new LlamaServerSupervisorError('runtime is not ready', 'RUNTIME_NOT_READY');
392
+ const headers = new Headers(options.headers ?? {});
393
+ if (!headers.has('Accept')) headers.set('Accept', 'application/json');
394
+ headers.set('Authorization', `Bearer ${current.apiKey}`);
395
+ return fetchImpl(`${current.baseUrl}${path}`, { ...options, headers });
396
+ }
397
+
398
+ /**
399
+ * @param {{modelId?: string, modelPath: string, port?: number, contextLength?: number}} opzioni
400
+ * `contextLength` — ⛔ QUANTI TOKEN chiedere al motore. Vedi il commento
401
+ * sopra `-c` più sotto: senza, llama.cpp prova ad allocare il contesto
402
+ * ADDESTRATO, e su un modello grande non ci sta in nessuna macchina.
403
+ */
404
+ /*
405
+ * ⭐⭐⭐ R-03, 13/09/2026 — IL MOTORE SI SCEGLIE DALLA MACCHINA, E SE LA SCHEDA
406
+ * NON C'È SI RIPIEGA SUL PROCESSORE.
407
+ *
408
+ * Il guscio sceglie la build Vulkan solo se `--list-devices` elenca un
409
+ * dispositivo (`desktop/runtime.mjs`), ma una scheda può mancare o sparire
410
+ * DOPO: driver assente («ggml_vulkan: No devices found», «ErrorIncompatibleDriver»)
411
+ * o dispositivo perso («vk::DeviceLostError», «ErrorDeviceLost»). In quei due
412
+ * casi, e solo in quelli, il caricamento riparte UNA volta sul binario CPU di
413
+ * riserva (`fallbackBinaryPath`, `-ngl 0 --device none`, senza KV quantizzata né
414
+ * speculativa se quel binario non la offre), e lo stato lo dice:
415
+ * `motore.ripiego = { da, a, motivo }`.
416
+ *
417
+ * ⛔ NON si ripiega su «ErrorOutOfDeviceMemory»: la scheda c'è, è il modello che
418
+ * non entra (llama.cpp #15054, #5848, #9271, letti il 13/09/2026); sul
419
+ * processore girerebbe ma lentissimo, e la scelta spetta alla persona:
420
+ * `motore.proposta = { a: 'cpu', motivo }`. Né su un errore generico (file GGUF
421
+ * rotto, argomento sconosciuto): cambiare binario non lo curerebbe.
422
+ * ⛔ Un processo morto DOPO essere diventato pronto non riparte da solo (come
423
+ * prima); il prossimo `start()` ritenta sempre dal binario scelto dal guscio.
424
+ * Firme prese da ggml-vulkan.cpp al pin b10517 (elenco chiuso: un regex largo
425
+ * scambierebbe un avviso per un guasto). Fonti nel rapporto R-03.
426
+ */
427
+ const FIRME_RIPIEGO = Object.freeze([
428
+ { classe: 'driver', motivo: 'la scheda grafica non è disponibile (driver Vulkan assente o incompatibile)', firme: ['ggml_vulkan: No devices found', 'ErrorIncompatibleDriver', 'ErrorInitializationFailed', 'ErrorLayerNotPresent'] },
429
+ { classe: 'perso', motivo: 'la scheda grafica non risponde più (dispositivo perso)', firme: ['DeviceLostError', 'ErrorDeviceLost', 'device lost'] },
430
+ ]);
431
+ const FIRME_MEMORIA = Object.freeze(['ErrorOutOfDeviceMemory', 'ErrorOutOfHostMemory']);
432
+
433
+ function classificaGuastoVulkan(righe) {
434
+ const testo = righe.join('\n');
435
+ for (const f of FIRME_RIPIEGO) if (f.firme.some(s => testo.includes(s))) return { classe: f.classe, motivo: f.motivo };
436
+ if (FIRME_MEMORIA.some(s => testo.includes(s))) return { classe: 'memoria', motivo: 'la memoria della scheda grafica non basta per questo modello con questo contesto' };
437
+ return null;
438
+ }
439
+
440
+ function nuovoMotore(variante) {
441
+ return { variante, dispositivi: variante === motoreIniziale.variante ? [...motoreIniziale.dispositivi] : [], ripiego: null, proposta: null };
442
+ }
443
+
444
+ async function lanciaProcesso({ modelId, modelPath, selectedPort, contextLength, binario, ngl, apiKey }) {
445
+ const entry = {
446
+ child: null,
447
+ apiKey,
448
+ modelId: modelId ?? null,
449
+ modelPath,
450
+ port: selectedPort,
451
+ baseUrl: `http://${LOOPBACK}:${selectedPort}`,
452
+ state: 'loading',
453
+ startedAt: now().toISOString(),
454
+ failure: null,
455
+ closed: false,
456
+ ultimeRighe: [],
457
+ residuoStderr: '',
458
+ binario,
459
+ };
460
+ current = entry;
461
+ state = 'loading';
462
+ const conOffload = Number.isInteger(ngl) && ngl > 0;
463
+ /*
464
+ * ⛔ Le domande al binario si fanno PRIMA di accenderlo, e il loro esito
465
+ * si dice ad alta voce: una leva che si accende in silenzio è una leva
466
+ * che nessuno può smentire. Misurato 12/09: il fitter risponde in 0,34 s
467
+ * su un modello da 2,3 GB e in 3,9 s su uno da 15,3 GB; `--help` in
468
+ * meno di 0,3 s; e si pagano una volta sola per (binario, modello, contesto).
469
+ */
470
+ const leve = conOffload
471
+ ? leveVelocita(binario, modelPath, contextLength)
472
+ : { kv: { tipo: 'q8_0', perche: 'nessun offload sul dispositivo: la KV cache non entra nella scelta' }, speculativa: speculativaDisponibile(binario) };
473
+ emitLog('stderr', `[talos] motore ${motore.variante}${motore.ripiego ? ` (ripiego da ${motore.ripiego.da})` : ''}: ${binario}\n`);
474
+ emitLog('stderr', `[talos] KV cache ${leve.kv.tipo} — ${leve.kv.perche}\n`);
475
+ emitLog('stderr', `[talos] decodifica speculativa a n-grammi: ${leve.speculativa ? 'accesa (--spec-type ngram-mod)' : 'non offerta da questo binario'}\n`);
476
+ entry.child = processPolicy.spawn(binario, [
477
+ '-m', modelPath,
478
+ ...(modelId ? ['--alias', modelId] : []),
479
+ '--host', LOOPBACK,
480
+ '--port', String(selectedPort),
481
+ '--api-key', apiKey,
482
+ /*
483
+ * ⛔⛔⛔ 03/9 — QUI NON PASSAVAMO MAI `-c`, e il 27B dell'owner non
484
+ * partiva: senza `-c` llama.cpp alloca il contesto ADDESTRATO del modello
485
+ * (262.144 token per quel file). Misurato: lo stesso file con `-c 2048`
486
+ * dice «model loaded / listening» in 13 secondi. Storia intera nel
487
+ * commit c89dc763 e seguenti.
488
+ */
489
+ ...(Number.isInteger(contextLength) && contextLength > 0 ? ['-c', String(contextLength)] : []),
490
+ /*
491
+ * ⭐⭐⭐ 03/9 — LA GPU: `-ngl 99` esplicito (misurato meglio di `auto` su
492
+ * RX 9070 XT, banco A/B del 03/9: 13,28 contro 11,12 tok/s). Solo se il
493
+ * binario ha davvero un backend. R-03: sul binario di RISERVA si dichiara
494
+ * `-ngl 0 --device none` (llama.cpp b10517, common/arg.cpp: «none = don't
495
+ * use»), così un ripiego non tenta mai un offload silenzioso.
496
+ */
497
+ ...(conOffload ? ['-ngl', String(ngl)] : (motore.ripiego ? ['-ngl', '0', '--device', 'none'] : [])),
498
+ /*
499
+ * ⭐⭐⭐ 3/9 + BC-13 12/09 — KV cache quantizzata SIMMETRICA con `-fa 1`
500
+ * solo con offload (+15 % generazione, +408 % prompt, misurati); il tipo
501
+ * lo decide il fitter del binario (vedi `decidiTipoKvCache`).
502
+ */
503
+ ...(conOffload
504
+ ? ['-fa', '1', '--cache-type-k', leve.kv.tipo, '--cache-type-v', leve.kv.tipo]
505
+ : []),
506
+ /*
507
+ * ⭐⭐⭐ BC-13 — decodifica speculativa a n-grammi, SENZA modello draft,
508
+ * solo se QUESTO binario la offre (un binario più vecchio morirebbe con
509
+ * «unknown argument»). Misure nel commento di `supportaSpeculativaNgram`.
510
+ */
511
+ ...(leve.speculativa ? ['--spec-type', 'ngram-mod'] : []),
512
+ '--jinja',
513
+ '--metrics',
514
+ '--props',
515
+ ], { cwd: isAbsolute(binario) ? dirname(binario) : process.cwd(), shell: false, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
516
+ if (!entry.child || typeof entry.child.once !== 'function') throw new LlamaServerSupervisorError('spawn did not return a child process', 'RUNTIME_PROCESS_FAILED');
517
+ attachProcess(entry);
518
+ /*
519
+ * ⛔ L'attesa la decide la DIMENSIONE del file, letta adesso dal disco:
520
+ * un modello che il sistema deve ancora leggere non e' un modello che
521
+ * non parte. Se la misura non riesce si resta sull'attesa di base.
522
+ */
523
+ let byteModello = 0;
524
+ try { byteModello = statSync(modelPath).size; } catch { byteModello = 0; }
525
+ const attesa = attesaSaluteMs(byteModello, healthTimeoutMs);
526
+ const deadline = Date.now() + attesa;
527
+ while (Date.now() < deadline) {
528
+ if (entry.failure) throw new LlamaServerSupervisorError(`llama-server failed: ${entry.failure.message}`, 'RUNTIME_PROCESS_FAILED');
529
+ /*
530
+ * ⛔⛔⛔ BC-13, 12/09/2026 — UN PROCESSO GIÀ MORTO NON DIVENTA PRONTO:
531
+ * il 27B dell'owner muore in 4,9 s con «ErrorOutOfDeviceMemory» e prima
532
+ * si aspettava 245 s bussando a una porta chiusa. Si guarda `closed`, e
533
+ * l'errore PORTA le ultime righe del motore.
534
+ */
535
+ if (entry.closed) {
536
+ const detto = entry.ultimeRighe.slice(-4).join(' | ');
537
+ const errore = new LlamaServerSupervisorError(
538
+ `llama-server si è chiuso dopo ${Math.round((Date.now() - (deadline - attesa)) / 1000)} s senza mai diventare pronto${detto ? `: ${detto}` : ''}`,
539
+ 'RUNTIME_PROCESS_FAILED',
540
+ );
541
+ errore.righeMotore = [...entry.ultimeRighe];
542
+ throw errore;
543
+ }
544
+ const result = await health();
545
+ if (result.ok) {
546
+ entry.state = 'ready';
547
+ state = 'ready';
548
+ return status();
549
+ }
550
+ await wait(pollIntervalMs);
551
+ }
552
+ entry.state = 'failed';
553
+ state = 'failed';
554
+ // ⛔ Il messaggio dice QUANTO si e' aspettato e quanto pesa il modello:
555
+ // «timeout» da solo manda a cercare un guasto che non c'e'.
556
+ throw new LlamaServerSupervisorError(`llama-server non è diventato pronto entro ${Math.round(attesa / 1000)} s (modello di ${(byteModello / 1_000_000_000).toFixed(1)} GB)`, 'RUNTIME_HEALTH_TIMEOUT');
557
+ }
558
+
559
+ async function start({ modelId, modelPath, port, contextLength } = {}) {
560
+ if (current && ['loading', 'ready', 'stopping'].includes(current.state)) throw new LlamaServerSupervisorError('runtime is already active', 'RUNTIME_ALREADY_RUNNING');
561
+ if (typeof modelPath !== 'string' || !isAbsolute(modelPath)) throw invalid('modelPath must be absolute');
562
+ const selectedPort = port ?? await portAllocator();
563
+ if (!Number.isInteger(selectedPort) || selectedPort < 1024 || selectedPort > 65535) throw invalid('port is invalid');
564
+ let locked = false;
565
+ if (modelStore && modelId) {
566
+ await modelStore.lock(modelId);
567
+ locked = true;
568
+ }
569
+ const apiKey = randomBytes(32).toString('hex');
570
+ // Ogni caricamento riparte dal binario scelto dal guscio: il ripiego vale per un giro solo.
571
+ motore = nuovoMotore(motoreIniziale.variante);
572
+ const chiudi = (entry) => { if (entry?.child && !entry.closed) entry.child.kill('SIGTERM'); };
573
+ try {
574
+ try {
575
+ return await lanciaProcesso({ modelId, modelPath, selectedPort, contextLength, binario: binaryPath, ngl: gpuLayers, apiKey });
576
+ } catch (primo) {
577
+ const guasto = motore.variante === 'vulkan' && primo.code === 'RUNTIME_PROCESS_FAILED' ? classificaGuastoVulkan(primo.righeMotore ?? []) : null;
578
+ if (guasto?.classe === 'memoria') {
579
+ motore.proposta = { a: 'cpu', motivo: `${guasto.motivo}; sul processore il modello può girare, più lento: la scelta è della persona` };
580
+ throw primo;
581
+ }
582
+ if (!guasto || !fallbackBinaryPath) throw primo;
583
+ const primaEntry = current;
584
+ chiudi(primaEntry);
585
+ if (current === primaEntry) current = null;
586
+ const ripiego = { da: motore.variante, a: 'cpu', motivo: guasto.motivo, classe: guasto.classe, righe: (primo.righeMotore ?? []).slice(-4) };
587
+ motore = { ...nuovoMotore('cpu'), ripiego };
588
+ for (const listener of listeners) {
589
+ try { listener({ stream: 'stderr', text: `[talos] ${guasto.motivo}: il modello viene caricato sul processore.\n`, ripiego }); } catch { /* observer failure must not affect the process */ }
590
+ }
591
+ try {
592
+ return await lanciaProcesso({ modelId, modelPath, selectedPort, contextLength, binario: fallbackBinaryPath, ngl: 0, apiKey });
593
+ } catch (secondo) {
594
+ // ⛔ Le DUE code restano leggibili: chi legge deve sapere che sono morti entrambi, e come.
595
+ const codaGpu = ripiego.righe.join(' | ');
596
+ const errore = new LlamaServerSupervisorError(`${secondo.message}${codaGpu ? ` [prima, sulla scheda grafica: ${codaGpu}]` : ''}`, secondo.code === 'RUNTIME_HEALTH_TIMEOUT' ? 'RUNTIME_HEALTH_TIMEOUT' : 'RUNTIME_PROCESS_FAILED');
597
+ errore.righeMotore = secondo.righeMotore;
598
+ throw errore;
599
+ }
600
+ }
601
+ } catch (error) {
602
+ chiudi(current);
603
+ current = null;
604
+ state = 'failed';
605
+ if (locked) await modelStore.unlock(modelId).catch(() => {});
606
+ throw error instanceof LlamaServerSupervisorError ? error : new LlamaServerSupervisorError(error.message, 'RUNTIME_PROCESS_FAILED');
607
+ }
608
+ }
609
+
610
+ async function stop() {
611
+ const entry = current;
612
+ if (!entry) { state = 'unavailable'; return status(); }
613
+ entry.state = 'stopping';
614
+ state = 'stopping';
615
+ if (entry.child && !entry.closed) entry.child.kill('SIGTERM');
616
+ current = null;
617
+ state = 'unavailable';
618
+ if (modelStore && entry.modelId) await modelStore.unlock(entry.modelId).catch(() => {});
619
+ return status();
620
+ }
621
+
622
+ function subscribeLogs(listener) {
623
+ if (typeof listener !== 'function') throw invalid('log listener must be a function');
624
+ listeners.add(listener);
625
+ return () => listeners.delete(listener);
626
+ }
627
+
628
+ return Object.freeze({ start, health, request, stop, status, subscribeLogs });
629
+ }