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,170 @@
1
+ /**
2
+ * terminal-ws.mjs — il ponte WebSocket fra il registro di PTY vere
3
+ * (`pty-terminal.mjs`) e il browser. Ledger: `.claude/LEDGER-TERMINALE-REALE.md`.
4
+ *
5
+ * ⛔⛔ Nessuna porta nuova: l'upgrade avviene sullo STESSO `http.Server`
6
+ * già bindato solo su loopback (`config.host`, validato in
7
+ * `config.mjs`) — la WS eredita lo stesso perimetro di rete di ogni
8
+ * altra rotta di questo backend, zero superficie aggiunta.
9
+ *
10
+ * ⛔ Validazione Origin ESPLICITA prima di completare l'upgrade — un
11
+ * browser NON applica la same-origin policy alle WebSocket come la
12
+ * applica a `fetch`/XHR (rischio "cross-site WebSocket hijacking",
13
+ * guardia raccolta in ricerca prima di scrivere). Qui la shell aperta
14
+ * "non ha limiti" (owner, 28/8): il confine giusto da tenere stretto è
15
+ * il TRASPORTO, non la shell stessa.
16
+ */
17
+ import { WebSocketServer } from 'ws';
18
+
19
+ import { codificaFrame, decodificaFrame, TIPO_FRAME_CONTROLLO, TIPO_FRAME_DATI } from './pty-terminal.mjs';
20
+
21
+ const PERCORSO_WS = '/api/v1/terminal/ws';
22
+
23
+ function leggiCookieGrezzo(grezzo, nome) {
24
+ if (typeof grezzo !== 'string' || grezzo === '') return null;
25
+ for (const parte of grezzo.split(';')) {
26
+ const i = parte.indexOf('=');
27
+ if (i !== -1 && parte.slice(0, i).trim() === nome) return parte.slice(i + 1).trim();
28
+ }
29
+ return null;
30
+ }
31
+
32
+ /**
33
+ * ⭐⭐⭐ W1-01 (05/9) — `?id=` non è più il `sessionId`: è il **`terminalId`**,
34
+ * e una sessione può averne più d'uno.
35
+ *
36
+ * ⛔⛔⛔ Il parametro `risolviCartella` (che tornava SEMPRE una cartella, al
37
+ * peggio la prima configurata) è stato sostituito da `risolviScheda`, che ha
38
+ * il diritto di dire **no**: `null` ⇒ upgrade rifiutato, e nessuna PTY nasce.
39
+ * Prima di oggi un id sconosciuto non veniva respinto — cadeva sul primo
40
+ * progetto — quindi con un `terminalId` libero qualunque stringa avrebbe
41
+ * aperto una shell. È la forma esatta di **CVE-2026-59224** (Open WebUI,
42
+ * 2026): un id scelto dal client che decide a quale PTY ti attacchi. E il
43
+ * terminale è l'endpoint dove un buco non è un buco: **CVE-2026-39987**
44
+ * (Marimo, 2026) era `/terminal/ws`, l'unica WebSocket senza controllo
45
+ * d'autenticazione, cioè RCE pre-auth. Entrambe lette il 05/09/2026, fonti nel
46
+ * ledger `.claude/LEDGER-W1-01-SCHEDE-TERMINALE-2026-09-05.md`.
47
+ */
48
+ export function creaGestoreTerminaleWs({ registro, originiConsentite, risolviScheda, token = null }, deps = {}) {
49
+ const WSS = deps.WebSocketServer ?? WebSocketServer;
50
+ const wss = new WSS({ noServer: true });
51
+
52
+ function gestisciUpgrade(req, socket, head) {
53
+ let url;
54
+ try {
55
+ url = new URL(req.url, 'http://localhost');
56
+ } catch {
57
+ socket.destroy();
58
+ return;
59
+ }
60
+ if (url.pathname !== PERCORSO_WS) {
61
+ socket.destroy();
62
+ return;
63
+ }
64
+ const origin = req.headers.origin;
65
+ if (origin && originiConsentite && !originiConsentite.has(origin)) {
66
+ socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
67
+ socket.destroy();
68
+ return;
69
+ }
70
+ // ⭐ 04/9, W1-10 — stesso cancello a token di /api/*: senza il cookie talos_token l'upgrade è 401, la PTY non nasce nemmeno.
71
+ if (token && leggiCookieGrezzo(req.headers.cookie, 'talos_token') !== token) {
72
+ socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
73
+ socket.destroy();
74
+ return;
75
+ }
76
+ const id = url.searchParams.get('id');
77
+ if (typeof id !== 'string' || id.length === 0 || id.length > 200) {
78
+ socket.destroy();
79
+ return;
80
+ }
81
+ /*
82
+ * ⛔⛔⛔ 05/9, W1-01 — IL CANCELLO. Si chiede al registro delle schede
83
+ * PRIMA di completare l'upgrade: un `terminalId` che il server non ha
84
+ * creato (e che non è un `sessionId` vivo) riceve 403 e il socket muore
85
+ * QUI. ⛔ `registro.apri` non viene nemmeno sfiorato: la prova che conta
86
+ * non è lo status code, è che la PTY non nasca — ed è così che i test la
87
+ * misurano (contano gli spawn, non le risposte).
88
+ */
89
+ const scheda = risolviScheda(id);
90
+ if (!scheda) {
91
+ socket.write('HTTP/1.1 403 Forbidden\r\n\r\n');
92
+ socket.destroy();
93
+ return;
94
+ }
95
+ wss.handleUpgrade(req, socket, head, (ws) => {
96
+ collega(ws, scheda);
97
+ });
98
+ }
99
+
100
+ function collega(ws, scheda) {
101
+ /* ⛔ L'id e la cartella vengono dalla SCHEDA del registro, mai dalla query: il client nomina, il server decide. */
102
+ const id = scheda.terminalId;
103
+ /*
104
+ * ⭐⭐⭐ Il segnale «ripreso», PRIMA di qualunque byte.
105
+ *
106
+ * ⛔ Senza, il client non poteva sapere se la sua shell fosse sopravvissuta:
107
+ * il ponte rigioca il backlog sia quando riaggancia una PTY viva sia
108
+ * quando il reaper l'ha chiusa e ne nasce una nuova (backlog vuoto — che è
109
+ * anche l'aspetto di una shell viva che non ha ancora stampato niente).
110
+ * L'interfaccia era costretta a dire «riconnesso, non sappiamo se la shell
111
+ * è ancora quella». Lo stato dell'arte separa l'identità della CONNESSIONE
112
+ * da quella della SESSIONE e fa dichiarare al server se ha ripreso davvero
113
+ * (un pattern noto come "connection state recovery"; ricerca del 05/09/2026,
114
+ * websocket.org/guides/reconnection/).
115
+ *
116
+ * ⛔ Va PRIMA del backlog: chi legge deve sapere di che shell sono i byte
117
+ * che sta per ricevere, non scoprirlo dopo averli scritti a schermo.
118
+ *
119
+ * ⛔ Sicuro per il monolite congelato (`public/app.js`), verificato alla
120
+ * fonte: alla riga 7702 tratta solo `evento === 'uscita'` e ignora in
121
+ * silenzio ogni altro evento di controllo — nessun `else`, nessun crash.
122
+ */
123
+ const { voce, ripresa } = registro.apriDichiarando({ id, cartella: scheda.cartella });
124
+ /* 06/9 B1 — la scheda prende il nome della shell che il server ha scelto DAVVERO (`enforcement` di `sceltaShell`), non uno indovinato dal client. */
125
+ ws.send(codificaFrame(TIPO_FRAME_CONTROLLO, JSON.stringify({ evento: 'agganciato', ripreso: ripresa, shell: voce.enforcement ?? null, comando: voce.comando ?? null })));
126
+
127
+ // ⭐ Riconnessione (F5, o WS caduta): replay del backlog PRIMA di tornare live — stesso principio del Last-Event-ID già in uso per SSE, qui su una PTY invece che su un run agente.
128
+ for (const pezzo of voce.backlog) {
129
+ ws.send(codificaFrame(TIPO_FRAME_DATI, pezzo));
130
+ }
131
+
132
+ const ascolta = (evento) => {
133
+ if (ws.readyState !== ws.OPEN) return;
134
+ if (evento.tipo === 'dati') {
135
+ ws.send(codificaFrame(TIPO_FRAME_DATI, evento.dati));
136
+ } else if (evento.tipo === 'uscita') {
137
+ ws.send(codificaFrame(TIPO_FRAME_CONTROLLO, JSON.stringify({ evento: 'uscita', codice: evento.exitCode ?? null })));
138
+ }
139
+ };
140
+ voce.ascoltatori.add(ascolta);
141
+
142
+ ws.on('message', (dati) => {
143
+ const frame = decodificaFrame(dati);
144
+ if (!frame) return; // AL CONTRARIO: un frame vuoto/di tipo ignoto si scarta, mai un crash della connessione
145
+ if (frame.tipo === TIPO_FRAME_DATI) {
146
+ registro.scrivi(id, frame.payload.toString('utf8'));
147
+ return;
148
+ }
149
+ let comando;
150
+ try {
151
+ comando = JSON.parse(frame.payload.toString('utf8'));
152
+ } catch {
153
+ return;
154
+ }
155
+ if (comando?.tipo === 'resize' && Number.isInteger(comando.cols) && Number.isInteger(comando.rows)) {
156
+ registro.ridimensiona(id, comando.cols, comando.rows);
157
+ }
158
+ });
159
+
160
+ ws.on('close', () => {
161
+ voce.ascoltatori.delete(ascolta);
162
+ registro.segnaDisconnesso(id);
163
+ });
164
+
165
+ // ⛔ Un errore di trasporto non deve propagarsi come un'eccezione non gestita del processo server.
166
+ ws.on('error', () => { /* 'close' segue comunque, la pulizia avviene lì */ });
167
+ }
168
+
169
+ return { gestisciUpgrade };
170
+ }
@@ -0,0 +1,299 @@
1
+ /**
2
+ * tool-forge-store.mjs — FASE N, nono e ultimo sistema (30/8): Tool
3
+ * Forge, "fetta onesta" (owner, AskUserQuestion: "Fetta onesta
4
+ * (consigliato)"). Letto alla fonte PRIMA di disegnare: `mobile/src/
5
+ * lib/tools/dynamic/forgeRegistryRepository.ts` (269 righe, SQLite
6
+ * device-wide con versioning) — questo store è la traduzione onesta
7
+ * su file, come ogni altro store di questa fase: un file per tool
8
+ * installato, mai un database.
9
+ *
10
+ * ⭐⭐⭐ Tool Forge è GLOBALE, come Notes/Tasks/Memory — non PER-PROGETTO
11
+ * come Libreria/Deep Research. Verificato non presunto: le 8 capability
12
+ * che un tool forgiato può chiamare (tasks, notes, memory — vedi
13
+ * CAPACITA_FORGE nel kernel) operano TUTTE su store GLOBALI
14
+ * (.tasks-store, .notes-store, .memory-store) — un tool forgiato creato
15
+ * mentre si lavora sul progetto A deve restare disponibile e utile
16
+ * anche lavorando sul progetto B, esattamente come una nota o
17
+ * un'attività. Storage: `.tool-forge-store/<id>.json`, accanto a
18
+ * `.notes-store/`/`.tasks-store/`/`.memory-store/`.
19
+ *
20
+ * ⛔⛔⛔ `cartella` qui è SEMPRE la cartella FINALE, mai un genitore da
21
+ * suffissare — stesso contratto ESATTO di notes-store.mjs/
22
+ * tasks-store.mjs/memory-store.mjs (verificato leggendo notes-store.mjs
23
+ * riga per riga, non presunto: `elencaNote` fa `join(cartella, nome)`,
24
+ * MAI `join(cartella, CARTELLA_NOTE, nome)`). Il default REALE vive in
25
+ * session-registry.mjs (`cartellaForge = .../.tool-forge-store/`,
26
+ * GIÀ col suffisso) — un secondo `join(cartella, CARTELLA_FORGE)` qui
27
+ * dentro avrebbe prodotto un doppio annidamento
28
+ * (`.tool-forge-store/.tool-forge-store/`).
29
+ *
30
+ * ⛔⛔⛔ Bug reale trovato E corretto dal vivo (30/8), non da lettura: la
31
+ * prima versione di questo file FACEVA quel doppio join — un
32
+ * `tool_create` chiamato da un modello vero tornava "Created..." (il
33
+ * messaggio di successo, corretto) ma il file non appariva MAI in
34
+ * `.tool-forge-store/` (la cartella vera, controllata sul disco): la
35
+ * scrittura andava un livello più in profondità
36
+ * (`.tool-forge-store/.tool-forge-store/<id>.json`), mai controllata da
37
+ * nessun test perché ogni test di QUESTO file passava una `cartella`
38
+ * generica ("/tmp/x") che non rivelava la duplicazione — solo
39
+ * confrontando il contratto con notes-store.mjs (il precedente diretto)
40
+ * il difetto è emerso.
41
+ *
42
+ * ⛔⛔⛔ Ogni tool installato nasce SEMPRE `abilitato:false` — porto
43
+ * diretto del vincolo mobile ("installed but stays DISABLED until the
44
+ * user turns it on"). Abilitare/disabilitare NON è mai un tool del
45
+ * modello (verificato in toolControlCatalog.ts: tool_create è l'UNICO
46
+ * tool_* chat-facing su ENTRAMBE le piattaforme) — solo un'azione
47
+ * owner-facing dal pannello Capability hub, `abilitaToolForgiato` sotto.
48
+ *
49
+ * Stile DI: stesso pattern di notes-store.mjs — funzioni async con
50
+ * `deps` opzionali per i test, mai un vero filesystem mockato altrove.
51
+ */
52
+ import { promises as fsp } from 'node:fs';
53
+ import { join } from 'node:path';
54
+
55
+ export class ToolForgeStoreError extends Error {
56
+ constructor(message, code = 'FORGE_INVALID') {
57
+ super(message);
58
+ this.name = 'ToolForgeStoreError';
59
+ this.code = code;
60
+ }
61
+ }
62
+
63
+ /** Nome canonico della cartella globale — MAI usato per un join interno qui (vedi la doc di testa): solo documentazione/riferimento esterno, come CARTELLA_NOTE in notes-store.mjs. */
64
+ export const CARTELLA_FORGE = '.tool-forge-store';
65
+ /** Censito da forgeRegistryRepository.ts mobile (MAX_INSTALLED_TOOLS = 64) — non inventato. */
66
+ export const MAX_TOOL_INSTALLATI = 64;
67
+
68
+ function percorsoVoce(cartella, id) {
69
+ return join(cartella, `${id}.json`);
70
+ }
71
+
72
+ /**
73
+ * @returns {Promise<Array<object>>} — [] se la cartella non esiste
74
+ * ancora (mai un errore: nessun tool forgiato è uno stato onesto).
75
+ * Più recenti prime.
76
+ */
77
+ export async function elencaToolForgiati({ cartella }, deps = {}) {
78
+ const readdirFn = deps.readdirFn ?? fsp.readdir;
79
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
80
+ let file;
81
+ try {
82
+ file = await readdirFn(cartella);
83
+ } catch {
84
+ return [];
85
+ }
86
+ const strumenti = [];
87
+ for (const nomeFile of file) {
88
+ if (!nomeFile.endsWith('.json')) continue;
89
+ try {
90
+ strumenti.push(JSON.parse(await readFileFn(join(cartella, nomeFile), 'utf8')));
91
+ } catch {
92
+ // ⛔ una voce corrotta non impedisce di vedere le altre — stesso principio di leggiRegistro (session-store.mjs).
93
+ }
94
+ }
95
+ strumenti.sort((a, b) => String(b.installatoAlle || '').localeCompare(String(a.installatoAlle || '')));
96
+ return strumenti;
97
+ }
98
+
99
+ /** @returns {Promise<object|null>} — null se l'id non esiste. */
100
+ export async function leggiToolForgiato({ cartella, id }, deps = {}) {
101
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
102
+ try {
103
+ return JSON.parse(await readFileFn(percorsoVoce(cartella, id), 'utf8'));
104
+ } catch (errore) {
105
+ if (errore?.code === 'ENOENT') return null;
106
+ throw new ToolForgeStoreError(`${id}: metadata presente ma illeggibile: ${errore.message}`, 'FORGE_READ_FAILED');
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Installa un manifest GIÀ VALIDATO (dal chiamante, via
112
+ * validaManifestForge del kernel — questo store non rivalida, si
113
+ * fida del chiamante come library-store.mjs si fida di
114
+ * sanificaNomeLibreria). Rifiuta un id già esistente (porto diretto
115
+ * del messaggio mobile — v1 non ha un tool_update, un secondo
116
+ * tool_create sullo stesso id è sempre un conflitto, mai un
117
+ * aggiornamento silenzioso) e un registro pieno (porto diretto del
118
+ * messaggio mobile, MAX_TOOL_INSTALLATI censito non inventato).
119
+ */
120
+ export async function installaToolForgiato({ cartella, manifest, capacita, azioni, rischio }, deps = {}) {
121
+ const mkdirFn = deps.mkdirFn ?? fsp.mkdir;
122
+ const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
123
+ const readdirFn = deps.readdirFn ?? fsp.readdir;
124
+ if (!manifest || typeof manifest.id !== 'string' || manifest.id.length === 0) {
125
+ throw new ToolForgeStoreError('a tool manifest needs an id', 'FORGE_INVALID');
126
+ }
127
+ let esistenti;
128
+ try {
129
+ esistenti = await readdirFn(cartella);
130
+ } catch {
131
+ esistenti = [];
132
+ }
133
+ if (esistenti.includes(`${manifest.id}.json`)) {
134
+ throw new ToolForgeStoreError(`a tool with id "${manifest.id}" already exists — pick a different id`, 'FORGE_VERSION_NOT_NEWER');
135
+ }
136
+ if (esistenti.filter((nome) => nome.endsWith('.json')).length >= MAX_TOOL_INSTALLATI) {
137
+ throw new ToolForgeStoreError('the tool registry on this device is full — remove an unused tool first', 'FORGE_REGISTRY_FULL');
138
+ }
139
+ await mkdirFn(cartella, { recursive: true });
140
+ const voce = {
141
+ id: manifest.id, manifest, capacita, azioni, rischio,
142
+ abilitato: false, installatoAlle: new Date().toISOString(),
143
+ };
144
+ await writeFileFn(percorsoVoce(cartella, manifest.id), JSON.stringify(voce, null, 2), 'utf8');
145
+ return voce;
146
+ }
147
+
148
+ /**
149
+ * L'UNICA mutazione owner-facing di questa fase (vedi la doc di testa
150
+ * sul perché) — mai chiamata da un tool del modello. `null` se l'id
151
+ * non esiste.
152
+ */
153
+ export async function abilitaToolForgiato({ cartella, id, abilitato }, deps = {}) {
154
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
155
+ const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
156
+ let voce;
157
+ try {
158
+ voce = JSON.parse(await readFileFn(percorsoVoce(cartella, id), 'utf8'));
159
+ } catch (errore) {
160
+ if (errore?.code === 'ENOENT') return null;
161
+ throw new ToolForgeStoreError(`${id}: metadata presente ma illeggibile: ${errore.message}`, 'FORGE_READ_FAILED');
162
+ }
163
+ const aggiornata = { ...voce, abilitato: Boolean(abilitato) };
164
+ await writeFileFn(percorsoVoce(cartella, id), JSON.stringify(aggiornata, null, 2), 'utf8');
165
+ return aggiornata;
166
+ }
167
+
168
+ /** `null` se l'id non esiste già (idempotente, mai un'eccezione — stesso principio di eliminaVoce/eliminaRicerca). */
169
+ export async function eliminaToolForgiato({ cartella, id }, deps = {}) {
170
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
171
+ const rmFn = deps.rmFn ?? fsp.rm;
172
+ try {
173
+ await readFileFn(percorsoVoce(cartella, id), 'utf8');
174
+ } catch (errore) {
175
+ if (errore?.code === 'ENOENT') return null;
176
+ throw new ToolForgeStoreError(`${id}: metadata presente ma illeggibile: ${errore.message}`, 'FORGE_READ_FAILED');
177
+ }
178
+ await rmFn(percorsoVoce(cartella, id), { force: true });
179
+ return { id };
180
+ }
181
+
182
+
183
+ /* M10-C — owner-managed revision history. The active <id>.json remains the only runtime source. */
184
+ const OWNER_HISTORY_DIR = '.owner-versions';
185
+ const OWNER_HISTORY_LIMIT = 10;
186
+ const OWNER_AUDIT_LIMIT = 256;
187
+ const OWNER_ID = /^[a-z0-9][a-z0-9_-]{2,63}$/;
188
+
189
+ function ownerDir(cartella, id) { return join(cartella, OWNER_HISTORY_DIR, id); }
190
+ function ownerVersionsDir(cartella, id) { return join(ownerDir(cartella, id), 'versions'); }
191
+ function ownerMetaPath(cartella, id) { return join(ownerDir(cartella, id), 'meta.json'); }
192
+ function ownerAuditPath(cartella, id) { return join(ownerDir(cartella, id), 'audit.jsonl'); }
193
+ function ownerVersionPath(cartella, id, revision) { return join(ownerVersionsDir(cartella, id), revision + '.json'); }
194
+ function validOwnerInput(id, revision) {
195
+ if (typeof id !== 'string' || !OWNER_ID.test(id)) throw new ToolForgeStoreError('owner-managed tool id is invalid', 'FORGE_INVALID');
196
+ if (!Number.isSafeInteger(revision) || revision < 1) throw new ToolForgeStoreError('owner revision must be a positive integer', 'FORGE_INVALID');
197
+ }
198
+ async function readOwnerMeta(cartella, id, deps = {}) {
199
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
200
+ try {
201
+ const value = JSON.parse(await readFileFn(ownerMetaPath(cartella, id), 'utf8'));
202
+ return { maxRevision: Number.isSafeInteger(value?.maxRevision) && value.maxRevision > 0 ? value.maxRevision : 0 };
203
+ } catch (error) {
204
+ if (error?.code === 'ENOENT') return { maxRevision: 0 };
205
+ throw new ToolForgeStoreError(`${id}: owner version metadata unreadable: ${error.message}`, 'FORGE_READ_FAILED');
206
+ }
207
+ }
208
+ async function appendOwnerAudit(cartella, id, event, deps = {}) {
209
+ const mkdirFn = deps.mkdirFn ?? fsp.mkdir;
210
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
211
+ const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
212
+ await mkdirFn(ownerDir(cartella, id), { recursive: true });
213
+ let rows = [];
214
+ try { rows = (await readFileFn(ownerAuditPath(cartella, id), 'utf8')).split('\n').filter(Boolean).map((line) => JSON.parse(line)); }
215
+ catch (error) { if (error?.code !== 'ENOENT') throw new ToolForgeStoreError(`${id}: owner audit unreadable: ${error.message}`, 'FORGE_READ_FAILED'); }
216
+ rows.push(event);
217
+ rows = rows.slice(-OWNER_AUDIT_LIMIT);
218
+ await writeFileFn(ownerAuditPath(cartella, id), rows.map((row) => JSON.stringify(row)).join('\n') + '\n', 'utf8');
219
+ }
220
+ async function pruneOwnerVersions(cartella, id, deps = {}) {
221
+ const readdirFn = deps.readdirFn ?? fsp.readdir;
222
+ const rmFn = deps.rmFn ?? fsp.rm;
223
+ let names = [];
224
+ try { names = await readdirFn(ownerVersionsDir(cartella, id)); } catch { return; }
225
+ const revisions = names.map((name) => /^([1-9][0-9]*)\.json$/.exec(name)).filter(Boolean).map((match) => Number(match[1])).filter(Number.isSafeInteger).sort((a, b) => a - b);
226
+ for (const revision of revisions.slice(0, Math.max(0, revisions.length - OWNER_HISTORY_LIMIT))) await rmFn(ownerVersionPath(cartella, id, revision), { force: true });
227
+ }
228
+
229
+ /**
230
+ * Owner-only versioned install/update. This intentionally does NOT replace installaToolForgiato:
231
+ * model tool_create remains create-only and same-id conflicts forever.
232
+ */
233
+ export async function installaVersioneToolForgiatoOwner({ cartella, revision, manifest, capacita, azioni, rischio, evidence }, deps = {}) {
234
+ validOwnerInput(manifest?.id, revision);
235
+ const mkdirFn = deps.mkdirFn ?? fsp.mkdir;
236
+ const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
237
+ const readdirFn = deps.readdirFn ?? fsp.readdir;
238
+ const active = await leggiToolForgiato({ cartella, id: manifest.id }, deps);
239
+ if (active && active.ownerManaged !== true) throw new ToolForgeStoreError(`tool "${manifest.id}" exists with legacy/model provenance and cannot be silently adopted`, 'FORGE_OWNER_ADOPTION_REQUIRED');
240
+ if (!active) {
241
+ let existing = []; try { existing = await readdirFn(cartella); } catch {}
242
+ if (existing.filter((name) => name.endsWith('.json')).length >= MAX_TOOL_INSTALLATI) throw new ToolForgeStoreError('the tool registry on this device is full — remove an unused tool first', 'FORGE_REGISTRY_FULL');
243
+ }
244
+ const meta = await readOwnerMeta(cartella, manifest.id, deps);
245
+ const maxEver = Math.max(meta.maxRevision, Number.isSafeInteger(active?.ownerRevision) ? active.ownerRevision : 0);
246
+ if (revision <= maxEver) throw new ToolForgeStoreError(`owner revision ${revision} is not newer than max-ever revision ${maxEver}`, 'FORGE_VERSION_NOT_NEWER');
247
+ await mkdirFn(ownerVersionsDir(cartella, manifest.id), { recursive: true });
248
+ const at = new Date().toISOString();
249
+ const record = {
250
+ id: manifest.id, manifest, capacita: Array.isArray(capacita) ? capacita : [], azioni: Array.isArray(azioni) ? azioni : [],
251
+ rischio: typeof rischio === 'string' ? rischio : 'R1', abilitato: false, installatoAlle: at,
252
+ ownerManaged: true, ownerRevision: revision, evidence: evidence ?? null,
253
+ };
254
+ try { await writeFileFn(ownerVersionPath(cartella, manifest.id, revision), JSON.stringify(record, null, 2), { encoding: 'utf8', flag: 'wx' }); }
255
+ catch (error) { if (error?.code === 'EEXIST') throw new ToolForgeStoreError(`owner revision ${revision} already exists`, 'FORGE_VERSION_NOT_NEWER'); throw error; }
256
+ await mkdirFn(cartella, { recursive: true });
257
+ await writeFileFn(percorsoVoce(cartella, manifest.id), JSON.stringify(record, null, 2), 'utf8');
258
+ await writeFileFn(ownerMetaPath(cartella, manifest.id), JSON.stringify({ maxRevision: revision }, null, 2), 'utf8');
259
+ await appendOwnerAudit(cartella, manifest.id, { kind: active ? 'update' : 'install', revision, at }, deps);
260
+ await pruneOwnerVersions(cartella, manifest.id, deps);
261
+ return record;
262
+ }
263
+
264
+ export async function elencaVersioniToolForgiatoOwner({ cartella, id }, deps = {}) {
265
+ if (typeof id !== 'string' || !OWNER_ID.test(id)) throw new ToolForgeStoreError('owner-managed tool id is invalid', 'FORGE_INVALID');
266
+ const readdirFn = deps.readdirFn ?? fsp.readdir;
267
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
268
+ let names = []; try { names = await readdirFn(ownerVersionsDir(cartella, id)); } catch (error) { if (error?.code === 'ENOENT') return []; throw error; }
269
+ const revisions = names.map((name) => /^([1-9][0-9]*)\.json$/.exec(name)).filter(Boolean).map((match) => Number(match[1])).filter(Number.isSafeInteger).sort((a, b) => a - b);
270
+ const rows = [];
271
+ for (const revision of revisions) {
272
+ try { const value = JSON.parse(await readFileFn(ownerVersionPath(cartella, id, revision), 'utf8')); rows.push({ ...value, revision }); }
273
+ catch (error) { throw new ToolForgeStoreError(`${id} revision ${revision} unreadable: ${error.message}`, 'FORGE_READ_FAILED'); }
274
+ }
275
+ return rows;
276
+ }
277
+
278
+ export async function leggiAuditToolForgiatoOwner({ cartella, id }, deps = {}) {
279
+ if (typeof id !== 'string' || !OWNER_ID.test(id)) throw new ToolForgeStoreError('owner-managed tool id is invalid', 'FORGE_INVALID');
280
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
281
+ try { return (await readFileFn(ownerAuditPath(cartella, id), 'utf8')).split('\n').filter(Boolean).map((line) => JSON.parse(line)); }
282
+ catch (error) { if (error?.code === 'ENOENT') return []; throw new ToolForgeStoreError(`${id}: owner audit unreadable: ${error.message}`, 'FORGE_READ_FAILED'); }
283
+ }
284
+
285
+ export async function ripristinaVersioneToolForgiatoOwner({ cartella, id, revision }, deps = {}) {
286
+ validOwnerInput(id, revision);
287
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
288
+ const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
289
+ const active = await leggiToolForgiato({ cartella, id }, deps);
290
+ if (!active) throw new ToolForgeStoreError(`tool "${id}" does not exist`, 'FORGE_NOT_FOUND');
291
+ if (active.ownerManaged !== true) throw new ToolForgeStoreError(`tool "${id}" is not owner-managed`, 'FORGE_OWNER_ADOPTION_REQUIRED');
292
+ let snapshot;
293
+ try { snapshot = JSON.parse(await readFileFn(ownerVersionPath(cartella, id, revision), 'utf8')); }
294
+ catch (error) { if (error?.code === 'ENOENT') throw new ToolForgeStoreError(`owner revision ${revision} is not retained`, 'FORGE_VERSION_NOT_FOUND'); throw new ToolForgeStoreError(`${id} revision ${revision} unreadable: ${error.message}`, 'FORGE_READ_FAILED'); }
295
+ const restored = { ...snapshot, abilitato: false };
296
+ await writeFileFn(percorsoVoce(cartella, id), JSON.stringify(restored, null, 2), 'utf8');
297
+ await appendOwnerAudit(cartella, id, { kind: 'rollback', revision, at: new Date().toISOString() }, deps);
298
+ return restored;
299
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Rendere JSON Schema uno schema che JSON Schema non è.
3
+ *
4
+ * ## Il guasto che l'ha fatto nascere, misurato
5
+ *
6
+ * Owner 03/9: «il modello locale dà errore, quindi non funziona». Il motore
7
+ * rispondeva:
8
+ *
9
+ * HTTP 400 — Unable to generate parser for this template.
10
+ * JSON schema conversion failed: Unrecognized schema: "string"
11
+ * Unrecognized schema: "string"
12
+ *
13
+ * Due volte. Ed erano esattamente due: un manifest Forge in
14
+ * `.tool-forge-store/promemoria-trattativa.json` dichiara
15
+ *
16
+ * { required: ['nome_contatto'],
17
+ * properties: { nome_contatto: 'string', dettagli_contatto: 'string' } }
18
+ *
19
+ * cioè le proprietà come STRINGHE NUDE invece di `{ type: 'string' }`, e senza
20
+ * `type: 'object'` in testa. È un'abbreviazione comoda, ed è nostra: la scrive
21
+ * chi forgia un attrezzo. Solo che non è JSON Schema.
22
+ *
23
+ * ## Perché non si era mai visto
24
+ *
25
+ * OpenRouter non costruisce grammatiche: riceve lo schema, lo passa al
26
+ * modello come testo e non protesta. llama.cpp invece deve trasformarlo in
27
+ * GBNF per vincolare l'output, quindi lo legge davvero — e si ferma.
28
+ * ⇒ Il difetto era lì da sempre, e a scoprirlo è stato il primo motore che
29
+ * quello schema lo legge sul serio. ⛔ Non è «un limite del locale»: è una
30
+ * cosa rotta nostra che solo il locale è abbastanza severo da vedere.
31
+ *
32
+ * ⭐ Ricerca 03/9 (llama.cpp PR #17572; docs LLGuidance): il convertitore
33
+ * JSON-Schema→GBNF di llama.cpp copre un SOTTOINSIEME e rifiuta il resto con
34
+ * questo messaggio; upstream sta cambiando il 500 in 400 proprio per non far
35
+ * ritentare a vuoto. LLGuidance coprirebbe di più ma vuole Rust.
36
+ * ⭐ E il mobile aveva già scelto il principio giusto per lo stesso problema
37
+ * (`localToolPromptProtocol.ts`): il trasporto si sceglie dalle capacità
38
+ * MISURATE, «mai da un nome di file o da una famiglia indovinata».
39
+ *
40
+ * ## Cosa fa questo modulo, e cosa NON fa
41
+ *
42
+ * Espande le abbreviazioni verso il JSON Schema vero. ⛔ Non inventa vincoli
43
+ * e non butta via proprietà che non capisce: uno schema sconosciuto resta com'è
44
+ * e sarà il motore a dire la sua. Togliere in silenzio un parametro
45
+ * cambierebbe cosa il modello può chiedere, ed è peggio di un errore visibile.
46
+ */
47
+
48
+ /** I nomi di tipo che JSON Schema conosce. Una stringa nuda che ne è uno è un'abbreviazione. */
49
+ const TIPI_JSON = new Set(['string', 'number', 'integer', 'boolean', 'object', 'array', 'null']);
50
+
51
+ /**
52
+ * @param {unknown} schema
53
+ * @returns {unknown} lo stesso schema, con le abbreviazioni espanse
54
+ */
55
+ export function normalizzaSchemaAttrezzo(schema) {
56
+ // `'string'` → `{ type: 'string' }`. È l'abbreviazione che ha rotto tutto.
57
+ if (typeof schema === 'string') return TIPI_JSON.has(schema) ? { type: schema } : schema;
58
+ if (!schema || typeof schema !== 'object') return schema;
59
+ if (Array.isArray(schema)) return schema.map(normalizzaSchemaAttrezzo);
60
+
61
+ const uscita = { ...schema };
62
+ if (uscita.properties && typeof uscita.properties === 'object' && !Array.isArray(uscita.properties)) {
63
+ uscita.properties = Object.fromEntries(
64
+ Object.entries(uscita.properties).map(([nome, valore]) => [nome, normalizzaSchemaAttrezzo(valore)]),
65
+ );
66
+ /*
67
+ * ⛔ Un oggetto con `properties` ma senza `type` è la seconda metà della
68
+ * stessa abbreviazione: chi scrive `{ properties: {...} }` intende un
69
+ * oggetto, e il convertitore di grammatica non tira a indovinare.
70
+ */
71
+ if (uscita.type === undefined) uscita.type = 'object';
72
+ }
73
+ if (uscita.items !== undefined) uscita.items = normalizzaSchemaAttrezzo(uscita.items);
74
+ if (uscita.additionalProperties !== undefined && typeof uscita.additionalProperties !== 'boolean') {
75
+ uscita.additionalProperties = normalizzaSchemaAttrezzo(uscita.additionalProperties);
76
+ }
77
+ for (const combinatore of ['anyOf', 'oneOf', 'allOf']) {
78
+ if (Array.isArray(uscita[combinatore])) uscita[combinatore] = uscita[combinatore].map(normalizzaSchemaAttrezzo);
79
+ }
80
+ if (uscita.$defs && typeof uscita.$defs === 'object') {
81
+ uscita.$defs = Object.fromEntries(Object.entries(uscita.$defs).map(([k, v]) => [k, normalizzaSchemaAttrezzo(v)]));
82
+ }
83
+ if (uscita.definitions && typeof uscita.definitions === 'object') {
84
+ uscita.definitions = Object.fromEntries(Object.entries(uscita.definitions).map(([k, v]) => [k, normalizzaSchemaAttrezzo(v)]));
85
+ }
86
+ return uscita;
87
+ }
88
+
89
+ /**
90
+ * Uno schema di ingresso valido anche quando il manifest non ne dichiara uno.
91
+ * ⛔ `{}` vuoto è ammesso da JSON Schema ma non dice niente a una grammatica:
92
+ * meglio un oggetto senza proprietà, che è ciò che l'attrezzo intende.
93
+ */
94
+ export function schemaIngressoAttrezzo(schema) {
95
+ if (schema === undefined || schema === null) return { type: 'object', properties: {} };
96
+ const normalizzato = normalizzaSchemaAttrezzo(schema);
97
+ if (!normalizzato || typeof normalizzato !== 'object' || Array.isArray(normalizzato)) return { type: 'object', properties: {} };
98
+ if (normalizzato.type === undefined && normalizzato.properties === undefined) return { ...normalizzato, type: 'object', properties: {} };
99
+ return normalizzato;
100
+ }