talos-code 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +661 -0
  3. package/README.md +116 -3
  4. package/THIRD_PARTY_NOTICES.md +40 -0
  5. package/dist/archive/zip.js +73 -0
  6. package/dist/args.js +100 -0
  7. package/dist/automations/history-store.js +34 -0
  8. package/dist/automations/policy-store.js +43 -0
  9. package/dist/automations/runner.js +107 -0
  10. package/dist/automations/schedule.js +85 -0
  11. package/dist/automations/windows-task.js +53 -0
  12. package/dist/commands/advanced-cli.js +233 -0
  13. package/dist/commands/checkpoint-cli.js +45 -0
  14. package/dist/commands/config-cli.js +107 -0
  15. package/dist/commands/context.js +17 -0
  16. package/dist/commands/extensions-cli.js +207 -0
  17. package/dist/commands/project-cli.js +74 -0
  18. package/dist/commands/project-commands.js +143 -0
  19. package/dist/commands/provider-cli.js +244 -0
  20. package/dist/commands/services-cli.js +248 -0
  21. package/dist/commands/session-cli.js +171 -0
  22. package/dist/commands/system-cli.js +405 -0
  23. package/dist/config/commands.js +60 -0
  24. package/dist/config/load.js +382 -0
  25. package/dist/config/migrations.js +48 -0
  26. package/dist/config/types.js +11 -0
  27. package/dist/diagnostics/development-log.js +154 -0
  28. package/dist/diagnostics/doctor.js +104 -0
  29. package/dist/diagnostics/redact.js +80 -0
  30. package/dist/diagnostics/zip.js +52 -0
  31. package/dist/errors.js +156 -0
  32. package/dist/events/bridge.js +130 -0
  33. package/dist/extensions/installer.js +178 -0
  34. package/dist/extensions/package-schema.js +24 -0
  35. package/dist/headless/result.js +84 -0
  36. package/dist/headless/run.js +230 -0
  37. package/dist/i18n/en/approval.js +46 -0
  38. package/dist/i18n/en/common.js +9 -0
  39. package/dist/i18n/en/credentials.js +92 -0
  40. package/dist/i18n/en/errors.js +267 -0
  41. package/dist/i18n/en/firstrun.js +101 -0
  42. package/dist/i18n/en/providers.js +37 -0
  43. package/dist/i18n/en/screen.js +774 -0
  44. package/dist/i18n/en/tools.js +127 -0
  45. package/dist/i18n/en.js +14 -0
  46. package/dist/i18n/error-view.js +307 -0
  47. package/dist/i18n/index.js +19 -0
  48. package/dist/i18n/kernel-map.js +139 -0
  49. package/dist/io.js +45 -0
  50. package/dist/main.js +331 -0
  51. package/dist/paths.js +41 -0
  52. package/dist/protocol/v2/codec.js +9 -0
  53. package/dist/protocol/v2/events.js +555 -0
  54. package/dist/protocol/v2/index.js +4 -0
  55. package/dist/protocol/v2/replay.js +28 -0
  56. package/dist/protocol/v2/types.js +1 -0
  57. package/dist/provider/control-plane.js +135 -0
  58. package/dist/provider/environment-keys.js +275 -0
  59. package/dist/provider/health.js +110 -0
  60. package/dist/provider/missing-key.js +48 -0
  61. package/dist/provider/model-catalog.js +168 -0
  62. package/dist/provider/openrouter-login.js +172 -0
  63. package/dist/provider/provider-text.js +13 -0
  64. package/dist/provider/store.js +95 -0
  65. package/dist/provider/system-keyring.js +214 -0
  66. package/dist/runtime/active-run.js +63 -0
  67. package/dist/runtime/agent-tree.js +74 -0
  68. package/dist/runtime/attachments.js +84 -0
  69. package/dist/runtime/brokered-executor.js +395 -0
  70. package/dist/runtime/context-archive.js +69 -0
  71. package/dist/runtime/context-status.js +76 -0
  72. package/dist/runtime/create-runtime.js +111 -0
  73. package/dist/runtime/model-profile.js +361 -0
  74. package/dist/runtime/output-store.js +137 -0
  75. package/dist/runtime/provider-attempts.js +185 -0
  76. package/dist/runtime/replay-buffer.js +153 -0
  77. package/dist/runtime/repo.js +95 -0
  78. package/dist/runtime/session-facade.js +135 -0
  79. package/dist/runtime/session-summary.js +125 -0
  80. package/dist/runtime/supervisor.js +265 -0
  81. package/dist/runtime/talos-composition.js +1011 -0
  82. package/dist/runtime/types.js +5 -0
  83. package/dist/runtime/usage-snapshot.js +82 -0
  84. package/dist/security/auto-classifier.js +38 -0
  85. package/dist/security/credential-free-environment.js +54 -0
  86. package/dist/security/evaluate.js +243 -0
  87. package/dist/security/execution-backends.js +236 -0
  88. package/dist/security/execution-broker.js +102 -0
  89. package/dist/security/forge-scan.js +74 -0
  90. package/dist/security/from-approval.js +39 -0
  91. package/dist/security/mxc-execution-backend.js +281 -0
  92. package/dist/security/permission-engine.js +138 -0
  93. package/dist/security/permission-explanation.js +54 -0
  94. package/dist/security/persist.js +179 -0
  95. package/dist/security/plugin-guard.js +230 -0
  96. package/dist/security/process-tree-evidence-store.js +74 -0
  97. package/dist/security/process-tree-probe.js +554 -0
  98. package/dist/security/project-resource-inventory.js +186 -0
  99. package/dist/security/project-trust-gate.js +38 -0
  100. package/dist/security/project-trust.js +367 -0
  101. package/dist/security/rule-parser.js +201 -0
  102. package/dist/security/shell-segmentation.js +68 -0
  103. package/dist/security/trust-authority.js +324 -0
  104. package/dist/security/types.js +1 -0
  105. package/dist/security/workspace-identity.js +102 -0
  106. package/dist/services/automation-facade.js +59 -0
  107. package/dist/services/forge-facade.js +77 -0
  108. package/dist/services/hook-facade.js +240 -0
  109. package/dist/services/index.js +16 -0
  110. package/dist/services/library-facade.js +174 -0
  111. package/dist/services/mcp-facade.js +348 -0
  112. package/dist/services/memory-facade.js +126 -0
  113. package/dist/services/notes-facade.js +157 -0
  114. package/dist/services/plugin-facade.js +308 -0
  115. package/dist/services/research-facade.js +110 -0
  116. package/dist/services/task-board-facade.js +236 -0
  117. package/dist/services/workflow-catalog.js +246 -0
  118. package/dist/sessions/export-format.js +98 -0
  119. package/dist/sessions/metadata-store.js +160 -0
  120. package/dist/sessions/share.js +122 -0
  121. package/dist/sessions/transfer.js +16 -0
  122. package/dist/subcommands.js +62 -0
  123. package/dist/tui/agent-roster.js +36 -0
  124. package/dist/tui/agent-view.js +51 -0
  125. package/dist/tui/app.js +4098 -0
  126. package/dist/tui/approval.js +36 -0
  127. package/dist/tui/boot/boot-sequence.js +76 -0
  128. package/dist/tui/boot/cinematic.js +243 -0
  129. package/dist/tui/boot/desktop-logo.js +82 -0
  130. package/dist/tui/boot.js +3 -0
  131. package/dist/tui/busy-input.js +58 -0
  132. package/dist/tui/catalog-service.js +560 -0
  133. package/dist/tui/components/assistant-stream.js +1 -0
  134. package/dist/tui/components/command-menu.js +68 -0
  135. package/dist/tui/components/composer.js +219 -0
  136. package/dist/tui/components/diff.js +35 -0
  137. package/dist/tui/components/footer.js +48 -0
  138. package/dist/tui/components/header.js +6 -0
  139. package/dist/tui/components/markdown.js +305 -0
  140. package/dist/tui/components/status-indicator.js +38 -0
  141. package/dist/tui/components/terminal-shell.js +214 -0
  142. package/dist/tui/components/thinking-row.js +19 -0
  143. package/dist/tui/components/tool-row.js +97 -0
  144. package/dist/tui/components/transcript-virtualizer.js +165 -0
  145. package/dist/tui/components/transcript.js +43 -0
  146. package/dist/tui/descendant-approvals.js +104 -0
  147. package/dist/tui/diff-model.js +77 -0
  148. package/dist/tui/editor-history.js +21 -0
  149. package/dist/tui/editor.js +210 -0
  150. package/dist/tui/event-adapter.js +436 -0
  151. package/dist/tui/exit-output.js +58 -0
  152. package/dist/tui/external-editor.js +53 -0
  153. package/dist/tui/file-completion.js +89 -0
  154. package/dist/tui/focus-manager.js +5 -0
  155. package/dist/tui/highlight.js +30 -0
  156. package/dist/tui/input-router.js +18 -0
  157. package/dist/tui/interrupt.js +99 -0
  158. package/dist/tui/keybindings.js +194 -0
  159. package/dist/tui/keymap-resolver.js +91 -0
  160. package/dist/tui/launch-state.js +148 -0
  161. package/dist/tui/line-diff.js +57 -0
  162. package/dist/tui/live-activity.js +56 -0
  163. package/dist/tui/metrics.js +123 -0
  164. package/dist/tui/onboarding.js +36 -0
  165. package/dist/tui/overlays/agent-tree.js +43 -0
  166. package/dist/tui/overlays/approval-dialog.js +642 -0
  167. package/dist/tui/overlays/automation-center.js +51 -0
  168. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  169. package/dist/tui/overlays/context-inspector.js +36 -0
  170. package/dist/tui/overlays/effort-line.js +110 -0
  171. package/dist/tui/overlays/forge-center.js +46 -0
  172. package/dist/tui/overlays/help-dialog.js +16 -0
  173. package/dist/tui/overlays/history-picker.js +37 -0
  174. package/dist/tui/overlays/hook-center.js +70 -0
  175. package/dist/tui/overlays/library-center.js +55 -0
  176. package/dist/tui/overlays/mcp-center.js +60 -0
  177. package/dist/tui/overlays/memory-center.js +63 -0
  178. package/dist/tui/overlays/model-picker.js +77 -0
  179. package/dist/tui/overlays/notes-center.js +53 -0
  180. package/dist/tui/overlays/overlay-host.js +8 -0
  181. package/dist/tui/overlays/plugin-center.js +76 -0
  182. package/dist/tui/overlays/provider-picker.js +171 -0
  183. package/dist/tui/overlays/queue-editor.js +32 -0
  184. package/dist/tui/overlays/research-center.js +59 -0
  185. package/dist/tui/overlays/scroll-window.js +234 -0
  186. package/dist/tui/overlays/session-picker.js +115 -0
  187. package/dist/tui/overlays/tasks-center.js +83 -0
  188. package/dist/tui/overlays/theme-picker.js +21 -0
  189. package/dist/tui/overlays/transcript-search.js +58 -0
  190. package/dist/tui/overlays/trust-center.js +29 -0
  191. package/dist/tui/overlays/workflow-center.js +46 -0
  192. package/dist/tui/project-file-index.js +214 -0
  193. package/dist/tui/project-references.js +85 -0
  194. package/dist/tui/project-trust-prompt.js +289 -0
  195. package/dist/tui/prompt-history-store.js +116 -0
  196. package/dist/tui/queue-store.js +110 -0
  197. package/dist/tui/regions/budget.js +59 -0
  198. package/dist/tui/regions/views.js +96 -0
  199. package/dist/tui/render-coordinator.js +148 -0
  200. package/dist/tui/render-scheduler.js +4 -0
  201. package/dist/tui/run.js +69 -0
  202. package/dist/tui/selection-list.js +29 -0
  203. package/dist/tui/session-controller.js +791 -0
  204. package/dist/tui/session-export.js +54 -0
  205. package/dist/tui/setup-wizard.js +46 -0
  206. package/dist/tui/shell-input.js +35 -0
  207. package/dist/tui/shell-layout.js +48 -0
  208. package/dist/tui/shell-model.js +98 -0
  209. package/dist/tui/slash-commands.js +82 -0
  210. package/dist/tui/state.js +151 -0
  211. package/dist/tui/status-bar.js +125 -0
  212. package/dist/tui/status-view.js +40 -0
  213. package/dist/tui/terminal-capabilities.js +9 -0
  214. package/dist/tui/terminal-session.js +11 -0
  215. package/dist/tui/text-width.js +66 -0
  216. package/dist/tui/theme-catalog.js +27 -0
  217. package/dist/tui/theme-store.js +23 -0
  218. package/dist/tui/theme.js +23 -0
  219. package/dist/tui/tool-display.js +135 -0
  220. package/dist/tui/tool-facts.js +1 -0
  221. package/dist/tui/tools/bash-renderer.js +32 -0
  222. package/dist/tui/tools/change.js +61 -0
  223. package/dist/tui/tools/edit-renderer.js +17 -0
  224. package/dist/tui/tools/generic-renderer.js +13 -0
  225. package/dist/tui/tools/list-renderer.js +14 -0
  226. package/dist/tui/tools/read-renderer.js +13 -0
  227. package/dist/tui/tools/registry.js +20 -0
  228. package/dist/tui/tools/row-format.js +187 -0
  229. package/dist/tui/tools/search-renderer.js +38 -0
  230. package/dist/tui/tools/shared.js +97 -0
  231. package/dist/tui/tools/write-renderer.js +15 -0
  232. package/dist/tui/transcript-model.js +441 -0
  233. package/dist/tui/ui-preferences.js +79 -0
  234. package/dist/tui/usage-view.js +77 -0
  235. package/dist/tui/vim-mode.js +99 -0
  236. package/dist/update/check.js +15 -0
  237. package/dist/update/npm.js +51 -0
  238. package/dist/update/run.js +129 -0
  239. package/dist/version.js +2 -0
  240. package/dist/workspace/checkpoint-store.js +210 -0
  241. package/dist/workspace/checkpoint.js +413 -0
  242. package/dist/workspace/restore.js +232 -0
  243. package/package.json +64 -5
  244. package/vendor/context-engine/package.json +11 -0
  245. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  246. package/vendor/context-engine/src/contracts.mjs +48 -0
  247. package/vendor/context-engine/src/engine.mjs +445 -0
  248. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  249. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  250. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  251. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  252. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  253. package/vendor/context-engine/src/profiles.mjs +10 -0
  254. package/vendor/context-engine/src/retrieval.mjs +104 -0
  255. package/vendor/context-engine/src/summary.mjs +97 -0
  256. package/vendor/context-engine/src/usage.mjs +34 -0
  257. package/vendor/harness-ui/package.json +38 -0
  258. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  259. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  260. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  261. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  262. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  263. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  264. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  265. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  266. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  267. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  268. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  269. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  270. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  271. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  272. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  273. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  274. package/vendor/harness-ui/src/config.mjs +697 -0
  275. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  276. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  277. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  278. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  279. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  280. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  281. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  282. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  283. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  284. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  285. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  286. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  287. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  288. package/vendor/harness-ui/src/doctor.mjs +142 -0
  289. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  290. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  291. package/vendor/harness-ui/src/document-report.mjs +331 -0
  292. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  293. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  294. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  295. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  296. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  297. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  298. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  299. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  300. package/vendor/harness-ui/src/git-service.mjs +626 -0
  301. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  302. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  303. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  304. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  305. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  306. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  307. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  308. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  309. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  310. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  311. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  312. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  313. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  314. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  315. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  316. package/vendor/harness-ui/src/library-store.mjs +652 -0
  317. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  318. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  319. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  320. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  321. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  322. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  323. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  324. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  325. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  326. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  327. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  328. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  329. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  330. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  331. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  332. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  333. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  334. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  335. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  336. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  337. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  338. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  339. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  340. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  341. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  342. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  343. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  344. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  345. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  346. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  347. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  348. package/vendor/harness-ui/src/research/card.mjs +235 -0
  349. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  350. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  351. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  352. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  353. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  354. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  355. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  356. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  357. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  358. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  359. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  360. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  361. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  362. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  363. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  364. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  365. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  366. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  367. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  368. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  369. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  370. package/vendor/harness-ui/src/research/report.mjs +203 -0
  371. package/vendor/harness-ui/src/research/run.mjs +527 -0
  372. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  373. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  374. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  375. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  376. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  377. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  378. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  379. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  380. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  381. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  382. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  383. package/vendor/harness-ui/src/session-store.mjs +220 -0
  384. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  385. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  386. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  387. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  388. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  389. package/vendor/harness-ui/src/static-files.mjs +96 -0
  390. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  391. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  392. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  393. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  394. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  395. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  396. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  397. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  398. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  399. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  400. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  401. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  402. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  403. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  404. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  405. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  406. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  407. package/vendor/manifest.json +170 -0
