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,221 @@
1
+ /** Canonical, provider-neutral contract for user-authored forged tools. */
2
+ const MAX_MANIFEST_BYTES = 65_536;
3
+ const MAX_NODES = 64;
4
+ const MAX_TRANSITIONS = 256;
5
+ const SAFE_SEGMENT = /^[A-Za-z0-9_-]+$/;
6
+ const FORBIDDEN_SEGMENTS = new Set(['__proto__', 'constructor', 'prototype', '__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__']);
7
+ const READ_ONLY_ROOTS = new Set(['input', 'runtime']);
8
+ const NODE_TYPES = ['capability', 'if', 'return', 'fail'];
9
+ const CAPABILITIES = Object.freeze({
10
+ 'tasks.list': { actions: ['read'], risk: 'R1' },
11
+ 'tasks.create': { actions: ['write'], risk: 'R2' },
12
+ 'tasks.setStatus': { actions: ['write'], risk: 'R2' },
13
+ 'notes.list': { actions: ['read'], risk: 'R1' },
14
+ 'notes.create': { actions: ['write'], risk: 'R2' },
15
+ 'notes.update': { actions: ['write'], risk: 'R2' },
16
+ 'memory.search': { actions: ['read'], risk: 'R2' },
17
+ 'memory.create': { actions: ['write'], risk: 'R3' },
18
+ });
19
+ const RISK_ORDER = { R1: 1, R2: 2, R3: 3 };
20
+ const ID_PATTERN = /^[a-z0-9][a-z0-9_-]{2,63}$/;
21
+
22
+ function rawSegments(path) {
23
+ const normalized = path.startsWith('$.') ? path.slice(2) : path === '$' ? '' : path;
24
+ return normalized.split('.').filter(Boolean);
25
+ }
26
+
27
+ export function forgePathViolation(path, { writable = false } = {}) {
28
+ if (typeof path !== 'string') return 'must be a string';
29
+ const parts = rawSegments(path);
30
+ if (parts.length > 16) return 'path exceeds 16 segments';
31
+ for (const part of parts) {
32
+ if (part.length > 64) return `segment "${part}" exceeds 64 characters`;
33
+ if (!SAFE_SEGMENT.test(part)) return `segment "${part}" must be alphanumeric, "_" or "-" only`;
34
+ if (FORBIDDEN_SEGMENTS.has(part)) return `segment "${part}" is forbidden`;
35
+ }
36
+ if (writable && parts[0] && READ_ONLY_ROOTS.has(parts[0])) return `"${parts[0]}" is read-only; write to "state" instead`;
37
+ return null;
38
+ }
39
+
40
+ function isReference(value) {
41
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 1 && typeof value.$ref === 'string';
42
+ }
43
+
44
+ function collectReferences(value, result = []) {
45
+ if (isReference(value)) { result.push(value.$ref); return result; }
46
+ if (Array.isArray(value)) { for (const item of value) collectReferences(item, result); return result; }
47
+ if (value && typeof value === 'object') { for (const item of Object.values(value)) collectReferences(item, result); }
48
+ return result;
49
+ }
50
+
51
+ function addReferenceDiagnostics(value, path, add) {
52
+ for (const reference of collectReferences(value)) {
53
+ const violation = forgePathViolation(reference);
54
+ if (violation) add(path, `unsafe $ref "${reference}": ${violation}`);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Validates the versioned forge manifest without any filesystem or process
60
+ * access. It returns diagnostics instead of throwing so the caller can show
61
+ * a useful explanation and avoid writing an unsafe manifest.
62
+ */
63
+ export function validaManifestForgeLocale(manifest) {
64
+ const diagnostics = [];
65
+ const add = (path, message) => diagnostics.push(`${path}: ${message}`);
66
+ if (!manifest || typeof manifest !== 'object') return { ok: false, diagnostica: ['manifest: must be an object'] };
67
+ let bytes;
68
+ try { bytes = new TextEncoder().encode(JSON.stringify(manifest)).length; } catch { bytes = Number.POSITIVE_INFINITY; }
69
+ if (bytes > MAX_MANIFEST_BYTES) add('manifest', `exceeds ${MAX_MANIFEST_BYTES} bytes`);
70
+ if (typeof manifest.id !== 'string' || !ID_PATTERN.test(manifest.id)) add('id', 'must be a lowercase slug, 3-64 chars, a-z0-9_- only');
71
+ if (typeof manifest.title !== 'string' || manifest.title.length < 1 || manifest.title.length > 80) add('title', 'must be 1-80 characters');
72
+ if (typeof manifest.description !== 'string' || manifest.description.length < 1 || manifest.description.length > 400) add('description', 'must be 1-400 characters');
73
+ const flow = manifest.flow;
74
+ if (!flow || typeof flow !== 'object') { add('flow', 'is required'); return { ok: false, diagnostica: diagnostics }; }
75
+ if (typeof flow.entry !== 'string' || flow.entry.length === 0) add('flow.entry', 'is required');
76
+ if (!Array.isArray(flow.nodes) || flow.nodes.length === 0) add('flow.nodes', 'must be a non-empty array');
77
+ if (Array.isArray(flow.nodes) && flow.nodes.length > MAX_NODES) add('flow.nodes', `exceeds ${MAX_NODES} nodes`);
78
+ const maxTransitions = Number(flow.maxTransitions);
79
+ if (!Number.isFinite(maxTransitions) || maxTransitions < 1 || maxTransitions > MAX_TRANSITIONS) add('flow.maxTransitions', `must be 1-${MAX_TRANSITIONS}`);
80
+ if (!Array.isArray(flow.nodes)) return { ok: false, diagnostica: diagnostics };
81
+ const ids = new Set();
82
+ const capabilities = new Set();
83
+ for (const [index, node] of flow.nodes.entries()) {
84
+ const path = `flow.nodes[${index}]`;
85
+ if (!node || typeof node !== 'object' || typeof node.id !== 'string' || node.id.length === 0) { add(path, 'must have a non-empty "id"'); continue; }
86
+ if (ids.has(node.id)) add(path, `duplicate node id "${node.id}"`);
87
+ ids.add(node.id);
88
+ if (!NODE_TYPES.includes(node.type)) { add(`${path}.type`, `must be one of ${NODE_TYPES.join(', ')}`); continue; }
89
+ if (node.type === 'capability') {
90
+ if (typeof node.capability !== 'string' || !CAPABILITIES[node.capability]) add(`${path}.capability`, `must be one of ${Object.keys(CAPABILITIES).join(', ')}`);
91
+ else capabilities.add(node.capability);
92
+ if (typeof node.next !== 'string' || node.next.length === 0) add(`${path}.next`, 'is required');
93
+ addReferenceDiagnostics(node.input, `${path}.input`, add);
94
+ if (node.target !== undefined) {
95
+ const violation = typeof node.target !== 'string' ? 'must be a string path' : forgePathViolation(node.target, { writable: true });
96
+ if (violation) add(`${path}.target`, violation);
97
+ }
98
+ } else if (node.type === 'if') {
99
+ if (!node.condition || typeof node.condition !== 'object') add(`${path}.condition`, 'is required');
100
+ else { addReferenceDiagnostics(node.condition.left, `${path}.condition`, add); addReferenceDiagnostics(node.condition.right, `${path}.condition`, add); }
101
+ if (typeof node.then !== 'string' || node.then.length === 0) add(`${path}.then`, 'is required');
102
+ if (typeof node.else !== 'string' || node.else.length === 0) add(`${path}.else`, 'is required');
103
+ } else if (node.type === 'return') {
104
+ addReferenceDiagnostics(node.value, `${path}.value`, add);
105
+ } else if (node.type === 'fail') {
106
+ if (typeof node.code !== 'string' || node.code.length === 0) add(`${path}.code`, 'is required');
107
+ if (typeof node.message !== 'string' || node.message.length === 0) add(`${path}.message`, 'is required');
108
+ }
109
+ }
110
+ if (diagnostics.length > 0) return { ok: false, diagnostica: diagnostics };
111
+ for (const node of flow.nodes) {
112
+ for (const field of ['next', 'then', 'else']) {
113
+ if (typeof node[field] === 'string' && !ids.has(node[field])) return { ok: false, diagnostica: [`flow: "${node[field]}" (referenced by node "${node.id}".${field}) is not a node id`] };
114
+ }
115
+ }
116
+ if (!ids.has(flow.entry)) return { ok: false, diagnostica: [`flow.entry: "${flow.entry}" is not a node id`] };
117
+ const actions = new Set();
118
+ let risk = 'R1';
119
+ for (const capability of capabilities) {
120
+ const descriptor = CAPABILITIES[capability];
121
+ for (const action of descriptor.actions) actions.add(action);
122
+ if (RISK_ORDER[descriptor.risk] > RISK_ORDER[risk]) risk = descriptor.risk;
123
+ }
124
+ return { ok: true, diagnostica: [], capacita: [...capabilities], azioni: [...actions], rischio: risk };
125
+ }
126
+
127
+ export const FORGE_PREFISSO_NOME_TOOL = 'forge_';
128
+
129
+ export function forgeReadPath(root, path) {
130
+ if (path === '$') return root;
131
+ let current = root;
132
+ for (const segment of rawSegments(path)) {
133
+ if (current === null || typeof current !== 'object') return undefined;
134
+ if (Array.isArray(current) && /^\d+$/.test(segment)) { current = current[Number(segment)]; continue; }
135
+ if (!Object.prototype.hasOwnProperty.call(current, segment)) return undefined;
136
+ current = current[segment];
137
+ }
138
+ return current;
139
+ }
140
+
141
+ export function forgeWritePath(root, path, value) {
142
+ const violation = forgePathViolation(path, { writable: true });
143
+ if (violation) throw new Error(`TALOS_FORGE_PATH_UNSAFE:${violation}`);
144
+ const parts = rawSegments(path);
145
+ if (parts.length === 0) throw new Error('TALOS_FORGE_SET_ROOT_FORBIDDEN');
146
+ let current = root;
147
+ for (const part of parts.slice(0, -1)) {
148
+ const existing = current[part];
149
+ if (!existing || typeof existing !== 'object' || Array.isArray(existing)) current[part] = Object.create(null);
150
+ current = current[part];
151
+ }
152
+ current[parts.at(-1)] = value;
153
+ }
154
+
155
+ export function forgeResolveExpression(expression, vars) {
156
+ if (isReference(expression)) return forgeReadPath(vars, expression.$ref);
157
+ if (Array.isArray(expression)) return expression.map((value) => forgeResolveExpression(value, vars));
158
+ if (expression && typeof expression === 'object') return Object.fromEntries(Object.entries(expression).map(([key, value]) => [key, forgeResolveExpression(value, vars)]));
159
+ return expression;
160
+ }
161
+
162
+ export function forgeEvaluateCondition(condition, vars) {
163
+ const left = forgeResolveExpression(condition.left, vars);
164
+ const right = forgeResolveExpression(condition.right, vars);
165
+ switch (condition.op) {
166
+ case 'eq': return Object.is(left, right);
167
+ case 'neq': return !Object.is(left, right);
168
+ case 'truthy': return Boolean(left);
169
+ case 'exists': return left !== undefined && left !== null;
170
+ case 'contains': return typeof left === 'string' ? left.includes(String(right ?? '')) : Array.isArray(left) ? left.some((value) => Object.is(value, right)) : false;
171
+ case 'gt': return typeof left === 'number' && typeof right === 'number' && left > right;
172
+ case 'gte': return typeof left === 'number' && typeof right === 'number' && left >= right;
173
+ case 'lt': return typeof left === 'number' && typeof right === 'number' && left < right;
174
+ case 'lte': return typeof left === 'number' && typeof right === 'number' && left <= right;
175
+ default: return false;
176
+ }
177
+ }
178
+
179
+ export async function eseguiFlowForgeLocale(manifest, input, { capacitaFn } = {}) {
180
+ if (typeof capacitaFn !== 'function') throw new Error('TALOS_FORGE_CAPABILITY_UNAVAILABLE');
181
+ const vars = Object.create(null);
182
+ vars.input = input;
183
+ vars.state = Object.create(null);
184
+ const trace = [];
185
+ const nodes = new Map(manifest.flow.nodes.map((node) => [node.id, node]));
186
+ let currentId = manifest.flow.entry;
187
+ let transitions = 0;
188
+ const limit = Number.isFinite(manifest.flow.maxTransitions) ? Math.min(manifest.flow.maxTransitions, MAX_TRANSITIONS) : MAX_TRANSITIONS;
189
+ const pathFailure = (error) => ({ status: 'failed', error: { code: 'TALOS_FORGE_PATH_UNSAFE', message: error instanceof Error ? error.message : String(error) }, trace });
190
+ while (true) {
191
+ transitions += 1;
192
+ if (transitions > limit) return { status: 'failed', error: { code: 'TALOS_FORGE_MAX_TRANSITIONS', message: 'The tool ran too many steps without finishing.' }, trace };
193
+ const node = nodes.get(currentId);
194
+ if (!node) return { status: 'failed', error: { code: 'TALOS_FORGE_NODE_MISSING', message: `Node "${currentId}" does not exist.` }, trace };
195
+ if (node.type === 'capability') {
196
+ let resolved;
197
+ try { resolved = forgeResolveExpression(node.input, vars); } catch (error) { return pathFailure(error); }
198
+ let result;
199
+ try { result = await capacitaFn(node.capability, resolved); } catch (error) { return { status: 'failed', error: { code: 'TALOS_FORGE_CAPABILITY_FAILED', message: `Capability "${node.capability}" failed: ${error instanceof Error ? error.message : String(error)}` }, trace }; }
200
+ trace.push({ node: node.id, type: 'capability', capability: node.capability });
201
+ if (node.target) { try { forgeWritePath(vars, node.target, result); } catch (error) { return pathFailure(error); } }
202
+ currentId = node.next;
203
+ } else if (node.type === 'if') {
204
+ let outcome;
205
+ try { outcome = forgeEvaluateCondition(node.condition, vars); } catch (error) { return pathFailure(error); }
206
+ trace.push({ node: node.id, type: 'if', result: outcome });
207
+ currentId = outcome ? node.then : node.else;
208
+ } else if (node.type === 'return') {
209
+ let output;
210
+ try { output = forgeResolveExpression(node.value, vars); } catch (error) { return pathFailure(error); }
211
+ trace.push({ node: node.id, type: 'return' });
212
+ return { status: 'succeeded', output, trace };
213
+ } else if (node.type === 'fail') {
214
+ trace.push({ node: node.id, type: 'fail' });
215
+ return { status: 'failed', error: { code: node.code, message: node.message }, trace };
216
+ } else {
217
+ return { status: 'failed', error: { code: 'TALOS_FORGE_NODE_TYPE_UNKNOWN', message: `Unknown node type "${node.type}".` }, trace };
218
+ }
219
+ }
220
+ }
221
+
@@ -0,0 +1,134 @@
1
+ /**
2
+ * frequent-dirs.mjs — owner, coda del piano `elegant-spinning-dongarra.md`:
3
+ * "nella lista file quando si crea una sessione, bisogna mettere directory
4
+ * più usate (tipo desktop downloads etc)". Solo LETTURA, solo suggerimenti:
5
+ * questo file non decide se una cartella è AMMESSA — quello resta
6
+ * `custom-task.mjs`/`avviaLibero` (permesso "Full access", vedi la sua doc
7
+ * sul perché niente denylist), qui si limita a proporre percorsi VERI e
8
+ * VERIFICATI, mai una scorciatoia verso il nulla.
9
+ *
10
+ * ⛔⛔⛔ 30/8, CORRETTO — owner dal vivo: "come mai non ho le cartelle più
11
+ * usate?" La prima versione (28/8) calcolava SOLO dalle cartelle utente
12
+ * standard di Windows (`os.homedir()`) — sempre le stesse tre, MAI la
13
+ * cronologia reale, quindi mai davvero "più usate". Ora la fonte PRIMARIA
14
+ * è `sessionRegistry.cartellePiuUsate()` (cronologia reale delle
15
+ * sessioni, aggregata in `session-registry.mjs`); le tre cartelle
16
+ * Windows standard restano SOLO il ripiego onesto per l'avvio a freddo
17
+ * (zero sessioni mai partite) — vedi `cartelleFrequenti()` sotto.
18
+ *
19
+ * ⛔ Deliberatamente SEPARATO da `config.mjs#parseCartelleProgetto`: quella
20
+ * è l'allowlist fissa dell'amministratore, validata fail-closed all'AVVIO
21
+ * del server. Questa è un elenco DINAMICO, calcolato a ogni richiesta —
22
+ * le due liste non si mescolano MAI: le cartelle frequenti sono solo
23
+ * suggerimenti per il campo "Full access" (percorso a piacere), non una
24
+ * seconda allowlist che aggirerebbe il permesso.
25
+ */
26
+ import { homedir } from 'node:os';
27
+ import { join, resolve } from 'node:path';
28
+ import { statSync } from 'node:fs';
29
+
30
+ /**
31
+ * ⭐ Le stesse TRE cartelle che ogni file manager Windows mostra per
32
+ * default nella barra laterale ("Quick access") — non inventate, sono i
33
+ * nomi di cartella standard sotto il profilo utente su ogni installazione
34
+ * Windows moderna. Il ripiego onesto per l'avvio a freddo (nessuna
35
+ * cronologia reale ancora), MAI mescolate con essa — vedi doc di testa.
36
+ */
37
+ const CANDIDATE = Object.freeze([
38
+ { etichetta: 'Desktop', sottocartella: 'Desktop' },
39
+ { etichetta: 'Download', sottocartella: 'Downloads' },
40
+ { etichetta: 'Documenti', sottocartella: 'Documents' },
41
+ ]);
42
+
43
+ /**
44
+ * ⭐⭐⭐ 30/8 — owner dal vivo: "come mai non ho le cartelle più usate?"
45
+ * Le tre cartelle Windows standard sopra NON erano mai state "più usate"
46
+ * per davvero — erano SEMPRE le stesse, indipendentemente da cosa
47
+ * l'owner avesse effettivamente aperto. Ora la fonte VERA
48
+ * (`sessionRegistry.cartellePiuUsate()`, aggregata dalla cronologia reale
49
+ * delle sessioni) ha priorità; le cartelle Windows standard restano
50
+ * SOLO il ripiego onesto per l'avvio a freddo (zero sessioni mai
51
+ * partite) — mai sparire del tutto, mai fingere una cronologia che non
52
+ * c'è.
53
+ *
54
+ * @param {number} massimoRisultati — quante suggerirne al massimo (il campo è solo un aiuto, non un elenco esaustivo).
55
+ * @returns {Array<{percorso:string, conteggio:number, ultimaVolta:string}>}
56
+ */
57
+ function candidatiDaCronologia(sessionRegistry, massimoRisultati) {
58
+ if (!sessionRegistry || typeof sessionRegistry.cartellePiuUsate !== 'function') return [];
59
+ return sessionRegistry.cartellePiuUsate().slice(0, massimoRisultati);
60
+ }
61
+
62
+ function cartellaReale(percorso, statSyncFn) {
63
+ try {
64
+ return statSyncFn(percorso).isDirectory();
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ function cartelleStandard({ homedirFn, statSyncFn }) {
71
+ const home = homedirFn();
72
+ return CANDIDATE.flatMap(({ etichetta, sottocartella }) => {
73
+ const percorso = join(home, sottocartella);
74
+ return cartellaReale(percorso, statSyncFn) ? [{ etichetta, percorso }] : [];
75
+ });
76
+ }
77
+
78
+ function chiavePercorso(percorso) {
79
+ return resolve(percorso).replace(/[\\/]+$/, '').toLocaleLowerCase('en-US');
80
+ }
81
+
82
+ /**
83
+ * @param {{homedirFn?: typeof homedir, statSyncFn?: typeof statSync, sessionRegistry?: {cartellePiuUsate: () => Array<{percorso:string, conteggio:number, ultimaVolta:string}>}}} [deps] — `sessionRegistry` opzionale: assente nei test che provano solo il ripiego Windows, sempre presente dal server reale (vedi http-app.mjs).
84
+ * @returns {Array<{etichetta:string, percorso:string}>} — SOLO le cartelle che esistono
85
+ * davvero sul disco in questo momento, mai una candidata a occhio: una
86
+ * cartella cancellata dall'owner (Download standard, o una copia
87
+ * usa-e-getta del corpus benchmark ormai ripulita) non deve comparire
88
+ * come scorciatoia verso il nulla.
89
+ */
90
+ export function cartelleFrequenti({ homedirFn = homedir, statSyncFn = statSync, sessionRegistry } = {}) {
91
+ const MASSIMO = 6;
92
+ const daCronologia = candidatiDaCronologia(sessionRegistry, MASSIMO);
93
+ const trovate = [];
94
+ for (const { percorso } of daCronologia) {
95
+ if (!cartellaReale(percorso, statSyncFn)) continue;
96
+ // ⭐ etichetta = nome dell'ultima cartella nel percorso ("qa-visiva-harness-2026-08-30" da ...\projects\qa-visiva-harness-2026-08-30), stesso principio di nomeCartella lato client (app.js, split su [\\/]).
97
+ const etichetta = percorso.replace(/[/\\]+$/, '').split(/[/\\]/).pop() || percorso;
98
+ trovate.push({ etichetta, percorso });
99
+ }
100
+ if (trovate.length > 0) return trovate; // ⛔ cronologia reale disponibile: MAI mescolarla con Desktop/Downloads/Documenti sotto — quelle tre non sono "più usate", solo il ripiego a freddo.
101
+ return cartelleStandard({ homedirFn, statSyncFn });
102
+ }
103
+
104
+ /**
105
+ * Scelte rapide della workbench "Nuova sessione". A differenza del contratto
106
+ * storico `cartelleFrequenti()` (che resta identico), qui l'owner ha bisogno
107
+ * sia della cronologia vera sia delle cartelle standard realmente presenti.
108
+ * L'unione è solo presentazione: non crea una allowlist e non modifica i
109
+ * permessi della sessione.
110
+ *
111
+ * @returns {Array<{etichetta:string, percorso:string, tipo:'recent'|'known'}>}
112
+ */
113
+ export function cartelleConsigliate({ homedirFn = homedir, statSyncFn = statSync, sessionRegistry } = {}) {
114
+ const trovate = [];
115
+ const viste = new Set();
116
+ for (const { percorso } of candidatiDaCronologia(sessionRegistry, 6)) {
117
+ if (!cartellaReale(percorso, statSyncFn)) continue;
118
+ const chiave = chiavePercorso(percorso);
119
+ if (viste.has(chiave)) continue;
120
+ viste.add(chiave);
121
+ trovate.push({
122
+ etichetta: percorso.replace(/[/\\]+$/, '').split(/[/\\]/).pop() || percorso,
123
+ percorso,
124
+ tipo: 'recent',
125
+ });
126
+ }
127
+ for (const { etichetta, percorso } of cartelleStandard({ homedirFn, statSyncFn })) {
128
+ const chiave = chiavePercorso(percorso);
129
+ if (viste.has(chiave)) continue;
130
+ viste.add(chiave);
131
+ trovate.push({ etichetta, percorso, tipo: 'known' });
132
+ }
133
+ return trovate;
134
+ }
@@ -0,0 +1,147 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
3
+ import { extname, join, resolve } from 'node:path';
4
+
5
+ /**
6
+ * Durable local store for images produced by an agent run.
7
+ *
8
+ * The workspace copy remains the user-facing Library item. This store is the
9
+ * recovery copy: it is written atomically, carries a small JSON sidecar, and
10
+ * can be reopened by a fresh process after a server restart. No remote URL is
11
+ * retained as the image itself.
12
+ */
13
+ export const GENERATED_IMAGE_MIME_TYPES = Object.freeze({
14
+ 'image/png': 'png',
15
+ 'image/jpeg': 'jpg',
16
+ 'image/webp': 'webp',
17
+ 'image/gif': 'gif',
18
+ 'image/avif': 'avif',
19
+ });
20
+
21
+ const ID = /^[a-f0-9]{64}$/u;
22
+ const SHA256 = /^[a-f0-9]{64}$/u;
23
+
24
+ export class GeneratedImageStoreError extends Error {
25
+ constructor(message, code = 'TALOS_IMAGE_PERSIST_FAILED') {
26
+ super(message);
27
+ this.name = 'GeneratedImageStoreError';
28
+ this.code = code;
29
+ }
30
+ }
31
+
32
+ function asBytes(value) {
33
+ if (Buffer.isBuffer(value)) return value;
34
+ if (value instanceof Uint8Array) return Buffer.from(value);
35
+ throw new GeneratedImageStoreError('image bytes are missing or invalid', 'TALOS_IMAGE_PERSIST_INVALID');
36
+ }
37
+
38
+ function validatePromptHash(value) {
39
+ if (value == null || value === '') return null;
40
+ if (typeof value !== 'string' || !SHA256.test(value)) throw new GeneratedImageStoreError('image prompt hash is invalid', 'TALOS_IMAGE_PERSIST_INVALID');
41
+ return value;
42
+ }
43
+
44
+ function validateSource(value) {
45
+ if (typeof value !== 'string' || value.trim() === '' || value.length > 512 || /[\0\r\n]/u.test(value)) {
46
+ throw new GeneratedImageStoreError('image source is invalid', 'TALOS_IMAGE_PERSIST_INVALID');
47
+ }
48
+ return value.trim();
49
+ }
50
+
51
+ function safeId({ bytes, source, promptHash }) {
52
+ const digest = createHash('sha256');
53
+ digest.update(bytes);
54
+ digest.update('\0');
55
+ digest.update(source);
56
+ digest.update('\0');
57
+ digest.update(promptHash ?? '');
58
+ return digest.digest('hex');
59
+ }
60
+
61
+ function parseMetadata(value, id) {
62
+ if (!value || typeof value !== 'object' || value.id !== id || typeof value.mimeType !== 'string' || !Object.hasOwn(GENERATED_IMAGE_MIME_TYPES, value.mimeType) || typeof value.source !== 'string' || (value.promptHash !== null && !SHA256.test(value.promptHash)) || !Number.isSafeInteger(value.bytes) || value.bytes <= 0 || !SHA256.test(value.sha256) || typeof value.createdAt !== 'string' || !Number.isFinite(Date.parse(value.createdAt))) {
63
+ throw new GeneratedImageStoreError('stored image metadata is invalid', 'TALOS_IMAGE_PERSIST_CORRUPT');
64
+ }
65
+ return structuredClone(value);
66
+ }
67
+
68
+ export function createGeneratedImageStore({ rootDir, fsImpl = {}, now = () => new Date() } = {}) {
69
+ if (typeof rootDir !== 'string' || !rootDir.trim() || !resolve(rootDir)) throw new GeneratedImageStoreError('generated image store root is invalid', 'TALOS_IMAGE_PERSIST_CONFIG_INVALID');
70
+ const fs = { mkdir, readFile, rename, rm, stat, writeFile, ...fsImpl };
71
+ const imageDir = resolve(rootDir);
72
+
73
+ const paths = (id, mimeType) => {
74
+ if (typeof id !== 'string' || !ID.test(id)) throw new GeneratedImageStoreError('stored image id is invalid', 'TALOS_IMAGE_PERSIST_INVALID');
75
+ const extension = GENERATED_IMAGE_MIME_TYPES[mimeType];
76
+ return {
77
+ bytes: join(imageDir, `${id}.${extension}`),
78
+ metadata: join(imageDir, `${id}.json`),
79
+ };
80
+ };
81
+
82
+ async function persistGeneratedImage({ bytes: rawBytes, mimeType, source, promptHash = null } = {}) {
83
+ const bytes = asBytes(rawBytes);
84
+ if (bytes.byteLength === 0) throw new GeneratedImageStoreError('empty image cannot be saved', 'TALOS_IMAGE_PERSIST_INVALID');
85
+ if (typeof mimeType !== 'string' || !Object.hasOwn(GENERATED_IMAGE_MIME_TYPES, mimeType)) throw new GeneratedImageStoreError('image format is not supported', 'TALOS_IMAGE_PERSIST_INVALID');
86
+ const normalizedSource = validateSource(source);
87
+ const normalizedPromptHash = validatePromptHash(promptHash);
88
+ const id = safeId({ bytes, source: normalizedSource, promptHash: normalizedPromptHash });
89
+ const destination = paths(id, mimeType);
90
+ const sha256 = createHash('sha256').update(bytes).digest('hex');
91
+ const metadata = {
92
+ schema: 'talos.generated-image.v1', id, mimeType, source: normalizedSource,
93
+ promptHash: normalizedPromptHash, bytes: bytes.byteLength, sha256, createdAt: now().toISOString(),
94
+ };
95
+ await fs.mkdir(imageDir, { recursive: true });
96
+ const temporaryBytes = `${destination.bytes}.tmp-${process.pid}-${randomUUID()}`;
97
+ const temporaryMetadata = `${destination.metadata}.tmp-${process.pid}-${randomUUID()}`;
98
+ try {
99
+ await fs.writeFile(temporaryBytes, bytes, { flag: 'wx' });
100
+ await fs.writeFile(temporaryMetadata, `${JSON.stringify(metadata)}\n`, { encoding: 'utf8', flag: 'wx' });
101
+ await fs.rename(temporaryBytes, destination.bytes);
102
+ await fs.rename(temporaryMetadata, destination.metadata);
103
+ } catch (error) {
104
+ await fs.rm(temporaryBytes, { force: true }).catch(() => {});
105
+ await fs.rm(temporaryMetadata, { force: true }).catch(() => {});
106
+ // A deterministic id means a retry of the same response is safe: if a
107
+ // complete pair is already present, reopen and return it instead of
108
+ // pretending that the second write was a new image.
109
+ if (error?.code === 'EEXIST') {
110
+ const existing = await readGeneratedImage(id).catch(() => null);
111
+ if (existing?.sha256 === sha256 && existing.mimeType === mimeType) return existing;
112
+ }
113
+ throw new GeneratedImageStoreError(`image could not be saved safely: ${error?.message ?? String(error)}`);
114
+ }
115
+ return { ...metadata, path: destination.bytes };
116
+ }
117
+
118
+ async function readGeneratedImage(id) {
119
+ if (typeof id !== 'string' || !ID.test(id)) throw new GeneratedImageStoreError('stored image id is invalid', 'TALOS_IMAGE_PERSIST_INVALID');
120
+ let metadata;
121
+ try { metadata = parseMetadata(JSON.parse(await fs.readFile(join(imageDir, `${id}.json`), 'utf8')), id); }
122
+ catch (error) {
123
+ if (error instanceof GeneratedImageStoreError) throw error;
124
+ if (error?.code === 'ENOENT') throw new GeneratedImageStoreError('stored image was not found', 'TALOS_IMAGE_PERSIST_NOT_FOUND');
125
+ throw new GeneratedImageStoreError(`stored image metadata could not be read: ${error?.message ?? String(error)}`, 'TALOS_IMAGE_PERSIST_CORRUPT');
126
+ }
127
+ const destination = paths(id, metadata.mimeType);
128
+ let bytes;
129
+ try { bytes = await fs.readFile(destination.bytes); }
130
+ catch (error) { throw new GeneratedImageStoreError(`stored image bytes could not be read: ${error?.message ?? String(error)}`, 'TALOS_IMAGE_PERSIST_CORRUPT'); }
131
+ if (bytes.byteLength !== metadata.bytes || createHash('sha256').update(bytes).digest('hex') !== metadata.sha256) throw new GeneratedImageStoreError('stored image checksum does not match', 'TALOS_IMAGE_PERSIST_CORRUPT');
132
+ return { ...metadata, bytes: Buffer.from(bytes), path: destination.bytes };
133
+ }
134
+
135
+ async function removeGeneratedImage(id) {
136
+ const metadataPath = join(imageDir, `${id}.json`);
137
+ let metadata;
138
+ try { metadata = parseMetadata(JSON.parse(await fs.readFile(metadataPath, 'utf8')), id); }
139
+ catch (error) { if (error?.code === 'ENOENT') return false; throw error; }
140
+ await fs.rm(paths(id, metadata.mimeType).bytes, { force: true });
141
+ await fs.rm(metadataPath, { force: true });
142
+ return true;
143
+ }
144
+
145
+ return Object.freeze({ persistGeneratedImage, readGeneratedImage, removeGeneratedImage });
146
+ }
147
+