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,132 @@
1
+ /** Shared lifecycle primitives for HTTP requests, SSE streams and shutdown. */
2
+
3
+ /*
4
+ * ⛔⛔⛔ P0 · punto 7 (16/09/2026) — QUI C'ERA UNA MINA: `createRequestLifecycle`, RIMOSSA.
5
+ *
6
+ * Era una funzione che abortiva una richiesta dopo `timeoutMs = 30_000`, e **non la usava
7
+ * nessuno**: misurato con `grep -rn createRequestLifecycle harness-ui --include=*.mjs` — due sole
8
+ * occorrenze, la sua definizione e il suo test. Zero chiamanti in tre mesi.
9
+ *
10
+ * ⛔ Perché toglierla invece di lasciarla lì: era pronta per essere cablata dal primo che avesse
11
+ * cercato «lifecycle» per la rotta `/events`. E cablarla avrebbe ucciso OGNI sessione SSE dopo
12
+ * trenta secondi — cioè avrebbe rimesso, in un colpo solo, il tetto di durata che questo punto
13
+ * sta togliendo, dalla parte del server invece che da quella del fornitore. Un attrezzo che fa
14
+ * la cosa sbagliata e aspetta paziente è peggio di un attrezzo mancante: il secondo lo scrivi
15
+ * guardando il problema, il primo lo adotti perché c'era già.
16
+ * ⭐ Ciò che serviva davvero — chiudere quando il client se ne va — `createSseSession` lo fa già,
17
+ * qui sotto, senza nessun orologio: ascolta `close` sulla risposta e l'abort del segnale.
18
+ * ⛔ Il cancello che impedisce di rimetterla sta in `tests/timeout-generazione-p0.test.mjs`
19
+ * (P0-D-13), non nella memoria di qualcuno.
20
+ */
21
+
22
+ /**
23
+ * ⛔⛔ P0 · punto 7 (16/09/2026) — I TEMPI DEL SERVER, DICHIARATI INVECE CHE SUBITI.
24
+ *
25
+ * `server.mjs` faceva `createServer(app)` e `listen()` senza toccare nessuno dei quattro tempi:
26
+ * quelli attivi erano quelli che capitavano. Misurati su **Node v24.18.0** (script su file, non
27
+ * `node -e`), il 16/09/2026:
28
+ * server.timeout = 0 · headersTimeout = 60000 · keepAliveTimeout = 5000 · requestTimeout = 300000
29
+ * Sono i default di OGGI, e cambiano fra le versioni di Node — `server.timeout` valeva 120 s fino a
30
+ * Node 13. Una rotta SSE che deve reggere un ragionamento lungo non può dipendere da quale Node è
31
+ * installato sul computer di chi apre l'app.
32
+ *
33
+ * I quattro valori, e perché:
34
+ * · `timeout: 0` — nessun guardiano di inattività sul SOCKET. È il valore che rende possibile una
35
+ * risposta SSE lunga: qualunque numero qui ucciderebbe la chat mentre il modello pensa. Il
36
+ * canale non resta comunque muto, perché il battito scrive ogni 15 s.
37
+ * · `keepAliveTimeout: 72_000` — quanto si tiene aperta una connessione INUTILIZZATA fra due
38
+ * richieste. Il default di 5 s fa ricostruire il socket di continuo a un'interfaccia che
39
+ * chiacchiera; 72 s sono poco meno di cinque battiti, cioè comodamente oltre il ritmo con cui
40
+ * l'app parla col server.
41
+ * · `headersTimeout: 75_000` — **deve restare maggiore di `keepAliveTimeout`**: se fosse minore,
42
+ * Node può chiudere un socket riusato mentre la richiesta successiva è per strada, e il browser
43
+ * vede un `ECONNRESET` senza colpevole. Tre secondi di margine sopra il keep-alive.
44
+ * · `requestTimeout: 300_000` — tempo per ricevere la RICHIESTA completa (intestazioni + corpo),
45
+ * non la risposta: non tocca lo streaming in uscita, e a zero aprirebbe uno slowloris. Resta il
46
+ * valore di Node, ma scritto, così nessuno deve andare a cercarlo.
47
+ *
48
+ * ⛔ Stanno QUI e non dentro `server.mjs` perché la prova che il battito regge oltre il vecchio
49
+ * muro (`P0-D-12`, e la misura lunga in `tests/aiuto/`) deve usare gli STESSI numeri del
50
+ * prodotto: due copie divergerebbero, e la prova continuerebbe a essere verde su numeri che
51
+ * nessuno usa più.
52
+ */
53
+ export const TEMPI_SERVER_HTTP = Object.freeze({
54
+ timeout: 0,
55
+ keepAliveTimeout: 72_000,
56
+ headersTimeout: 75_000,
57
+ requestTimeout: 300_000,
58
+ });
59
+
60
+ /**
61
+ * Applica i tempi dichiarati sopra a un server HTTP, e li restituisce come letti DAL server —
62
+ * non come li abbiamo chiesti.
63
+ *
64
+ * ⛔ Il ritorno è la lettura vera (`server.timeout`, …) perché un assegnamento che non attecchisse
65
+ * (una versione di Node che rifiuta un valore, un server finto) lascerebbe il chiamante convinto
66
+ * di aver deciso qualcosa. «L'ho scritto» e «è così» sono due misure diverse.
67
+ */
68
+ export function applicaTempiDelServer(server, tempi = TEMPI_SERVER_HTTP) {
69
+ if (!server) return null;
70
+ for (const [nome, valore] of Object.entries(tempi)) server[nome] = valore;
71
+ return Object.freeze(Object.fromEntries(Object.keys(tempi).map((nome) => [nome, server[nome]])));
72
+ }
73
+
74
+ export function createSseSession({ response, heartbeatMs = 15_000, signal = null, lastEventId = 0, headers = {}, setIntervalFn = setInterval, clearIntervalFn = clearInterval } = {}) {
75
+ let closed = false;
76
+ let heartbeat = null;
77
+ let onResponseClose;
78
+ let onAbort;
79
+ const send = (event) => {
80
+ if (closed || response?.writableEnded || response?.destroyed) return false;
81
+ if (typeof event?._sequenza === 'number' && event._sequenza <= lastEventId) return false;
82
+ if (typeof event?._sequenza === 'number') response.write(`id: ${event._sequenza}\n`);
83
+ response.write(`data: ${JSON.stringify(event)}\n\n`);
84
+ return true;
85
+ };
86
+ const close = () => {
87
+ if (closed) return;
88
+ closed = true;
89
+ if (heartbeat !== null) clearIntervalFn(heartbeat);
90
+ response?.removeListener?.('close', onResponseClose);
91
+ signal?.removeEventListener?.('abort', onAbort);
92
+ };
93
+ const start = () => {
94
+ if (closed) return;
95
+ response.writeHead(200, {
96
+ ...headers,
97
+ 'Content-Type': 'text/event-stream; charset=utf-8',
98
+ 'Cache-Control': 'no-store',
99
+ Connection: 'keep-alive',
100
+ });
101
+ response.socket?.setNoDelay?.(true);
102
+ response.write(':ok\n\n');
103
+ onResponseClose = close;
104
+ response.once?.('close', onResponseClose);
105
+ onAbort = close;
106
+ signal?.addEventListener?.('abort', onAbort, { once: true });
107
+ heartbeat = setIntervalFn(() => {
108
+ if (closed || response.writableEnded || response.destroyed) { close(); return; }
109
+ response.write(':battito\n\n');
110
+ }, heartbeatMs);
111
+ heartbeat?.unref?.();
112
+ };
113
+ return Object.freeze({ start, send, close, get closed() { return closed; } });
114
+ }
115
+
116
+ export async function closeRuntimeResources(reason, { resources = [], logger = null } = {}) {
117
+ const pending = [];
118
+ for (const resource of resources) {
119
+ try {
120
+ const result = typeof resource?.abort === 'function' ? resource.abort(reason)
121
+ : typeof resource?.stop === 'function' ? resource.stop(reason)
122
+ : typeof resource?.close === 'function' ? resource.close(reason)
123
+ : typeof resource?.destroy === 'function' ? resource.destroy(reason) : undefined;
124
+ if (result && typeof result.then === 'function') pending.push(Promise.resolve(result).catch((error) => {
125
+ try { logger?.warn?.(`[runtime-close] ${error?.code || 'cleanup-failed'}`); } catch { /* cleanup best effort */ }
126
+ }));
127
+ } catch (error) {
128
+ try { logger?.warn?.(`[runtime-close] ${error?.code || 'cleanup-failed'}`); } catch { /* cleanup best effort */ }
129
+ }
130
+ }
131
+ await Promise.all(pending);
132
+ }
@@ -0,0 +1,27 @@
1
+ /*
2
+ * ⛔⛔⛔ 14/09/2026 — L'ID DI UN ARCHIVIO (Note/Attività/Memoria) ARRIVA DA FUORI, e finiva dritto nel percorso di un file.
3
+ *
4
+ * Trovato dalla review ingegneristica del 13/09 (F01) e CONFERMATO dal vivo su un banco a porta effimera: una
5
+ * `DELETE /api/v1/sessions/<id>/notes/..%5C<nome>` rispondeva 200 e cancellava un file FUORI da `.notes-store/`. La causa:
6
+ * `http-app.mjs` fa `decodeURIComponent` dell'id e lo passa a `percorsoDi(cartella, id) = join(cartella, id + '.json')`,
7
+ * senza nessun controllo — e lo stesso id arriva ANCHE dagli attrezzi del modello (`agent-service.mjs`, `argomenti?.id`),
8
+ * quindi la porta era aperta pure a un prompt-injection, non solo a una richiesta HTTP sul loopback.
9
+ *
10
+ * La Libreria aveva già chiuso lo stesso buco il 10/09 (`library-store.mjs#idVoceLibreriaValido`), con QUESTA identica
11
+ * grammatica. Le tre restanti no. La difesa sta nel magazzino, in un posto solo, così vale per tutti e due i chiamanti
12
+ * (la persona via HTTP e il modello via attrezzo) e non c'è una seconda copia che un giorno diverge.
13
+ *
14
+ * ⛔ Un id fuori grammatica NON è un'eccezione: `percorsoDi` torna `null` e chi lo chiama si comporta come per un id
15
+ * assente — «questa voce non c'è». È vero (un id con una barra o `..` dentro non può nominare nessuna voce reale),
16
+ * tiene l'idempotenza delle `elimina*` (un id già assente non è un errore) e non arriva in faccia al modello come un
17
+ * guasto nuovo per una chiamata che oggi si chiude con un «non trovato». È la stessa scelta della Libreria.
18
+ *
19
+ * ⛔ La grammatica è quella della Libreria, non quella (più stretta, coi nomi riservati di Windows) della patch della
20
+ * review: gli id di questi archivi li genera SEMPRE `randomUUID()` (solo esadecimali e trattini), il primo carattere
21
+ * dev'essere alfanumerico — quindi `..`, `a/b`, `a\b`, un id vuoto e ogni separatore o `..` sono già respinti, cioè
22
+ * TUTTO ciò che fa uscire dalla cartella. Restare sulla stessa grammatica della Libreria è la regola di casa: una
23
+ * difesa sola, non due che divergono.
24
+ */
25
+ export function idArchivioValido(id) {
26
+ return typeof id === 'string' && /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(id);
27
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * image-generator.mjs — FASE H del piano `elegant-spinning-dongarra.md`
3
+ * (29/8): chiama la Image API di OpenRouter (lanciata fine giugno
4
+ * 2026), la STESSA chiave già in `config.mjs` — zero provider nuovo,
5
+ * il one-up dichiarato: niente attrezzo immagine verso un provider
6
+ * separato dal modello di chat.
7
+ *
8
+ * Codici d'errore `TALOS_IMAGE_*` — stessa famiglia di nomi già in
9
+ * uso nell'omologo mobile (`mobile/src/lib/images/imageTools.ts`:
10
+ * `TALOS_IMAGE_NO_PROVIDER`/`TALOS_IMAGE_SOURCE_NOT_FOUND`/...), non
11
+ * inventata qui — un modello che ha già visto quei codici (in
12
+ * un'altra conversazione, o nella sua formazione) li riconosce.
13
+ *
14
+ * Due percorsi CONFERMATI (29/8, tre fonti incluso il riferimento API
15
+ * ufficiale, non presunti — vedi il piano madre, FASE H):
16
+ * - **DEDICATO** (`nativo:false`): `POST /api/v1/images`, corpo
17
+ * `{model, prompt, aspect_ratio}`, risposta
18
+ * `data[0].{b64_json, media_type}`. Il catalogo di questi modelli è
19
+ * `GET /api/v1/images/models` — un endpoint SEPARATO da
20
+ * `/api/v1/models` (verificato dal vivo il 29/8: un modello dedicato
21
+ * reale, `bytedance-seed/seedream-4.5`, NON compare affatto in
22
+ * `/api/v1/models` — solo i modelli NATIVI ci compaiono, con
23
+ * `architecture.output_modalities` che include `"image"`).
24
+ * - **NATIVO** (`nativo:true`): `POST /api/v1/chat/completions`
25
+ * STANDARD (lo stesso endpoint che il kernel già chiama per il
26
+ * testo) con `modalities:["text","image"]` nel corpo — l'immagine
27
+ * arriva in `choices[0].message.images[0].image_url.url`, una data
28
+ * URL (`data:<mediaType>;base64,<dati>`), MAI in `data[].b64_json`
29
+ * (quello è solo del percorso dedicato).
30
+ *
31
+ * ⛔ Quale dei due usare NON si indovina qui — è una proprietà del
32
+ * MODELLO scelto (`config.mjs`, `immagine.nativo`), decisa a monte:
33
+ * questo modulo non chiama nessun catalogo per scoprirlo (un giro di
34
+ * rete in più per ogni immagine, per un fatto che il chiamante può
35
+ * già sapere). I due cataloghi VERI esistono e sono stati verificati
36
+ * dal vivo il 29/8 — restano per un futuro picker owner-facing
37
+ * (mirror del picker `modello` già in uso), non per questa decisione
38
+ * a runtime.
39
+ *
40
+ * ⛔ Aspect ratio sul percorso NATIVO: non inviato in questa fetta —
41
+ * un campo che alcune fonti secondarie citano (`image_config.aspect_ratio`)
42
+ * non è confermato nel riferimento API ufficiale consultato il 29/8;
43
+ * mai un campo non verificato nella chiamata vera. Il prompt può
44
+ * comunque descrivere l'inquadratura in linguaggio naturale.
45
+ */
46
+ import { talosSafeFileStem } from './document-filename.mjs';
47
+
48
+ /*
49
+ * ⛔⛔⛔ DICHIARATO E NON CURATO — CLI-REQ-05, punto 3 del terzo giro (17/09/2026).
50
+ *
51
+ * Questi due indirizzi sono FISSI e la chiamata parte da una `fetch` nuda: in una sessione il cui
52
+ * fornitore NON è OpenRouter — una DeepSeek, una locale — il prompt dell'immagine va comunque a
53
+ * openrouter.ai. È la stessa classe del difetto curato per la compattazione e per il giudice
54
+ * (`session-registry.mjs`, `compatta()` e `creaChiediAlModelloGiudice`), che adesso passano dalla
55
+ * destinazione multi-fornitore dell'host col modello DELLA SESSIONE.
56
+ *
57
+ * ⇒ NON si cura qui, ed è una scelta dichiarata, non una dimenticanza: la generazione di immagini
58
+ * è nata su OpenRouter come unico fornitore («zero provider nuovo»), e spostarla vuol dire
59
+ * decidere quale fornitore genera le immagini per una sessione che non è di OpenRouter — una
60
+ * decisione dell'owner, non di questa riga.
61
+ * ⛔ Finché resta così: chi sceglie una sessione locale perché niente esca, e poi chiede
62
+ * un'immagine, manda comunque il suo prompt a openrouter.ai. Sta scritto qui perché la prossima
63
+ * persona che legge questo file lo sappia senza doverlo scoprire.
64
+ */
65
+ const OPENROUTER_IMAGES_URL = 'https://openrouter.ai/api/v1/images';
66
+ const OPENROUTER_CHAT_URL = 'https://openrouter.ai/api/v1/chat/completions';
67
+
68
+ /** ⭐ Le tre forme che il tool del kernel offre — stesso vocabolario di `mobile/src/lib/images/imageTools.ts` (`SHAPES`), non un terzo nuovo. */
69
+ export const ASPECT_RATIO_PER_FORMA = Object.freeze({ square: '1:1', portrait: '9:16', landscape: '16:9' });
70
+
71
+ function decodeDataUrl(dataUrl) {
72
+ const match = /^data:([^;]+);base64,(.+)$/s.exec(dataUrl ?? '');
73
+ if (!match) throw new Error('TALOS_IMAGE_BAD_RESPONSE: OpenRouter has returned an image in an unexpected format (not a base64 data URL).');
74
+ return { mediaType: match[1], bytes: Buffer.from(match[2], 'base64') };
75
+ }
76
+
77
+ async function leggiCorpoJson(risposta, dove) {
78
+ try {
79
+ return await risposta.json();
80
+ } catch {
81
+ throw new Error(`TALOS_IMAGE_BAD_RESPONSE: ${dove} returned a response that is not valid JSON.`);
82
+ }
83
+ }
84
+
85
+ async function chiamaImmaginiDedicato({ prompt, shape, modello, chiave }, fetchFn) {
86
+ let risposta;
87
+ try {
88
+ risposta = await fetchFn(OPENROUTER_IMAGES_URL, {
89
+ method: 'POST',
90
+ headers: { Authorization: `Bearer ${chiave}`, 'Content-Type': 'application/json' },
91
+ body: JSON.stringify({ model: modello, prompt, aspect_ratio: ASPECT_RATIO_PER_FORMA[shape] ?? '1:1' }),
92
+ });
93
+ } catch (errore) {
94
+ throw new Error(`TALOS_IMAGE_UNREACHABLE: the OpenRouter Image API could not be reached (${errore instanceof Error ? errore.message : String(errore)}).`);
95
+ }
96
+ const corpo = await leggiCorpoJson(risposta, 'the OpenRouter Image API');
97
+ if (!risposta.ok) {
98
+ throw new Error(`TALOS_IMAGE_UPSTREAM_ERROR: the OpenRouter Image API responded ${risposta.status} (${corpo?.error?.message ?? 'no detail'}).`);
99
+ }
100
+ const voce = corpo?.data?.[0];
101
+ if (!voce?.b64_json) throw new Error('TALOS_IMAGE_BAD_RESPONSE: the OpenRouter Image API response has no image data.');
102
+ return { mediaType: typeof voce.media_type === 'string' && voce.media_type ? voce.media_type : 'image/png', bytes: Buffer.from(voce.b64_json, 'base64') };
103
+ }
104
+
105
+ async function chiamaImmaginiNativo({ prompt, modello, chiave }, fetchFn) {
106
+ let risposta;
107
+ try {
108
+ risposta = await fetchFn(OPENROUTER_CHAT_URL, {
109
+ method: 'POST',
110
+ headers: { Authorization: `Bearer ${chiave}`, 'Content-Type': 'application/json' },
111
+ body: JSON.stringify({ model: modello, messages: [{ role: 'user', content: prompt }], modalities: ['text', 'image'] }),
112
+ });
113
+ } catch (errore) {
114
+ throw new Error(`TALOS_IMAGE_UNREACHABLE: OpenRouter could not be reached for native image generation (${errore instanceof Error ? errore.message : String(errore)}).`);
115
+ }
116
+ const corpo = await leggiCorpoJson(risposta, 'OpenRouter (native image generation)');
117
+ if (!risposta.ok) {
118
+ throw new Error(`TALOS_IMAGE_UPSTREAM_ERROR: OpenRouter responded ${risposta.status} for native image generation (${corpo?.error?.message ?? 'no detail'}).`);
119
+ }
120
+ const dataUrl = corpo?.choices?.[0]?.message?.images?.[0]?.image_url?.url;
121
+ if (!dataUrl) {
122
+ throw new Error('TALOS_IMAGE_NO_IMAGE: the model did not return an image — it may have answered in text only. Try a more explicit drawing prompt, or a dedicated image model.');
123
+ }
124
+ return decodeDataUrl(dataUrl);
125
+ }
126
+
127
+ /**
128
+ * `spec.shape` è ignorato sul percorso nativo (vedi la doc sopra sul
129
+ * campo non confermato) — non un bug, un limite dichiarato.
130
+ * @param {{prompt:string, shape?:'square'|'portrait'|'landscape', modello:string, nativo:boolean, chiave:string}} spec
131
+ * @param {{fetchFn?: typeof fetch}} [deps]
132
+ * @returns {Promise<{mediaType:string, bytes:Buffer, fileStem:string}>}
133
+ */
134
+ export async function generaImmagineOpenRouter(spec, deps = {}) {
135
+ const fetchFn = deps.fetchFn ?? fetch;
136
+ const { prompt, shape, modello, nativo, chiave } = spec;
137
+ const { mediaType, bytes } = nativo
138
+ ? await chiamaImmaginiNativo({ prompt, modello, chiave }, fetchFn)
139
+ : await chiamaImmaginiDedicato({ prompt, shape, modello, chiave }, fetchFn);
140
+ // ⭐ un nome file leggibile dal prompt, mai "immagine-<uuid>" — stesso principio di talosSafeFileStem già in uso per document-generator.mjs.
141
+ const fileStem = talosSafeFileStem(prompt, 80, 'immagine');
142
+ return { mediaType, bytes, fileStem };
143
+ }
@@ -0,0 +1,234 @@
1
+ /**
2
+ * BC-48 A (12/09/2026): indice della radice e dei file oltre 60 righe; sezioni sempre
3
+ * intere, commenti HTML esterni al codice rimossi. Il compositore omette file interi
4
+ * con avviso. Il resoconto storico sotto e `tagliaIstruzioni` descrivono il contratto
5
+ * precedente; la funzione esportata resta per compatibilità, fuori dal preambolo.
6
+ *
7
+ * istruzioni-di-progetto.mjs — BLOCCO 3 del preambolo: `AGENTS.md` / `CLAUDE.md`, con un tetto
8
+ * di byte DICHIARATO e un troncamento che dice dove riprendere.
9
+ *
10
+ * ── IL DIFETTO CHE CHIUDE ────────────────────────────────────────────────────────────────────
11
+ *
12
+ * Misurato l'11/09/2026 (`.claude/RAPPORTO-PREAMBOLO-CONCORRENTI-2026-09-11.md` §6.3): in
13
+ * `harness-ui/src/` i nomi `AGENTS.md` e `CLAUDE.md` comparivano **solo** in `path-policy.mjs:35`
14
+ * (permessi) e dentro i commenti. ⇒ **Al modello arrivavano 17.000 token di inventario dei file e
15
+ * ZERO byte di istruzioni del progetto** — l'esatto contrario di tutti e quattro i concorrenti
16
+ * letti nel codice lo stesso giorno:
17
+ * · Codex .......... `agents_md.rs`, catena dalla radice del progetto al cwd, tetto
18
+ * `AGENTS_MD_MAX_BYTES = 32 KiB`, e il commento dice *«Larger files are
19
+ * silently truncated»*;
20
+ * · Claude Code .... budget `max(40.000, 5% della finestra)`, taglio per file a 200 righe /
21
+ * 25.000 byte con una sezione `WARNING` che lo dichiara;
22
+ * · Hermes v0.21 ... `CONTEXT_FILE_MAX_CHARS = 20_000`, head 70% / tail 20%, e il marcatore dice
23
+ * **dove riprendere** (`read_file` sul percorso vero);
24
+ * · DeepSeek `dsh` . budget in byte **obbligatorio** in configurazione (65.536 di serie), e la
25
+ * politica: *«it drops whole broader files before truncating the most-specific
26
+ * file, and emits a visible `Workspace instruction budget …` notice naming the
27
+ * omitted and truncated paths»*.
28
+ *
29
+ * RICERCA WEB, letta l'11/09/2026 PRIMA di scrivere:
30
+ * 1. Specifica aperta AGENTS.md <https://agents.md/> — verbatim: *«Place another AGENTS.md inside
31
+ * each package. Agents automatically read the nearest file in the directory tree, so the
32
+ * closest one takes precedence.»* e *«used by over 60k open-source projects»*. ⛔ La specifica
33
+ * **non dichiara nessun tetto di byte**: il tetto è una scelta di ogni harness, e ogni harness
34
+ * che abbiamo letto ne ha uno. Chi non ce l'ha, prima o poi manda in contesto un file da 200 KB.
35
+ * 2. Claude Platform Docs, «Prompt caching» (letto 11/09/2026,
36
+ * <https://platform.claude.com/docs/en/docs/build-with-claude/prompt-caching>) — *«Cache hits
37
+ * require 100% identical prompt segments»*: il testo prodotto qui sta nel PREFISSO, quindi
38
+ * dev'essere una funzione pura del contenuto dei file, senza orari, senza percorsi assoluti,
39
+ * senza niente che cambi da una chiamata all'altra.
40
+ *
41
+ * ── LE QUATTRO DECISIONI ─────────────────────────────────────────────────────────────────────
42
+ *
43
+ * (1) LA CATENA, dalla radice del progetto al cwd, il più SPECIFICO per ULTIMO. È la forma di
44
+ * Codex (`agents_md.rs`: *«Collect every AGENTS.md found from the project root down to the
45
+ * current working directory (inclusive) and concatenate their contents in that order»*) e la
46
+ * precedenza che la specifica dichiara. L'ultimo letto vince perché è l'ultimo letto: non
47
+ * serve nessuna regola in più, e il testo lo dice al modello a parole.
48
+ * ⛔ Non si risale MAI oltre la radice del progetto: un `AGENTS.md` nella home dell'utente
49
+ * riguarda un altro lavoro, e trascinarlo dentro sarebbe rifare il difetto del `.gitignore`
50
+ * che ereditava regole da fuori (curato in `gitignore-elenco.mjs`).
51
+ *
52
+ * (2) UN SOLO FILE PER CARTELLA, nell'ordine `AGENTS.md` poi `CLAUDE.md` — i candidati di serie di
53
+ * `dsh` (`packages/context/agent-instructions/src/config.ts:11-15`). Prenderli entrambi
54
+ * significherebbe pagare due volte lo stesso contenuto in un repo che li tiene allineati, ed è
55
+ * esattamente il caso di questo repo.
56
+ *
57
+ * (3) IL TETTO SI DICHIARA, e il troncamento è ONESTO. Due livelli, come `dsh`:
58
+ * · se la catena sfora, si tolgono INTERI i file più GENERICI (i più lontani dal cwd) prima
59
+ * di tagliare il più specifico — quello vicino al lavoro è quello che serve;
60
+ * · il file che resta e sfora si taglia head 70% / tail 20% (Hermes), e il marcatore dice
61
+ * quanti caratteri mancano e **con quale attrezzo** leggere il file intero.
62
+ * ⛔ Un taglio silenzioso è il difetto che questo progetto ha già pagato sulla propria
63
+ * memoria: `MEMORY.md` perde righe oltre 200/25.000 senza un avviso in sessione, e nessuno
64
+ * se n'era accorto per settimane. Qui il taglio è una RIGA DI TESTO, non un effetto.
65
+ *
66
+ * (4) SE NON C'È NIENTE, IL BLOCCO NON ESISTE. Nessuna intestazione vuota, nessun «(nessuna
67
+ * istruzione trovata)»: zero byte. È il principio che Hermes scrive in chiaro
68
+ * (`system_prompt.py:713-716`): *«Emits a single line; emits NOTHING when the environment is
69
+ * clean (no token cost)»*.
70
+ */
71
+ import { readFile, stat } from 'node:fs/promises';
72
+ import { dirname, join, parse, relative, sep } from 'node:path';
73
+ import { rendiFileIstruzioni } from './sezioni-istruzioni.mjs';
74
+
75
+ /**
76
+ * Il tetto, in BYTE, dell'intero blocco 3. 24.000 sta fra Hermes (20.000 caratteri) e Codex
77
+ * (32 KiB), e sotto `dsh` (65.536). Perché non di più: a 3,5 byte/token (la taratura misurata il
78
+ * 09/09 su `z-ai/glm-5.3-flash`, `costo-elenco.mjs`) sono ~6.900 token, cioè già il 40% di tutto
79
+ * ciò che l'elenco dei file costava su `AVM/mobile` prima di questa cura (17.149 token misurati).
80
+ * Un tetto più alto renderebbe il preambolo nuovo non più leggero di quello vecchio nel caso
81
+ * peggiore, e il caso peggiore è l'unico che conta.
82
+ * ⛔ È un ARGOMENTO, non una legge: chi ha un progetto con istruzioni enormi lo alza sapendo cosa
83
+ * paga. Ciò che non è negoziabile è che sia DICHIARATO nel testo quando morde.
84
+ */
85
+ export const TETTO_BYTE_PREDEFINITO = 24_000;
86
+
87
+ /** I candidati, nell'ordine in cui si guardano dentro una cartella. Vedi decisione (2). */
88
+ export const NOMI_CANDIDATI = Object.freeze(['AGENTS.md', 'CLAUDE.md']);
89
+
90
+ /** Quanti livelli si risale al massimo cercando la radice del progetto. */
91
+ export const RISALITA_MASSIMA = 40;
92
+
93
+ /** Quote del taglio per file: testa 70%, coda 20%, il 10% di mezzo è il marcatore e il margine. */
94
+ export const QUOTA_TESTA = 0.7;
95
+ export const QUOTA_CODA = 0.2;
96
+
97
+ const normalizza = (p) => String(p).replace(/\\/g, '/');
98
+
99
+ /**
100
+ * La radice del progetto: la prima cartella, risalendo, che ha un `.git`.
101
+ * ⛔ `.git` si cerca con `stat` e non si distingue DIRECTORY da FILE: in un worktree è un file, ed
102
+ * è il caso di oggi su questo repo. È la stessa scelta di ripgrep (`Ignore::add_parents`, che
103
+ * usa `exists()`) già documentata in `gitignore-elenco.mjs`.
104
+ */
105
+ export async function trovaRadiceProgetto(cartella, { fs, risalitaMassima = RISALITA_MASSIMA } = {}) {
106
+ const disco = { stat, ...(fs ?? {}) };
107
+ let corrente = String(cartella ?? '');
108
+ for (let i = 0; i < risalitaMassima && corrente; i += 1) {
109
+ try {
110
+ await disco.stat(join(corrente, '.git'));
111
+ return corrente;
112
+ } catch { /* non è la radice: si sale */ }
113
+ const sopra = dirname(corrente);
114
+ if (!sopra || sopra === corrente || sopra === parse(corrente).root) break;
115
+ corrente = sopra;
116
+ }
117
+ return null;
118
+ }
119
+
120
+ /**
121
+ * Trova i file di istruzioni della catena, dalla radice del progetto al cwd.
122
+ * @returns {Promise<{percorso:string, etichetta:string, lettura:string, contenuto:string, byte:number}[]>}
123
+ * in ordine dal più GENERICO al più SPECIFICO (l'ultimo vince).
124
+ */
125
+ export async function trovaIstruzioniDiProgetto(cartella, { fs, nomi = NOMI_CANDIDATI, risalitaMassima = RISALITA_MASSIMA } = {}) {
126
+ const disco = { readFile, stat, ...(fs ?? {}) };
127
+ const cwd = String(cartella ?? '');
128
+ if (!cwd) return [];
129
+ const radice = await trovaRadiceProgetto(cwd, { fs, risalitaMassima }) ?? cwd;
130
+
131
+ /* Le cartelle da guardare: dalla radice del progetto giù fino al cwd, incluse entrambe. */
132
+ const tratto = relative(radice, cwd);
133
+ const pezzi = tratto && tratto !== '.' ? tratto.split(sep).filter(Boolean) : [];
134
+ const cartelle = [radice];
135
+ let corrente = radice;
136
+ for (const pezzo of pezzi) {
137
+ corrente = join(corrente, pezzo);
138
+ cartelle.push(corrente);
139
+ }
140
+
141
+ const trovati = [];
142
+ for (const dove of cartelle) {
143
+ for (const nome of nomi) {
144
+ let contenuto;
145
+ try {
146
+ contenuto = await disco.readFile(join(dove, nome), 'utf8');
147
+ } catch { continue; } // non c'è, o non si legge: si passa al candidato dopo
148
+ if (typeof contenuto !== 'string' || contenuto.trim() === '') continue;
149
+ /*
150
+ * ⛔ L'ETICHETTA È RELATIVA ALLA RADICE DEL PROGETTO, MAI ASSOLUTA. Un percorso assoluto
151
+ * contiene il nome della persona e questo testo esce dalla macchina dentro un prompt —
152
+ * lezione [[cancello-4-non-guardava-tutto-mobile]]. E in più: un percorso assoluto
153
+ * cambia da macchina a macchina, quindi romperebbe il prefisso della cache fra due
154
+ * installazioni dello stesso progetto.
155
+ */
156
+ const etichetta = normalizza(relative(radice, join(dove, nome))) || nome;
157
+ trovati.push({ percorso: join(dove, nome), etichetta, lettura: normalizza(relative(cwd, join(dove, nome))), contenuto, byte: Buffer.byteLength(contenuto, 'utf8') });
158
+ break; // decisione (2): un solo file per cartella
159
+ }
160
+ }
161
+ return trovati;
162
+ }
163
+
164
+ /**
165
+ * Taglia un contenuto al tetto dato, testa 70% / coda 20%, e DICE dove riprendere.
166
+ * ⛔ Si taglia sui BYTE (non sui caratteri) perché il tetto è in byte e un carattere accentato ne
167
+ * vale 2: contare caratteri farebbe sforare il tetto proprio sui testi italiani, cioè i nostri.
168
+ * E si ritaglia all'a-capo più vicino, per non spezzare una riga a metà parola.
169
+ */
170
+ export function tagliaIstruzioni(contenuto, tetto, etichetta) {
171
+ const byte = Buffer.byteLength(contenuto, 'utf8');
172
+ if (byte <= tetto) return { testo: contenuto, tagliato: false, byteTenuti: byte, byteTotali: byte };
173
+
174
+ const buf = Buffer.from(contenuto, 'utf8');
175
+ const byteTesta = Math.max(0, Math.floor(tetto * QUOTA_TESTA));
176
+ const byteCoda = Math.max(0, Math.floor(tetto * QUOTA_CODA));
177
+ /* `toString` su un taglio a metà di una sequenza UTF-8 produce U+FFFD: si accetta e si ripulisce
178
+ all'a-capo, invece di fingere che il taglio sui byte sia sempre su un confine di carattere. */
179
+ let testa = buf.subarray(0, byteTesta).toString('utf8');
180
+ const aCapoTesta = testa.lastIndexOf('\n');
181
+ if (aCapoTesta > byteTesta * 0.5) testa = testa.slice(0, aCapoTesta);
182
+ let coda = buf.subarray(buf.length - byteCoda).toString('utf8');
183
+ const aCapoCoda = coda.indexOf('\n');
184
+ if (aCapoCoda >= 0 && aCapoCoda < byteCoda * 0.5) coda = coda.slice(aCapoCoda + 1);
185
+
186
+ const mancanti = byte - Buffer.byteLength(testa, 'utf8') - Buffer.byteLength(coda, 'utf8');
187
+ const marcatore = `\n\n[...«${etichetta}» è stato TAGLIATO: di ${byte} byte ne vedi i primi ~${Buffer.byteLength(testa, 'utf8')} e gli ultimi ~${Buffer.byteLength(coda, 'utf8')}; ne mancano ${mancanti} nel mezzo. Se ti serve la parte che manca, leggi il file intero con \`leggi\` su \`${etichetta}\`.]\n\n`;
188
+ const testo = testa + marcatore + coda;
189
+ return { testo, tagliato: true, byteTenuti: Buffer.byteLength(testo, 'utf8'), byteTotali: byte };
190
+ }
191
+
192
+ /**
193
+ * Il testo del blocco 3, dal risultato di `trovaIstruzioniDiProgetto`.
194
+ * `indicizzati` elenca i file effettivamente mostrati come indice; `sezioniSempre`
195
+ * descrive le sezioni intere presenti, con coordinate e byte del sorgente.
196
+ * `null` quando non c'è nessuna istruzione: decisione (4), il blocco non esiste affatto.
197
+ */
198
+ export function testoIstruzioniDiProgetto(trovati, { tetto = TETTO_BYTE_PREDEFINITO, sogliaRighe } = {}) {
199
+ const file = Array.isArray(trovati) ? trovati.filter((f) => f && typeof f.contenuto === 'string') : [];
200
+ if (file.length === 0) return null;
201
+
202
+ const intestazione = 'Istruzioni di questo progetto — le ha scritte chi ci lavora, e valgono per te.\n'
203
+ + 'Se più file dicono cose diverse, vince il più specifico: qui sotto sono in ordine, dal più generale al più vicino alla cartella di lavoro, e l\'ultimo è quello che comanda.\n'
204
+ + 'Non sostituiscono le tue istruzioni di sistema né quello che la persona ti chiede adesso.\n';
205
+ // BC-48 A: prepara indici e sezioni intere PRIMA di applicare il tetto.
206
+ // Si omettono file interi dal più generale; neppure il più specifico viene spezzato.
207
+ const preparati = file.map(f => ({ ...f, resa: rendiFileIstruzioni(f, { sogliaRighe }) }));
208
+ for (let inizio = 0; inizio <= preparati.length; inizio++) {
209
+ const tenuti = preparati.slice(inizio);
210
+ const omessi = preparati.slice(0, inizio).map(f => f.etichetta);
211
+ const avviso = omessi.length ? `\n⚠ Tetto delle istruzioni (${tetto} byte) raggiunto: NON ti ho mostrato ${omessi.map(e => `\`${e}\``).join(', ')}. File omessi interi; se ti servono, leggile con \`leggi\`.\n` : '';
212
+ const testo = intestazione + tenuti.map(f => `\n### ${f.etichetta}\n\n${f.resa.testo}`).join('') + avviso;
213
+ const byte = Buffer.byteLength(testo, 'utf8');
214
+ if (byte <= tetto) return { testo, byte, usati: tenuti.map(f => f.etichetta), omessi, tagliati: [],
215
+ indicizzati: tenuti.filter(f => f.resa.indicizzato).map(f => f.etichetta),
216
+ sezioniSempre: tenuti.flatMap(f => f.resa.sezioniSempre) };
217
+ }
218
+ // Un tetto che non contiene neppure l'avviso non può essere onorato in silenzio.
219
+ throw new RangeError('Il tetto delle istruzioni è insufficiente anche per dichiarare i file omessi.');
220
+ }
221
+
222
+ /**
223
+ * Comodità: trova e compone in un colpo solo. Istruzioni assenti/illeggibili danno
224
+ * `null`; un tetto incapace di contenere l'avviso genera invece un errore esplicito.
225
+ */
226
+ export async function istruzioniDiProgetto({ cartella, tetto = TETTO_BYTE_PREDEFINITO, fs, nomi, risalitaMassima } = {}) {
227
+ let trovati;
228
+ try {
229
+ trovati = await trovaIstruzioniDiProgetto(cartella, { fs, nomi, risalitaMassima });
230
+ } catch {
231
+ return null;
232
+ }
233
+ return testoIstruzioniDiProgetto(trovati, { tetto });
234
+ }