@@ -0,0 +1,244 @@
1
+ import { parseRuntimeEventEnvelope } from './local-runtime-events.mjs';
2
+ import { createStreamPartitioner } from './stream-partition.mjs';
3
+
4
+ export class LlamaServerRuntimeError extends Error {
5
+ constructor(message, code = 'RUNTIME_FAILED') {
6
+ super(message);
7
+ this.name = 'LlamaServerRuntimeError';
8
+ this.code = code;
9
+ }
10
+ }
11
+
12
+ function runtimeInvalid(message) {
13
+ return new LlamaServerRuntimeError(message, 'RUNTIME_INVALID');
14
+ }
15
+
16
+ function envelope({ runId, turnId, seq, type, runtimeId = 'llama.cpp', ...payload }, now) {
17
+ return parseRuntimeEventEnvelope({ runId, turnId, runtimeId, seq, at: now().toISOString(), type, ...payload });
18
+ }
19
+
20
+ async function readText(response) {
21
+ return typeof response.text === 'function' ? response.text() : '';
22
+ }
23
+
24
+ async function* sseEvents(response) {
25
+ if (!response.body || typeof response.body.getReader !== 'function') throw new LlamaServerRuntimeError('SSE body is missing', 'RUNTIME_SSE_INVALID');
26
+ const reader = response.body.getReader();
27
+ const decoder = new TextDecoder();
28
+ let buffer = '';
29
+ let finished = false;
30
+ try {
31
+ while (!finished) {
32
+ const { value, done } = await reader.read();
33
+ buffer += decoder.decode(value ?? new Uint8Array(), { stream: !done });
34
+ const lines = buffer.split(/\r?\n/u);
35
+ buffer = lines.pop() ?? '';
36
+ for (const line of lines) {
37
+ if (!line.startsWith('data:')) continue;
38
+ const data = line.slice(5).trim();
39
+ if (data === '') continue;
40
+ if (data === '[DONE]') { finished = true; break; }
41
+ try { yield JSON.parse(data); } catch { yield { __invalid: true }; }
42
+ }
43
+ if (done) break;
44
+ }
45
+ if (buffer.startsWith('data:')) {
46
+ const data = buffer.slice(5).trim();
47
+ if (data && data !== '[DONE]') {
48
+ try { yield JSON.parse(data); } catch { yield { __invalid: true }; }
49
+ }
50
+ }
51
+ } finally {
52
+ await reader.cancel().catch(() => {});
53
+ }
54
+ }
55
+
56
+ export function createLlamaServerRuntime({ supervisor, fetchImpl = fetch, now = () => new Date() } = {}) {
57
+ if (!supervisor || typeof supervisor.status !== 'function') throw new LlamaServerRuntimeError('supervisor is required', 'RUNTIME_MISCONFIGURED');
58
+ const activeRequests = new Map();
59
+
60
+ function baseUrl() {
61
+ const status = supervisor.status();
62
+ if (!status.baseUrl || status.state !== 'ready') throw new LlamaServerRuntimeError('runtime is not ready', 'RUNTIME_NOT_READY');
63
+ return status.baseUrl;
64
+ }
65
+
66
+ async function jsonRequest(path) {
67
+ const response = typeof supervisor.request === 'function'
68
+ ? await supervisor.request(path, { headers: { Accept: 'application/json' } })
69
+ : await fetchImpl(`${baseUrl()}${path}`, { headers: { Accept: 'application/json' } });
70
+ if (!response.ok) throw new LlamaServerRuntimeError(`runtime returned HTTP ${response.status}`, 'RUNTIME_HTTP_ERROR');
71
+ try { return await response.json(); } catch { throw new LlamaServerRuntimeError('runtime returned invalid JSON', 'RUNTIME_RESPONSE_INVALID'); }
72
+ }
73
+
74
+ async function probe() {
75
+ return jsonRequest('/props');
76
+ }
77
+
78
+ /**
79
+ * L'id del modello effettivamente caricato adesso, o `null` se non c'è
80
+ * nessun runtime pronto.
81
+ *
82
+ * ⛔ 02/9 — serve a `inspectModel()` per NON attribuire l'`n_ctx` osservato
83
+ * a un modello diverso da quello che lo ha prodotto. Si legge dal
84
+ * supervisore (codice nostro, lo stesso valore che finisce in `--alias`) e
85
+ * non da un campo indovinato dentro `/props`: llama.cpp ne espone di
86
+ * simili, ma un nome di campo dedotto invece che verificato è già costato
87
+ * tre difetti su questo stesso sottosistema il 02/9.
88
+ */
89
+ function loadedModelId() {
90
+ try {
91
+ const stato = supervisor.status();
92
+ return stato?.state === 'ready' && typeof stato.modelId === 'string' && stato.modelId !== ''
93
+ ? stato.modelId
94
+ : null;
95
+ } catch { return null; }
96
+ }
97
+
98
+ async function listModels() {
99
+ const body = await jsonRequest('/v1/models');
100
+ if (!Array.isArray(body?.data)) throw new LlamaServerRuntimeError('runtime model list is invalid', 'RUNTIME_RESPONSE_INVALID');
101
+ return body.data;
102
+ }
103
+
104
+ /** ⛔ `options` viaggia intero, `contextLength` compreso: era qui che si perdeva. */
105
+ async function load(options) {
106
+ return supervisor.start(options);
107
+ }
108
+
109
+ async function unload() {
110
+ return supervisor.stop();
111
+ }
112
+
113
+ async function health() {
114
+ return supervisor.health();
115
+ }
116
+
117
+ async function metrics() {
118
+ const response = typeof supervisor.request === 'function'
119
+ ? await supervisor.request('/metrics', { headers: { Accept: 'text/plain' } })
120
+ : await fetchImpl(`${baseUrl()}/metrics`, { headers: { Accept: 'text/plain' } });
121
+ if (!response.ok) throw new LlamaServerRuntimeError(`runtime returned HTTP ${response.status}`, 'RUNTIME_HTTP_ERROR');
122
+ return readText(response);
123
+ }
124
+
125
+ async function* generateStream({ runId, turnId, modelId, messages, reasoning, reasoningFormat, parseToolCalls = false, maxTokens = 512, signal, requestId } = {}) {
126
+ if (typeof runId !== 'string' || runId.trim() === '' || typeof turnId !== 'string' || turnId.trim() === '' || typeof modelId !== 'string' || modelId.trim() === '' || !Array.isArray(messages)) throw runtimeInvalid('stream request is invalid');
127
+ const controller = new AbortController();
128
+ if (signal?.aborted) controller.abort(signal.reason);
129
+ const combinedSignal = signal && typeof AbortSignal.any === 'function' ? AbortSignal.any([signal, controller.signal]) : controller.signal;
130
+ if (signal && typeof AbortSignal.any !== 'function') signal.addEventListener('abort', () => controller.abort(signal.reason), { once: true });
131
+ if (requestId) activeRequests.set(requestId, controller);
132
+ let response;
133
+ try {
134
+ combinedSignal.throwIfAborted();
135
+ const body = { model: modelId, messages, stream: true, max_tokens: Number.isInteger(maxTokens) && maxTokens > 0 ? maxTokens : 512 };
136
+ if (reasoning?.effort) body.reasoning_effort = reasoning.effort;
137
+ if (reasoningFormat) body.reasoning_format = reasoningFormat;
138
+ if (parseToolCalls) body.parse_tool_calls = true;
139
+ const requestOptions = {
140
+ method: 'POST',
141
+ headers: { Accept: 'text/event-stream', 'Content-Type': 'application/json' },
142
+ body: JSON.stringify(body),
143
+ signal: combinedSignal,
144
+ };
145
+ response = typeof supervisor.request === 'function'
146
+ ? await supervisor.request('/v1/chat/completions', requestOptions)
147
+ : await fetchImpl(`${baseUrl()}/v1/chat/completions`, requestOptions);
148
+ if (!response.ok) throw new LlamaServerRuntimeError(`runtime returned HTTP ${response.status}: ${await readText(response)}`, 'RUNTIME_HTTP_ERROR');
149
+ let seq = 0;
150
+ const pendingTools = new Set();
151
+ const toolsByIndex = new Map();
152
+ const toolsById = new Map();
153
+ // A valid JSON prefix is not a completed call (e.g. "1" then "2").
154
+ // Join and validate once, at finish_reason or ordinary stream EOF.
155
+ // Aborted/failed readers never reach the final flush.
156
+ const flushNativeTools = function* () {
157
+ for (const tool of pendingTools) {
158
+ combinedSignal.throwIfAborted();
159
+ let event;
160
+ try {
161
+ event = envelope({ runId, turnId, seq, type: 'tool_call', id: tool.id,
162
+ name: tool.names.join(''), arguments: tool.arguments.join('') }, now);
163
+ } catch (error) {
164
+ if (error?.code !== 'LOCAL_RUNTIME_INVALID') throw error;
165
+ event = envelope({ runId, turnId, seq, type: 'error', code: 'TOOL_CALL_MALFORMED',
166
+ message: `malformed tool call ${tool.id}`, retryable: false }, now);
167
+ }
168
+ seq++;
169
+ yield event;
170
+ }
171
+ pendingTools.clear();
172
+ toolsByIndex.clear();
173
+ toolsById.clear();
174
+ };
175
+ let taggedEvents = [];
176
+ const taggedContent = createStreamPartitioner({
177
+ onText: (value) => taggedEvents.push({ type: 'text', value }),
178
+ onReasoning: (value) => taggedEvents.push({ type: 'reasoning', value }),
179
+ onToolCall: (value) => taggedEvents.push({ type: 'tool_call', ...value }),
180
+ });
181
+ const flushTaggedEvents = function* () {
182
+ const events = taggedEvents;
183
+ taggedEvents = [];
184
+ for (const event of events) {
185
+ if (event.type === 'tool_call') {
186
+ yield envelope({ runId, turnId, seq: seq++, type: 'tool_call', id: event.id || `tagged-tool-${seq}`, name: event.name, arguments: typeof event.arguments === 'string' ? event.arguments : JSON.stringify(event.arguments ?? {}) }, now);
187
+ } else {
188
+ yield envelope({ runId, turnId, seq: seq++, type: event.type, value: event.value }, now);
189
+ }
190
+ }
191
+ };
192
+ for await (const chunk of sseEvents(response)) {
193
+ combinedSignal.throwIfAborted();
194
+ if (chunk.__invalid) {
195
+ yield envelope({ runId, turnId, seq: seq++, type: 'error', code: 'RUNTIME_SSE_INVALID', message: 'runtime emitted malformed SSE JSON', retryable: false }, now);
196
+ continue;
197
+ }
198
+ const choice = chunk.choices?.[0];
199
+ const delta = choice?.delta;
200
+ if (!delta) {
201
+ if (choice?.finish_reason != null) yield* flushNativeTools();
202
+ continue;
203
+ }
204
+ const reasoningDelta = delta.reasoning_content ?? delta.reasoning;
205
+ if (typeof reasoningDelta === 'string' && reasoningDelta !== '') yield envelope({ runId, turnId, seq: seq++, type: 'reasoning', value: reasoningDelta }, now);
206
+ if (typeof delta.content === 'string' && delta.content !== '') {
207
+ taggedContent.push(delta.content);
208
+ yield* flushTaggedEvents();
209
+ }
210
+ for (const call of delta.tool_calls ?? []) {
211
+ // OpenAI-style deltas normally supply id/name only in the first
212
+ // fragment; index remains stable. Keep id-only legacy streams too.
213
+ const index = Number.isInteger(call.index) && call.index >= 0 ? call.index : null;
214
+ const hasId = call.id !== undefined && call.id !== null;
215
+ const tool = (index !== null ? toolsByIndex.get(index) : undefined)
216
+ ?? (hasId ? toolsById.get(call.id) : undefined)
217
+ ?? { id: call.id ?? String(index ?? pendingTools.size), names: [], arguments: [] };
218
+ pendingTools.add(tool);
219
+ if (index !== null) toolsByIndex.set(index, tool);
220
+ if (hasId) { tool.id = call.id; toolsById.set(call.id, tool); }
221
+ if (call.function?.name != null) tool.names.push(call.function.name);
222
+ if (call.function?.arguments != null) tool.arguments.push(call.function.arguments);
223
+ }
224
+ if (choice.finish_reason != null) yield* flushNativeTools();
225
+ }
226
+ combinedSignal.throwIfAborted();
227
+ taggedContent.finish();
228
+ yield* flushTaggedEvents();
229
+ yield* flushNativeTools();
230
+ yield envelope({ runId, turnId, seq: seq++, type: 'done' }, now);
231
+ } finally {
232
+ if (requestId) activeRequests.delete(requestId);
233
+ }
234
+ }
235
+
236
+ function cancel(requestId) {
237
+ const controller = activeRequests.get(requestId);
238
+ if (!controller) return false;
239
+ controller.abort();
240
+ return true;
241
+ }
242
+
243
+ return Object.freeze({ probe, loadedModelId, listModels, load, unload, generateStream, cancel, health, metrics });
244
+ }
@@ -0,0 +1,401 @@
1
+ /**
2
+ * Unisce cartella e file del manifest. ⛔ Non usa `node:path.join`: i
3
+ * percorsi del manifest sono relativi e sempre con `/` (validati così da
4
+ * `local-model-store.mjs`), e su Windows `join` li riscriverebbe con `\`,
5
+ * cambiando una stringa che il chiamante potrebbe confrontare. Il
6
+ * chiamante ci antepone la radice assoluta con il join vero.
7
+ */
8
+ function joinPosix(cartella, file) {
9
+ return `${String(cartella).replace(/[/\\]+$/u, '')}/${String(file).replace(/^[/\\]+/u, '')}`;
10
+ }
11
+
12
+ export class LocalRuntimeProbeError extends Error {
13
+ constructor(message, code = 'LOCAL_RUNTIME_PROBE_FAILED') {
14
+ super(message);
15
+ this.name = 'LocalRuntimeProbeError';
16
+ this.code = code;
17
+ }
18
+ }
19
+
20
+ const unknown = () => ({ state: 'unknown', value: null });
21
+ const fact = (state, value) => ({ state, value });
22
+ const positiveInteger = (value) => Number.isSafeInteger(value) && value > 0;
23
+
24
+ function observedString(value) {
25
+ return typeof value === 'string' && value.trim() !== '' ? fact('observed', value) : unknown();
26
+ }
27
+
28
+ function observedBoolean(value) {
29
+ return typeof value === 'boolean' ? fact('observed', value) : unknown();
30
+ }
31
+
32
+ function validateHeader(value) {
33
+ if (!value || typeof value !== 'object' || Array.isArray(value)
34
+ || value.magic !== 'GGUF' || value.version !== 3
35
+ || !positiveInteger(value.trainedContext)
36
+ || !positiveInteger(value.estimatedWorkingBytes)) {
37
+ throw new LocalRuntimeProbeError('GGUF header is invalid', 'MODEL_HEADER_INVALID');
38
+ }
39
+ return value;
40
+ }
41
+
42
+ function parseTokensPerSecond(value) {
43
+ if (typeof value !== 'string') return unknown();
44
+ for (const line of value.split(/\r?\n/u)) {
45
+ const match = line.match(/^llamacpp:predicted_tokens_seconds(?:\{[^}]*\})?\s+([^\s]+)$/u);
46
+ const number = match ? Number(match[1]) : NaN;
47
+ if (Number.isFinite(number) && number >= 0) return fact('observed', number);
48
+ }
49
+ return unknown();
50
+ }
51
+
52
+ export function createLocalRuntimeProbe({
53
+ runtime,
54
+ modelStore,
55
+ readHeader,
56
+ measureMachine,
57
+ now = () => new Date(),
58
+ clockMs = () => performance.now(),
59
+ } = {}) {
60
+ if (!runtime || typeof runtime.probe !== 'function' || typeof runtime.generateStream !== 'function'
61
+ || !modelStore || typeof modelStore.inspect !== 'function'
62
+ || typeof readHeader !== 'function' || typeof measureMachine !== 'function') {
63
+ throw new LocalRuntimeProbeError('probe dependencies are invalid', 'LOCAL_RUNTIME_PROBE_MISCONFIGURED');
64
+ }
65
+
66
+ async function readRuntimeProps() {
67
+ try {
68
+ const props = await runtime.probe();
69
+ if (!props || typeof props !== 'object' || Array.isArray(props)) throw new Error('invalid response');
70
+ return props;
71
+ } catch (error) {
72
+ if (error instanceof LocalRuntimeProbeError) throw error;
73
+ throw new LocalRuntimeProbeError(`runtime probe failed: ${error?.message || 'unknown error'}`, 'RUNTIME_PROBE_FAILED');
74
+ }
75
+ }
76
+
77
+ /**
78
+ * @param {string} modelId
79
+ * @param {{contextTokens?: number}} [opzioni] `contextTokens` = il contesto
80
+ * per cui si vuole la stima di memoria. ⛔ Assente = si usa quello
81
+ * ADDESTRATO, che per un modello a contesto lungo è enormemente più
82
+ * grande di quello che si userà davvero (sul Qwen3 27B: 262.144 token
83
+ * invece dei 65.536 richiesti, un fattore 4 di sovrastima). `fit()` lo
84
+ * passa sempre; resta opzionale per non rompere chi chiama
85
+ * `inspectModel` da solo per sapere cosa dichiara il file.
86
+ */
87
+ async function inspectModel(modelId, { contextTokens } = {}) {
88
+ const manifest = await modelStore.inspect(modelId);
89
+ if (!manifest) throw new LocalRuntimeProbeError(`model ${modelId} not found`, 'MODEL_NOT_FOUND');
90
+ if (manifest.state !== 'ready') throw new LocalRuntimeProbeError(`model ${modelId} is not ready`, 'MODEL_NOT_READY');
91
+
92
+ let header;
93
+ try {
94
+ /*
95
+ * ⛔⛔⛔ 02/9 (sera) — QUI si passava `manifest.path` da solo, e
96
+ * `manifest.path` è la CARTELLA del modello, non il file: il lettore
97
+ * riceveva una directory e falliva sempre con
98
+ * `MODEL_HEADER_UNREADABLE`. Il difetto è mio, del collegamento
99
+ * scritto stamattina, e non era emerso perché i test iniettano un
100
+ * `readHeader` finto (a cui il percorso non importa) e sulla macchina
101
+ * non c'era NESSUN modello registrato con cui provarlo dal vivo:
102
+ * è saltato fuori solo dopo aver reimportato due GGUF veri.
103
+ * ⇒ Il percorso si compone qui, dove si conosce la forma del
104
+ * manifest (cartella + primo file), esattamente come già faceva
105
+ * `load` in `server.mjs`; la radice assoluta la mette il chiamante,
106
+ * che è l'unico a conoscerla.
107
+ */
108
+ const file = Array.isArray(manifest.files) ? manifest.files[0] : null;
109
+ if (!file || typeof file.path !== 'string') throw new LocalRuntimeProbeError(`model ${modelId} has no file to inspect`, 'MODEL_HEADER_UNREADABLE');
110
+ header = validateHeader(await readHeader(joinPosix(manifest.path, file.path)));
111
+ } catch (error) {
112
+ if (error instanceof LocalRuntimeProbeError) throw error;
113
+ throw new LocalRuntimeProbeError(`cannot read model header: ${error?.message || 'unknown error'}`, 'MODEL_HEADER_UNREADABLE');
114
+ }
115
+
116
+ /*
117
+ * ⛔⛔ 02/9 (sera) — `readRuntimeProps()` LANCIA se il runtime locale
118
+ * non risponde, e faceva abortire tutta `inspectModel`: `/fit`
119
+ * rispondeva `RUNTIME_PROBE_FAILED` anche quando aveva già letto
120
+ * l'header e poteva dire cose vere su spazio, memoria e contesto
121
+ * addestrato. Incoerente col disegno di questo stesso file: tutto ciò
122
+ * che segue è GIÀ scritto per degradare (`unknown()`,
123
+ * `observedBoolean(caps?.…)`), e `fit()` ha lo stato `unknown` con
124
+ * motivo `context` esattamente per questo caso.
125
+ * ⇒ Un runtime spento non è un errore della lettura: è un fatto NON
126
+ * OSSERVATO, e si dichiara come tale invece di rifiutare la risposta.
127
+ * ⛔ `qualify()` resta severo: chiede `fit()` compatibile e con
128
+ * `unknown` si ferma da solo — un giro di generazione vero senza
129
+ * runtime non deve neanche essere tentato.
130
+ */
131
+ let props = {};
132
+ let raggiungibile = true;
133
+ try {
134
+ props = await readRuntimeProps();
135
+ } catch (error) {
136
+ if (!(error instanceof LocalRuntimeProbeError) || error.code !== 'RUNTIME_PROBE_FAILED') throw error;
137
+ raggiungibile = false;
138
+ }
139
+ /*
140
+ * ⛔⛔⛔ L'`n_ctx` È DEL MODELLO CARICATO, NON DELLA MACCHINA — 02/9.
141
+ *
142
+ * Il difetto, trovato dal vivo e non leggendo: con un runtime acceso su
143
+ * un modello, la riga di OGNI ALTRO modello mostrava come «contesto
144
+ * disponibile» l'`n_ctx` di quello caricato. `/props` descrive la
145
+ * sessione in corso, non la macchina: quel numero è vero solo per il
146
+ * modello che l'ha prodotto. Un 262k caricato faceva sembrare enorme il
147
+ * contesto di un 4k, e un 4k caricato faceva bocciare per contesto un
148
+ * modello che ne ha 262k — cioè l'errore in ENTRAMBE le direzioni.
149
+ *
150
+ * ⭐ Ricerca 02/9: llama.cpp tiene i due numeri separati proprio perché
151
+ * sono cose diverse — `n_ctx` (quello caricato) contro `n_ctx_train`
152
+ * (quello del modello) — e stampa un avviso quando divergono
153
+ * («n_ctx_per_seq (4096) < n_ctx_train (8192)»). Noi ne usavamo uno solo
154
+ * per rispondere a due domande.
155
+ *
156
+ * ⇒ L'osservazione vale solo per il modello caricato. Per gli altri il
157
+ * runtime NON HA OSSERVATO NIENTE, e `unknown()` è la risposta onesta:
158
+ * quando quel modello verrà caricato avrà il contesto suo. ⛔ Non è un
159
+ * ripiego pessimista — è la differenza fra «non lo so» e «so che è
160
+ * questo», la stessa che regge tutto il resto di questo file.
161
+ */
162
+ /*
163
+ * `null` = nessun runtime pronto (e allora `props` è vuoto, quindi si
164
+ * ricade su `unknown()` da sé) oppure un adattatore che non sa ancora
165
+ * rispondere. In entrambi i casi non c'è un modello ALTRO a cui
166
+ * attribuire il numero, che è la cosa da impedire.
167
+ */
168
+ const caricato = typeof runtime.loadedModelId === 'function' ? runtime.loadedModelId() : null;
169
+ const parlaDiQuestoModello = caricato === null || caricato === manifest.id;
170
+ const runtimeTokens = props.default_generation_settings?.n_ctx;
171
+ const runtimeContext = parlaDiQuestoModello && positiveInteger(runtimeTokens)
172
+ ? fact('observed', runtimeTokens)
173
+ : unknown();
174
+ const caps = props.chat_template_caps;
175
+ return {
176
+ modelId: manifest.id,
177
+ format: { state: 'observed', magic: 'GGUF', version: 3 },
178
+ storageBytes: { state: 'declared', value: manifest.bytes },
179
+ /*
180
+ * ⛔ 02/9 (sera) — si stima sul contesto RICHIESTO quando il lettore
181
+ * espone il costo per token; senza quel dato (header vecchio o file
182
+ * che non dichiara le teste) si ricade sulla stima del file, che usa
183
+ * il contesto addestrato. Il tetto è il contesto addestrato: chiedere
184
+ * più di quanto il modello sa fare non costa più memoria, semmai è il
185
+ * controllo sul contesto a bocciarlo, e con un motivo suo.
186
+ */
187
+ workingMemoryBytes: {
188
+ state: 'declared',
189
+ // ⛔ Math.ceil come nel lettore: il consumatore a valle pretende interi.
190
+ value: (Number.isSafeInteger(header.kvCacheBytesPerToken) && Number.isSafeInteger(contextTokens) && contextTokens > 0)
191
+ ? Math.ceil((header.estimatedWorkingBytes - header.kvCacheBytesPerToken * header.trainedContext)
192
+ + header.kvCacheBytesPerToken * Math.min(contextTokens, header.trainedContext))
193
+ : header.estimatedWorkingBytes,
194
+ },
195
+ context: {
196
+ trainedTokens: fact('declared', header.trainedContext),
197
+ runtimeTokens: runtimeContext,
198
+ effectiveTokens: runtimeContext.state === 'observed'
199
+ ? fact('observed', Math.min(header.trainedContext, runtimeContext.value))
200
+ : fact('declared', header.trainedContext),
201
+ },
202
+ /*
203
+ * ⛔⛔⛔ E NON È SOLO IL CONTESTO — scoperto allargando la cura, 02/9.
204
+ *
205
+ * `chat_template` e `chat_template_caps` arrivano dallo STESSO `/props`,
206
+ * e descrivono anch'essi la sessione in corso. Con un modello caricato,
207
+ * la riga di un altro modello dichiarava il suo template e il suo
208
+ * supporto agli attrezzi — cioè rispondeva «sì, chiama gli attrezzi»
209
+ * per un modello che potrebbe non saperlo fare. È il difetto più grave
210
+ * dei due, perché una capacità inventata si scopre solo a metà di una
211
+ * sessione agentica.
212
+ * ⇒ Stessa regola del contesto: se non è il modello caricato, il
213
+ * runtime non ha osservato NIENTE di lui.
214
+ */
215
+ runtime: { reachable: raggiungibile, servingThisModel: parlaDiQuestoModello, servingModelId: caricato },
216
+ template: parlaDiQuestoModello && typeof props.chat_template === 'string' && props.chat_template !== ''
217
+ ? fact('observed', true)
218
+ : unknown(),
219
+ capabilities: parlaDiQuestoModello ? {
220
+ tools: observedBoolean(caps?.supports_tools),
221
+ toolCalls: observedBoolean(caps?.supports_tool_calls),
222
+ systemRole: observedBoolean(caps?.supports_system_role),
223
+ } : { tools: unknown(), toolCalls: unknown(), systemRole: unknown() },
224
+ backend: observedString(props.backend),
225
+ build: observedString(props.build),
226
+ observedAt: now().toISOString(),
227
+ };
228
+ }
229
+
230
+ async function measureBackend() {
231
+ const props = await readRuntimeProps();
232
+ return {
233
+ backend: observedString(props.backend),
234
+ thermal: unknown(),
235
+ observedAt: now().toISOString(),
236
+ };
237
+ }
238
+
239
+ async function fit(modelId, { profile = 'agent', contextTokens = profile === 'agent' ? 65_536 : 4_096 } = {}) {
240
+ if (!['agent', 'chat'].includes(profile) || !positiveInteger(contextTokens)) {
241
+ throw new LocalRuntimeProbeError('fit request is invalid', 'FIT_INVALID');
242
+ }
243
+ // ⛔ Il contesto RICHIESTO arriva fin qui: è quello per cui la memoria va stimata.
244
+ const inspection = await inspectModel(modelId, { contextTokens });
245
+ let machine;
246
+ try {
247
+ machine = await measureMachine();
248
+ } catch {
249
+ return { modelId, profile, state: 'unknown', reason: 'measurement', inspection };
250
+ }
251
+ const storageAvailable = machine?.storage?.allocatableBytes;
252
+ const memoryAvailable = machine?.memory?.freeBytes;
253
+ const base = {
254
+ modelId,
255
+ profile,
256
+ context: { requestedTokens: contextTokens, availableTokens: inspection.context.effectiveTokens.value },
257
+ storage: { requiredBytes: inspection.storageBytes.value, availableBytes: storageAvailable ?? null },
258
+ memory: { requiredBytes: inspection.workingMemoryBytes.value, availableBytes: memoryAvailable ?? null },
259
+ inspection,
260
+ };
261
+ if (!Number.isSafeInteger(storageAvailable) || !Number.isSafeInteger(memoryAvailable)) return { ...base, state: 'unknown', reason: 'measurement' };
262
+ if (inspection.storageBytes.value > storageAvailable) return { ...base, state: 'blocked', reason: 'storage' };
263
+ if (inspection.workingMemoryBytes.value > memoryAvailable) return { ...base, state: 'blocked', reason: 'memory' };
264
+ /*
265
+ * ⛔ Il cancello del contesto guarda la RAGGIUNGIBILITÀ, non l'osservazione.
266
+ *
267
+ * Prima leggeva `runtimeTokens.state !== 'observed'`, e finché quel campo
268
+ * conteneva l'`n_ctx` di chiunque fosse caricato le due cose coincidevano.
269
+ * Da quando il numero è attribuito al modello giusto, non coincidono più:
270
+ * un modello NON caricato ha `runtimeTokens` sconosciuto per costruzione,
271
+ * e con la vecchia riga ogni riga della lista sarebbe diventata «non lo
272
+ * so» — cioè la lista avrebbe smesso di rispondere alla sola domanda per
273
+ * cui esiste (*ci sta, prima di caricarlo?*).
274
+ *
275
+ * ⇒ Il caso che quella riga voleva prendere resta preso, e per intero: se
276
+ * il runtime non risponde non si sa niente né del contesto né del
277
+ * template. Se invece risponde ma sta servendo un altro modello, il
278
+ * contesto ADDESTRATO lo dichiara l'header — che è un fatto del modello,
279
+ * non della sessione — e il profilo agente si ferma comunque poco sotto,
280
+ * sul cancello delle capacità, che ora è sconosciuto per lo stesso motivo.
281
+ */
282
+ if (!inspection.runtime.reachable) return { ...base, state: 'unknown', reason: 'context' };
283
+ if (inspection.context.effectiveTokens.value < contextTokens) {
284
+ return { ...base, state: profile === 'agent' && inspection.context.effectiveTokens.value < 65_536 ? 'chat-only' : 'blocked', reason: 'context' };
285
+ }
286
+ if (profile === 'agent') {
287
+ const capabilityValues = Object.values(inspection.capabilities);
288
+ if (capabilityValues.some(({ state }) => state !== 'observed')) return { ...base, state: 'unknown', reason: 'capabilities' };
289
+ if (capabilityValues.some(({ value }) => value !== true)) return { ...base, state: 'chat-only', reason: 'template' };
290
+ }
291
+ return { ...base, state: 'compatible', reason: 'fits' };
292
+ }
293
+
294
+ /**
295
+ * ⭐⭐⭐ LA STIMA PRIMA DELLO SCARICAMENTO — 03/9, richiesta dell'owner:
296
+ * «badge e pulsanti per misurare in tempo reale se quel modello e
297
+ * quantizzazione entrano e girano nel pc».
298
+ *
299
+ * ## Perché non è `fit()`
300
+ *
301
+ * `fit()` legge l'header GGUF dal disco: sa strati, teste e contesto
302
+ * addestrato, quindi calcola la cache del contesto al byte. Qui il file
303
+ * **non c'è ancora** — è una variante su Hugging Face — e l'unica cosa
304
+ * nota è quanto pesa. Fingere lo stesso verdetto sarebbe inventare.
305
+ *
306
+ * ## Cosa dichiara, e cosa NON dichiara
307
+ *
308
+ * ⛔ Questa è una **soglia inferiore**, non un totale: copre i PESI e
309
+ * basta. La cache del contesto si somma sopra e non è calcolabile senza
310
+ * l'header (serve `n_kv_heads` × `head_dim` × strati). Perciò:
311
+ * · se già i pesi non ci stanno, **non ci sta**, ed è un fatto;
312
+ * · se i pesi ci stanno, si è detto solo che i pesi ci stanno.
313
+ * Il campo `basis: 'weights-only'` viaggia con la risposta proprio perché
314
+ * chi la mostra non possa dimenticarsene.
315
+ *
316
+ * ⭐ Ricerca 03/9 (LM Studio, tech-insider/insiderllm): il concorrente
317
+ * etichetta ogni quantizzazione con la memoria stimata e una scala a
318
+ * badge (giallo = offload parziale, rosso = non entra), e ha
319
+ * `lms load --estimate-only`. La stima prima dello scaricamento è una
320
+ * capacità affermata, non un'invenzione nostra — ma nessuno dei due
321
+ * dichiara che è una soglia inferiore, e noi sì.
322
+ *
323
+ * ⛔ Stessa `measureMachine()` e stesso vocabolario di stati/motivi di
324
+ * `fit()`: due verdetti sulla stessa schermata scritti con parole diverse
325
+ * insegnano a non fidarsi di nessuno dei due.
326
+ */
327
+ async function estimateFit({ bytes, contextTokens = null } = {}) {
328
+ if (!Number.isSafeInteger(bytes) || bytes <= 0) {
329
+ throw new LocalRuntimeProbeError('estimate request is invalid', 'FIT_INVALID');
330
+ }
331
+ if (contextTokens !== null && !positiveInteger(contextTokens)) {
332
+ throw new LocalRuntimeProbeError('estimate request is invalid', 'FIT_INVALID');
333
+ }
334
+ let machine;
335
+ try {
336
+ machine = await measureMachine();
337
+ } catch {
338
+ return { bytes, basis: 'weights-only', state: 'unknown', reason: 'measurement', storage: { requiredBytes: bytes, availableBytes: null }, memory: { requiredBytes: bytes, availableBytes: null }, observedAt: now().toISOString() };
339
+ }
340
+ const storageAvailable = machine?.storage?.allocatableBytes;
341
+ const memoryAvailable = machine?.memory?.freeBytes;
342
+ const base = {
343
+ bytes,
344
+ basis: 'weights-only',
345
+ contextTokens,
346
+ storage: { requiredBytes: bytes, availableBytes: Number.isSafeInteger(storageAvailable) ? storageAvailable : null },
347
+ memory: { requiredBytes: bytes, availableBytes: Number.isSafeInteger(memoryAvailable) ? memoryAvailable : null },
348
+ observedAt: now().toISOString(),
349
+ };
350
+ if (!Number.isSafeInteger(storageAvailable) || !Number.isSafeInteger(memoryAvailable)) return { ...base, state: 'unknown', reason: 'measurement' };
351
+ // ⛔ Il disco per primo, e con parole sue: lo spazio si libera, la memoria no.
352
+ if (bytes > storageAvailable) return { ...base, state: 'blocked', reason: 'storage' };
353
+ if (bytes > memoryAvailable) return { ...base, state: 'blocked', reason: 'memory' };
354
+ // Stessa soglia dichiarata di `fit()` lato UI: sopra il 90% del libero è «al limite».
355
+ if (bytes > memoryAvailable * 0.9) return { ...base, state: 'tight', reason: 'memory' };
356
+ return { ...base, state: 'compatible', reason: 'fits' };
357
+ }
358
+
359
+ async function qualify({ modelId, consent, profile = 'agent', contextTokens = profile === 'agent' ? 65_536 : 4_096 } = {}) {
360
+ if (consent !== true) throw new LocalRuntimeProbeError('qualification requires explicit consent', 'PROBE_CONSENT_REQUIRED');
361
+ const fitResult = await fit(modelId, { profile, contextTokens });
362
+ if (fitResult.state !== 'compatible') throw new LocalRuntimeProbeError(`model fit is ${fitResult.state}`, 'MODEL_NOT_COMPATIBLE');
363
+
364
+ const startedAt = clockMs();
365
+ let firstTokenAt = null;
366
+ let completed = false;
367
+ for await (const event of runtime.generateStream({
368
+ runId: `qualification-${modelId}`,
369
+ turnId: 'qualification',
370
+ modelId,
371
+ messages: [{ role: 'user', content: 'Reply with OK.' }],
372
+ parseToolCalls: false,
373
+ })) {
374
+ if (firstTokenAt === null && (event.type === 'text' || event.type === 'reasoning')) firstTokenAt = clockMs();
375
+ if (event.type === 'error') throw new LocalRuntimeProbeError(event.message || 'qualification stream failed', 'PROBE_GENERATION_FAILED');
376
+ if (event.type === 'done') completed = true;
377
+ }
378
+ const endedAt = clockMs();
379
+ if (!completed || firstTokenAt === null) throw new LocalRuntimeProbeError('qualification produced no complete response', 'PROBE_GENERATION_INCOMPLETE');
380
+
381
+ let tokensPerSecond = unknown();
382
+ if (typeof runtime.metrics === 'function') {
383
+ try { tokensPerSecond = parseTokensPerSecond(await runtime.metrics()); } catch { /* metric remains unknown */ }
384
+ }
385
+ return {
386
+ modelId,
387
+ state: 'qualified',
388
+ backend: fitResult.inspection.backend,
389
+ build: fitResult.inspection.build,
390
+ contextTokens: fitResult.context.availableTokens,
391
+ performance: {
392
+ ttftMs: fact('observed', Math.max(0, firstTokenAt - startedAt)),
393
+ tokensPerSecond,
394
+ },
395
+ durationMs: Math.max(0, endedAt - startedAt),
396
+ measuredAt: now().toISOString(),
397
+ };
398
+ }
399
+
400
+ return Object.freeze({ inspectModel, measureBackend, fit, estimateFit, qualify });
401
+ }