talos-code 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +661 -0
  3. package/README.md +116 -3
  4. package/THIRD_PARTY_NOTICES.md +40 -0
  5. package/dist/archive/zip.js +73 -0
  6. package/dist/args.js +100 -0
  7. package/dist/automations/history-store.js +34 -0
  8. package/dist/automations/policy-store.js +43 -0
  9. package/dist/automations/runner.js +107 -0
  10. package/dist/automations/schedule.js +85 -0
  11. package/dist/automations/windows-task.js +53 -0
  12. package/dist/commands/advanced-cli.js +233 -0
  13. package/dist/commands/checkpoint-cli.js +45 -0
  14. package/dist/commands/config-cli.js +107 -0
  15. package/dist/commands/context.js +17 -0
  16. package/dist/commands/extensions-cli.js +207 -0
  17. package/dist/commands/project-cli.js +74 -0
  18. package/dist/commands/project-commands.js +143 -0
  19. package/dist/commands/provider-cli.js +244 -0
  20. package/dist/commands/services-cli.js +248 -0
  21. package/dist/commands/session-cli.js +171 -0
  22. package/dist/commands/system-cli.js +405 -0
  23. package/dist/config/commands.js +60 -0
  24. package/dist/config/load.js +382 -0
  25. package/dist/config/migrations.js +48 -0
  26. package/dist/config/types.js +11 -0
  27. package/dist/diagnostics/development-log.js +154 -0
  28. package/dist/diagnostics/doctor.js +104 -0
  29. package/dist/diagnostics/redact.js +80 -0
  30. package/dist/diagnostics/zip.js +52 -0
  31. package/dist/errors.js +156 -0
  32. package/dist/events/bridge.js +130 -0
  33. package/dist/extensions/installer.js +178 -0
  34. package/dist/extensions/package-schema.js +24 -0
  35. package/dist/headless/result.js +84 -0
  36. package/dist/headless/run.js +230 -0
  37. package/dist/i18n/en/approval.js +46 -0
  38. package/dist/i18n/en/common.js +9 -0
  39. package/dist/i18n/en/credentials.js +92 -0
  40. package/dist/i18n/en/errors.js +267 -0
  41. package/dist/i18n/en/firstrun.js +101 -0
  42. package/dist/i18n/en/providers.js +37 -0
  43. package/dist/i18n/en/screen.js +774 -0
  44. package/dist/i18n/en/tools.js +127 -0
  45. package/dist/i18n/en.js +14 -0
  46. package/dist/i18n/error-view.js +307 -0
  47. package/dist/i18n/index.js +19 -0
  48. package/dist/i18n/kernel-map.js +139 -0
  49. package/dist/io.js +45 -0
  50. package/dist/main.js +331 -0
  51. package/dist/paths.js +41 -0
  52. package/dist/protocol/v2/codec.js +9 -0
  53. package/dist/protocol/v2/events.js +555 -0
  54. package/dist/protocol/v2/index.js +4 -0
  55. package/dist/protocol/v2/replay.js +28 -0
  56. package/dist/protocol/v2/types.js +1 -0
  57. package/dist/provider/control-plane.js +135 -0
  58. package/dist/provider/environment-keys.js +275 -0
  59. package/dist/provider/health.js +110 -0
  60. package/dist/provider/missing-key.js +48 -0
  61. package/dist/provider/model-catalog.js +168 -0
  62. package/dist/provider/openrouter-login.js +172 -0
  63. package/dist/provider/provider-text.js +13 -0
  64. package/dist/provider/store.js +95 -0
  65. package/dist/provider/system-keyring.js +214 -0
  66. package/dist/runtime/active-run.js +63 -0
  67. package/dist/runtime/agent-tree.js +74 -0
  68. package/dist/runtime/attachments.js +84 -0
  69. package/dist/runtime/brokered-executor.js +395 -0
  70. package/dist/runtime/context-archive.js +69 -0
  71. package/dist/runtime/context-status.js +76 -0
  72. package/dist/runtime/create-runtime.js +111 -0
  73. package/dist/runtime/model-profile.js +361 -0
  74. package/dist/runtime/output-store.js +137 -0
  75. package/dist/runtime/provider-attempts.js +185 -0
  76. package/dist/runtime/replay-buffer.js +153 -0
  77. package/dist/runtime/repo.js +95 -0
  78. package/dist/runtime/session-facade.js +135 -0
  79. package/dist/runtime/session-summary.js +125 -0
  80. package/dist/runtime/supervisor.js +265 -0
  81. package/dist/runtime/talos-composition.js +1011 -0
  82. package/dist/runtime/types.js +5 -0
  83. package/dist/runtime/usage-snapshot.js +82 -0
  84. package/dist/security/auto-classifier.js +38 -0
  85. package/dist/security/credential-free-environment.js +54 -0
  86. package/dist/security/evaluate.js +243 -0
  87. package/dist/security/execution-backends.js +236 -0
  88. package/dist/security/execution-broker.js +102 -0
  89. package/dist/security/forge-scan.js +74 -0
  90. package/dist/security/from-approval.js +39 -0
  91. package/dist/security/mxc-execution-backend.js +281 -0
  92. package/dist/security/permission-engine.js +138 -0
  93. package/dist/security/permission-explanation.js +54 -0
  94. package/dist/security/persist.js +179 -0
  95. package/dist/security/plugin-guard.js +230 -0
  96. package/dist/security/process-tree-evidence-store.js +74 -0
  97. package/dist/security/process-tree-probe.js +554 -0
  98. package/dist/security/project-resource-inventory.js +186 -0
  99. package/dist/security/project-trust-gate.js +38 -0
  100. package/dist/security/project-trust.js +367 -0
  101. package/dist/security/rule-parser.js +201 -0
  102. package/dist/security/shell-segmentation.js +68 -0
  103. package/dist/security/trust-authority.js +324 -0
  104. package/dist/security/types.js +1 -0
  105. package/dist/security/workspace-identity.js +102 -0
  106. package/dist/services/automation-facade.js +59 -0
  107. package/dist/services/forge-facade.js +77 -0
  108. package/dist/services/hook-facade.js +240 -0
  109. package/dist/services/index.js +16 -0
  110. package/dist/services/library-facade.js +174 -0
  111. package/dist/services/mcp-facade.js +348 -0
  112. package/dist/services/memory-facade.js +126 -0
  113. package/dist/services/notes-facade.js +157 -0
  114. package/dist/services/plugin-facade.js +308 -0
  115. package/dist/services/research-facade.js +110 -0
  116. package/dist/services/task-board-facade.js +236 -0
  117. package/dist/services/workflow-catalog.js +246 -0
  118. package/dist/sessions/export-format.js +98 -0
  119. package/dist/sessions/metadata-store.js +160 -0
  120. package/dist/sessions/share.js +122 -0
  121. package/dist/sessions/transfer.js +16 -0
  122. package/dist/subcommands.js +62 -0
  123. package/dist/tui/agent-roster.js +36 -0
  124. package/dist/tui/agent-view.js +51 -0
  125. package/dist/tui/app.js +4098 -0
  126. package/dist/tui/approval.js +36 -0
  127. package/dist/tui/boot/boot-sequence.js +76 -0
  128. package/dist/tui/boot/cinematic.js +243 -0
  129. package/dist/tui/boot/desktop-logo.js +82 -0
  130. package/dist/tui/boot.js +3 -0
  131. package/dist/tui/busy-input.js +58 -0
  132. package/dist/tui/catalog-service.js +560 -0
  133. package/dist/tui/components/assistant-stream.js +1 -0
  134. package/dist/tui/components/command-menu.js +68 -0
  135. package/dist/tui/components/composer.js +219 -0
  136. package/dist/tui/components/diff.js +35 -0
  137. package/dist/tui/components/footer.js +48 -0
  138. package/dist/tui/components/header.js +6 -0
  139. package/dist/tui/components/markdown.js +305 -0
  140. package/dist/tui/components/status-indicator.js +38 -0
  141. package/dist/tui/components/terminal-shell.js +214 -0
  142. package/dist/tui/components/thinking-row.js +19 -0
  143. package/dist/tui/components/tool-row.js +97 -0
  144. package/dist/tui/components/transcript-virtualizer.js +165 -0
  145. package/dist/tui/components/transcript.js +43 -0
  146. package/dist/tui/descendant-approvals.js +104 -0
  147. package/dist/tui/diff-model.js +77 -0
  148. package/dist/tui/editor-history.js +21 -0
  149. package/dist/tui/editor.js +210 -0
  150. package/dist/tui/event-adapter.js +436 -0
  151. package/dist/tui/exit-output.js +58 -0
  152. package/dist/tui/external-editor.js +53 -0
  153. package/dist/tui/file-completion.js +89 -0
  154. package/dist/tui/focus-manager.js +5 -0
  155. package/dist/tui/highlight.js +30 -0
  156. package/dist/tui/input-router.js +18 -0
  157. package/dist/tui/interrupt.js +99 -0
  158. package/dist/tui/keybindings.js +194 -0
  159. package/dist/tui/keymap-resolver.js +91 -0
  160. package/dist/tui/launch-state.js +148 -0
  161. package/dist/tui/line-diff.js +57 -0
  162. package/dist/tui/live-activity.js +56 -0
  163. package/dist/tui/metrics.js +123 -0
  164. package/dist/tui/onboarding.js +36 -0
  165. package/dist/tui/overlays/agent-tree.js +43 -0
  166. package/dist/tui/overlays/approval-dialog.js +642 -0
  167. package/dist/tui/overlays/automation-center.js +51 -0
  168. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  169. package/dist/tui/overlays/context-inspector.js +36 -0
  170. package/dist/tui/overlays/effort-line.js +110 -0
  171. package/dist/tui/overlays/forge-center.js +46 -0
  172. package/dist/tui/overlays/help-dialog.js +16 -0
  173. package/dist/tui/overlays/history-picker.js +37 -0
  174. package/dist/tui/overlays/hook-center.js +70 -0
  175. package/dist/tui/overlays/library-center.js +55 -0
  176. package/dist/tui/overlays/mcp-center.js +60 -0
  177. package/dist/tui/overlays/memory-center.js +63 -0
  178. package/dist/tui/overlays/model-picker.js +77 -0
  179. package/dist/tui/overlays/notes-center.js +53 -0
  180. package/dist/tui/overlays/overlay-host.js +8 -0
  181. package/dist/tui/overlays/plugin-center.js +76 -0
  182. package/dist/tui/overlays/provider-picker.js +171 -0
  183. package/dist/tui/overlays/queue-editor.js +32 -0
  184. package/dist/tui/overlays/research-center.js +59 -0
  185. package/dist/tui/overlays/scroll-window.js +234 -0
  186. package/dist/tui/overlays/session-picker.js +115 -0
  187. package/dist/tui/overlays/tasks-center.js +83 -0
  188. package/dist/tui/overlays/theme-picker.js +21 -0
  189. package/dist/tui/overlays/transcript-search.js +58 -0
  190. package/dist/tui/overlays/trust-center.js +29 -0
  191. package/dist/tui/overlays/workflow-center.js +46 -0
  192. package/dist/tui/project-file-index.js +214 -0
  193. package/dist/tui/project-references.js +85 -0
  194. package/dist/tui/project-trust-prompt.js +289 -0
  195. package/dist/tui/prompt-history-store.js +116 -0
  196. package/dist/tui/queue-store.js +110 -0
  197. package/dist/tui/regions/budget.js +59 -0
  198. package/dist/tui/regions/views.js +96 -0
  199. package/dist/tui/render-coordinator.js +148 -0
  200. package/dist/tui/render-scheduler.js +4 -0
  201. package/dist/tui/run.js +69 -0
  202. package/dist/tui/selection-list.js +29 -0
  203. package/dist/tui/session-controller.js +791 -0
  204. package/dist/tui/session-export.js +54 -0
  205. package/dist/tui/setup-wizard.js +46 -0
  206. package/dist/tui/shell-input.js +35 -0
  207. package/dist/tui/shell-layout.js +48 -0
  208. package/dist/tui/shell-model.js +98 -0
  209. package/dist/tui/slash-commands.js +82 -0
  210. package/dist/tui/state.js +151 -0
  211. package/dist/tui/status-bar.js +125 -0
  212. package/dist/tui/status-view.js +40 -0
  213. package/dist/tui/terminal-capabilities.js +9 -0
  214. package/dist/tui/terminal-session.js +11 -0
  215. package/dist/tui/text-width.js +66 -0
  216. package/dist/tui/theme-catalog.js +27 -0
  217. package/dist/tui/theme-store.js +23 -0
  218. package/dist/tui/theme.js +23 -0
  219. package/dist/tui/tool-display.js +135 -0
  220. package/dist/tui/tool-facts.js +1 -0
  221. package/dist/tui/tools/bash-renderer.js +32 -0
  222. package/dist/tui/tools/change.js +61 -0
  223. package/dist/tui/tools/edit-renderer.js +17 -0
  224. package/dist/tui/tools/generic-renderer.js +13 -0
  225. package/dist/tui/tools/list-renderer.js +14 -0
  226. package/dist/tui/tools/read-renderer.js +13 -0
  227. package/dist/tui/tools/registry.js +20 -0
  228. package/dist/tui/tools/row-format.js +187 -0
  229. package/dist/tui/tools/search-renderer.js +38 -0
  230. package/dist/tui/tools/shared.js +97 -0
  231. package/dist/tui/tools/write-renderer.js +15 -0
  232. package/dist/tui/transcript-model.js +441 -0
  233. package/dist/tui/ui-preferences.js +79 -0
  234. package/dist/tui/usage-view.js +77 -0
  235. package/dist/tui/vim-mode.js +99 -0
  236. package/dist/update/check.js +15 -0
  237. package/dist/update/npm.js +51 -0
  238. package/dist/update/run.js +129 -0
  239. package/dist/version.js +2 -0
  240. package/dist/workspace/checkpoint-store.js +210 -0
  241. package/dist/workspace/checkpoint.js +413 -0
  242. package/dist/workspace/restore.js +232 -0
  243. package/package.json +64 -5
  244. package/vendor/context-engine/package.json +11 -0
  245. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  246. package/vendor/context-engine/src/contracts.mjs +48 -0
  247. package/vendor/context-engine/src/engine.mjs +445 -0
  248. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  249. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  250. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  251. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  252. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  253. package/vendor/context-engine/src/profiles.mjs +10 -0
  254. package/vendor/context-engine/src/retrieval.mjs +104 -0
  255. package/vendor/context-engine/src/summary.mjs +97 -0
  256. package/vendor/context-engine/src/usage.mjs +34 -0
  257. package/vendor/harness-ui/package.json +38 -0
  258. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  259. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  260. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  261. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  262. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  263. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  264. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  265. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  266. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  267. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  268. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  269. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  270. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  271. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  272. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  273. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  274. package/vendor/harness-ui/src/config.mjs +697 -0
  275. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  276. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  277. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  278. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  279. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  280. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  281. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  282. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  283. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  284. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  285. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  286. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  287. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  288. package/vendor/harness-ui/src/doctor.mjs +142 -0
  289. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  290. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  291. package/vendor/harness-ui/src/document-report.mjs +331 -0
  292. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  293. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  294. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  295. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  296. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  297. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  298. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  299. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  300. package/vendor/harness-ui/src/git-service.mjs +626 -0
  301. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  302. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  303. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  304. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  305. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  306. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  307. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  308. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  309. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  310. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  311. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  312. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  313. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  314. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  315. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  316. package/vendor/harness-ui/src/library-store.mjs +652 -0
  317. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  318. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  319. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  320. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  321. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  322. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  323. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  324. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  325. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  326. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  327. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  328. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  329. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  330. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  331. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  332. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  333. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  334. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  335. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  336. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  337. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  338. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  339. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  340. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  341. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  342. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  343. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  344. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  345. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  346. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  347. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  348. package/vendor/harness-ui/src/research/card.mjs +235 -0
  349. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  350. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  351. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  352. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  353. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  354. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  355. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  356. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  357. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  358. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  359. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  360. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  361. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  362. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  363. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  364. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  365. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  366. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  367. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  368. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  369. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  370. package/vendor/harness-ui/src/research/report.mjs +203 -0
  371. package/vendor/harness-ui/src/research/run.mjs +527 -0
  372. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  373. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  374. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  375. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  376. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  377. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  378. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  379. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  380. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  381. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  382. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  383. package/vendor/harness-ui/src/session-store.mjs +220 -0
  384. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  385. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  386. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  387. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  388. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  389. package/vendor/harness-ui/src/static-files.mjs +96 -0
  390. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  391. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  392. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  393. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  394. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  395. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  396. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  397. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  398. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  399. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  400. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  401. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  402. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  403. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  404. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  405. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  406. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  407. package/vendor/manifest.json +170 -0
@@ -0,0 +1,315 @@
1
+ /**
2
+ * usage-cache.mjs — «quanti token sono arrivati dalla cache?», chiesto UNA volta sola.
3
+ *
4
+ * ## Perché esiste
5
+ *
6
+ * Lezione del 22/08/2026 (`la-cache-vale-sei-volte-e-non-la-contavamo`): misurato sul banco,
7
+ * **87 token dentro per ogni 1 fuori**, cioè il **93% del costo è rileggere lo stesso prefisso**.
8
+ * La cura non era nell'agente: era che **due lettori non conoscevano il nome**
9
+ * `prompt_tokens_details.cached_tokens`.
10
+ *
11
+ * ⛔ Il 12/09/2026 l'inventario ha trovato che il difetto si era solo spostato. Ogni lettore ne
12
+ * conosce **uno**, e ognuno un nome diverso:
13
+ *
14
+ * | lettore | nome che conosce |
15
+ * |---|---|
16
+ * | `native-provider-adapter.mjs:44` | `inputTokenDetails.cacheReadTokens` (AI SDK) |
17
+ * | `context-native-compaction.mjs:9` | `input_tokens_details.cached_tokens` |
18
+ * | il kernel (`talosHarness.mjs:6417`, `:6482`) | tre nomi, in una catena `??` scritta a mano |
19
+ *
20
+ * Tre punti, tre vocabolari, nessuna fixture, e nessun posto dove aggiungere il quarto nome.
21
+ * Hermes, sullo stesso problema, ne conosce **sette** e li tiene in una funzione sola
22
+ * (`agent/usage_pricing.py:1296-1400`, `normalize_usage`).
23
+ *
24
+ * ⇒ Qui c'è **una** funzione, e i nomi non stanno nemmeno qui: stanno nel record del fornitore
25
+ * (`provider-registry.mjs`, campo `cache.letturaUsage`). Aggiungere un fornitore con una forma
26
+ * nuova è una riga di dato, non una riga di codice.
27
+ *
28
+ * ## Le due trappole, dette prima che qualcuno ci ricaschi
29
+ *
30
+ * ⛔⛔ **NON SI SOMMANO.** 🌐 `https://api-docs.deepseek.com/api/create-chat-completion`
31
+ * (letto 12/09/2026): «prompt_tokens … It equals prompt_cache_hit_tokens +
32
+ * prompt_cache_miss_tokens». Sommare `prompt_cache_hit_tokens` ai `prompt_tokens` conterebbe
33
+ * due volte lo stesso token. Si legge il **primo percorso che risponde**, mai la somma di due.
34
+ *
35
+ * ⛔⛔ **IL TOTALE NON SEMPRE LI INCLUDE.** Sul wire OpenAI i cached sono **dentro**
36
+ * `prompt_tokens`; sul wire Anthropic no — 🌐 `https://platform.claude.com/docs/en/build-with-
37
+ * claude/prompt-caching` (letto 12/09/2026): `total_input = cache_read + cache_creation + input`.
38
+ * Il record lo dichiara in `cache.inclusiNelTotale`, così il conto non dipende da chi legge.
39
+ *
40
+ * ⛔ **`cache_discount` NON è un conteggio di token.** 🌐
41
+ * `https://openrouter.ai/docs/features/prompt-caching` (letto 12/09/2026): sta **fuori** da
42
+ * `usage`, al livello del corpo, ed è un risparmio **in denaro** che può essere **negativo**
43
+ * sulle scritture Anthropic. Entra nel pannello costi, mai nei token.
44
+ *
45
+ * ⛔ **`null` non è `0`.** `session-registry.mjs:930` lo dice già a parole: «il consumo registrato
46
+ * non porta cached_tokens: il fornitore non ha dichiarato quanti token venissero dalla cache, e
47
+ * "non dichiarato" non è "nessuno"». Qui diventa eseguibile: nessun percorso dichiarato, o
48
+ * nessun percorso presente nel corpo ⇒ `null`.
49
+ */
50
+
51
+ import { REGISTRO_FORNITORI, fornitore, WIRE } from './provider-registry.mjs';
52
+
53
+ /**
54
+ * I percorsi che valgono per un WIRE, quando chi chiama non sa quale fornitore fosse.
55
+ *
56
+ * ⛔ Sono l'UNIONE delle forme note per quel trasporto, non una media: un lettore che conosce un
57
+ * nome in più non sbaglia mai, un lettore che ne conosce uno in meno riporta zero su una
58
+ * sessione che la cache l'ha usata eccome.
59
+ *
60
+ * Provenienza di ogni nome, perché fra un anno nessuno se lo ricordi:
61
+ * · `prompt_tokens_details.cached_tokens` — wire OpenAI standard, e OpenRouter 🌐;
62
+ * · `cache_read_input_tokens` — Anthropic, e i proxy che instradano Claude su wire OpenAI;
63
+ * · `prompt_cache_hit_tokens` — DeepSeek nativo 🌐;
64
+ * · `cached_tokens` al primo livello — Kimi/Moonshot 🌐 `https://platform.kimi.ai/docs/api/chat`
65
+ * («Number of tokens served from cache»), e anche la forma già appiattita dal nostro kernel;
66
+ * · `input_tokens_details.cached_tokens` — wire Responses (OpenAI, Meta, Router, xAI);
67
+ * · `usageMetadata.cachedContentTokenCount` / `total_cached_tokens` — Gemini REST / SDK.
68
+ */
69
+ const LETTURA_PER_WIRE = Object.freeze({
70
+ 'openai-chat': Object.freeze([
71
+ 'prompt_tokens_details.cached_tokens',
72
+ 'cache_read_input_tokens',
73
+ 'prompt_cache_hit_tokens',
74
+ 'cached_tokens',
75
+ ]),
76
+ 'openai-responses': Object.freeze([
77
+ 'input_tokens_details.cached_tokens',
78
+ 'prompt_tokens_details.cached_tokens',
79
+ 'cached_tokens',
80
+ ]),
81
+ 'anthropic-messages': Object.freeze([
82
+ 'cache_read_input_tokens',
83
+ 'prompt_tokens_details.cached_tokens',
84
+ ]),
85
+ gemini: Object.freeze([
86
+ 'prompt_tokens_details.cached_tokens',
87
+ 'usageMetadata.cachedContentTokenCount',
88
+ 'total_cached_tokens',
89
+ 'cached_tokens',
90
+ ]),
91
+ locale: Object.freeze([]),
92
+ });
93
+
94
+ const SCRITTURA_PER_WIRE = Object.freeze({
95
+ 'openai-chat': Object.freeze(['prompt_tokens_details.cache_write_tokens']),
96
+ 'openai-responses': Object.freeze(['input_tokens_details.cache_write_tokens', 'input_tokens_details.cache_creation_tokens']),
97
+ 'anthropic-messages': Object.freeze(['cache_creation_input_tokens']),
98
+ gemini: Object.freeze([]),
99
+ locale: Object.freeze([]),
100
+ });
101
+
102
+ /** Legge un percorso puntato, senza mai lanciare su una forma inattesa. */
103
+ function valoreAlPercorso(oggetto, percorso) {
104
+ let corrente = oggetto;
105
+ for (const segmento of percorso.split('.')) {
106
+ if (corrente === null || typeof corrente !== 'object') return undefined;
107
+ corrente = corrente[segmento];
108
+ }
109
+ return corrente;
110
+ }
111
+
112
+ /** Un conteggio di token valido: intero, finito, non negativo. Tutto il resto è «non dichiarato». */
113
+ function conteggio(valore) {
114
+ if (typeof valore !== 'number' && (typeof valore !== 'string' || valore.trim() === '')) return null;
115
+ const numero = Number(valore);
116
+ return Number.isFinite(numero) && numero >= 0 ? Math.trunc(numero) : null;
117
+ }
118
+
119
+ /**
120
+ * I percorsi da provare, in ordine, per un fornitore o per un wire.
121
+ *
122
+ * ⛔ Il record del fornitore VINCE sul wire — è il posto dove una forma particolare si dichiara —
123
+ * ma i percorsi del wire restano in coda: un fornitore che un giorno cambia nome al campo non
124
+ * deve tornare a zero senza che nessuno se ne accorga.
125
+ */
126
+ function percorsi(chiave, tavola) {
127
+ if (typeof chiave !== 'string' || chiave === '') return [];
128
+ const record = fornitore(chiave);
129
+ if (record) {
130
+ const propri = tavola === 'letturaUsage' ? record.cache?.letturaUsage ?? [] : record.cache?.scritturaUsage ?? [];
131
+ const delWire = (tavola === 'letturaUsage' ? LETTURA_PER_WIRE : SCRITTURA_PER_WIRE)[record.wire] ?? [];
132
+ return [...new Set([...propri, ...delWire])];
133
+ }
134
+ if (WIRE.includes(chiave)) return [...((tavola === 'letturaUsage' ? LETTURA_PER_WIRE : SCRITTURA_PER_WIRE)[chiave] ?? [])];
135
+ return [];
136
+ }
137
+
138
+ /**
139
+ * I token LETTI dalla cache in questa risposta.
140
+ *
141
+ * @param {object|null} usage il `usage` così come l'ha mandato il fornitore
142
+ * @param {string} wire un wire (`openai-chat`, `anthropic-messages`, …) **oppure** l'id di un
143
+ * fornitore del registro (`deepseek`, `openrouter`, …), che è più preciso
144
+ * @returns {number|null} il conteggio, o `null` se il fornitore non l'ha dichiarato.
145
+ * ⛔ `null`, mai `0`: «non dichiarato» non è «nessuno».
146
+ */
147
+ export function tokenDaCache(usage, wire) {
148
+ if (!usage || typeof usage !== 'object') return null;
149
+ for (const percorso of percorsi(wire, 'letturaUsage')) {
150
+ const valore = conteggio(valoreAlPercorso(usage, percorso));
151
+ if (valore !== null) return valore;
152
+ }
153
+ return null;
154
+ }
155
+
156
+ /**
157
+ * I token SCRITTI nella cache in questa risposta (dove il fornitore li dichiara).
158
+ * ⛔ Costano più di un token normale — 1,25× a 5 minuti, 2× a un'ora su Anthropic 🌐 — quindi
159
+ * contarli come letture farebbe sembrare un risparmio una spesa.
160
+ */
161
+ export function tokenScrittiInCache(usage, wire) {
162
+ if (!usage || typeof usage !== 'object') return null;
163
+ for (const percorso of percorsi(wire, 'scritturaUsage')) {
164
+ const valore = conteggio(valoreAlPercorso(usage, percorso));
165
+ if (valore !== null) return valore;
166
+ }
167
+ return null;
168
+ }
169
+
170
+ /**
171
+ * Lo sconto **in denaro** dichiarato dal fornitore per questa generazione.
172
+ *
173
+ * ⛔ Non è un token e non entra in nessuna somma di token. Sta fuori da `usage` (OpenRouter lo
174
+ * mette al livello del corpo), quindi si passa il CORPO, non l'usage. Può essere negativo.
175
+ */
176
+ export function scontoDaCache(corpo, wire) {
177
+ const record = fornitore(wire);
178
+ const campo = record?.cache?.scontoDichiarato;
179
+ if (!campo || !corpo || typeof corpo !== 'object') return null;
180
+ const valore = Number(valoreAlPercorso(corpo, campo) ?? valoreAlPercorso(corpo, `usage.${campo}`));
181
+ return Number.isFinite(valore) ? valore : null;
182
+ }
183
+
184
+ /**
185
+ * Rende un `usage` di qualunque forma leggibile da chi conosce **solo** il nome canonico
186
+ * `prompt_tokens_details.cached_tokens`.
187
+ *
188
+ * ⛔ NON riscrive niente di ciò che c'era: aggiunge il nome canonico quando manca, e lascia intatti
189
+ * i campi nativi del fornitore. Un lettore che già li conosce continua a leggerli; uno che
190
+ * conosce solo il canonico smette di riportare zero. È la stessa disciplina del kernel, che
191
+ * somma «quello che c'è, senza inventare».
192
+ * ⛔ Se non c'è niente da dichiarare l'oggetto torna **identico** (stessa referenza): chi chiama
193
+ * può usare quel confronto per non ricostruire un corpo che non è cambiato.
194
+ *
195
+ * @param {object|null} usage
196
+ * @param {string} wire wire o id di fornitore
197
+ * @returns {object|null}
198
+ */
199
+ export function normalizzaUsage(usage, wire) {
200
+ if (!usage || typeof usage !== 'object') return usage;
201
+ const letti = tokenDaCache(usage, wire);
202
+ // P-K — una scrittura Bedrock può essere dichiarata senza una lettura della cache.
203
+ const soloScritturaCloud = fornitore(wire)?.cloud && tokenScrittiInCache(usage, wire) !== null;
204
+ if (letti === null && !soloScritturaCloud) return usage;
205
+ const gia = conteggio(valoreAlPercorso(usage, 'prompt_tokens_details.cached_tokens'));
206
+ const scritti = tokenScrittiInCache(usage, wire);
207
+ const giaScritti = conteggio(valoreAlPercorso(usage, 'prompt_tokens_details.cache_write_tokens'));
208
+ if (gia === letti && (scritti === null || giaScritti === scritti)) return usage;
209
+ const dettagli = { ...(usage.prompt_tokens_details && typeof usage.prompt_tokens_details === 'object' ? usage.prompt_tokens_details : {}) };
210
+ if (letti !== null) dettagli.cached_tokens = letti;
211
+ // P-K — fine
212
+ if (scritti !== null) dettagli.cache_write_tokens = scritti;
213
+ return { ...usage, prompt_tokens_details: dettagli };
214
+ }
215
+
216
+ /**
217
+ * I token di ingresso davvero FATTURABILI a prezzo pieno, cioè quelli che non sono venuti dalla
218
+ * cache — con la formula giusta per il wire.
219
+ *
220
+ * ⛔ È la riga che rende utile `cache.inclusiNelTotale`: su wire OpenAI i cached sono già dentro
221
+ * `prompt_tokens` e vanno SOTTRATTI; su wire Anthropic `input_tokens` è già il solo residuo e
222
+ * sottrarre di nuovo dimezzerebbe il conto. Sbagliare qui sbaglia del doppio, in silenzio.
223
+ *
224
+ * @returns {number|null} `null` se non si sa (nessun totale, o nessuna dichiarazione di cache).
225
+ */
226
+ export function tokenNonDaCache(usage, wire) {
227
+ if (!usage || typeof usage !== 'object') return null;
228
+ const totale = conteggio(usage.prompt_tokens ?? usage.input_tokens);
229
+ if (totale === null) return null;
230
+ const letti = tokenDaCache(usage, wire);
231
+ if (letti === null) return totale;
232
+ const record = fornitore(wire);
233
+ const inclusi = record ? record.cache?.inclusiNelTotale !== false : (wire !== 'anthropic-messages');
234
+ return inclusi ? Math.max(0, totale - letti) : totale;
235
+ }
236
+
237
+ /** I percorsi dichiarati, per fornitore — serve al test di parità e a chi scrive un rapporto. */
238
+ export function percorsiDiCacheDichiarati() {
239
+ return Object.fromEntries(Object.keys(REGISTRO_FORNITORI).map((id) => [id, percorsi(id, 'letturaUsage')]));
240
+ }
241
+
242
+ /**
243
+ * BC-48 C — ingresso completo di UNA chiamata, comprese letture e scritture di cache.
244
+ * Il `prompt_tokens` pubblico è già completo, anche quando arriva dall'adapter Anthropic.
245
+ * Solo `input_tokens` sul wire nativo Anthropic esclude letture e scritture: si aggiungono
246
+ * usando il contratto del registro, senza sommare due alias della stessa misura.
247
+ */
248
+ export function tokenIngressoDaUsage(usage, wire) {
249
+ if (!usage || typeof usage !== 'object' || Array.isArray(usage)) return null;
250
+ const pubblico = conteggio(usage.prompt_tokens);
251
+ if (pubblico !== null) return pubblico;
252
+ const ingresso = conteggio(usage.input_tokens);
253
+ if (ingresso === null) return null;
254
+ const record = fornitore(wire);
255
+ const inclusi = record ? record.cache?.inclusiNelTotale !== false : wire !== 'anthropic-messages';
256
+ if (inclusi) return ingresso;
257
+ const letti = tokenDaCache(usage, wire);
258
+ const scritti = tokenScrittiInCache(usage, wire);
259
+ if (letti === null || scritti === null) return null;
260
+ return ingresso + letti + scritti;
261
+ }
262
+
263
+ /**
264
+ * Proiezione additiva della sessione dai SOLI CUSTOM `consumo-fornitore` durabili (P-H).
265
+ * `/usage` è cumulativo dentro un invio: non si somma a questi eventi per chiamata.
266
+ * Senza P-H si dichiara assenza di misura, anche nei log storici. I giri senza ingresso
267
+ * valido o senza letture dichiarate non entrano né al numeratore né al denominatore.
268
+ * `_sequenza` identifica l'evento nel log di questa sessione: un replay non lo duplica.
269
+ */
270
+ export function cacheSessioneDaEventi(eventi) {
271
+ let tokenIngresso = 0; let letti = 0; let giriMisurati = 0; let giriNonMisurati = 0;
272
+ const viste = new Set();
273
+ for (const evento of Array.isArray(eventi) ? eventi : []) {
274
+ if (evento?.type !== 'CUSTOM' || evento.name !== 'consumo-fornitore') continue;
275
+ if (Number.isSafeInteger(evento._sequenza)) {
276
+ if (viste.has(evento._sequenza)) continue;
277
+ viste.add(evento._sequenza);
278
+ }
279
+ const { usage, provider } = evento.value && typeof evento.value === 'object' ? evento.value : {};
280
+ const ingresso = tokenIngressoDaUsage(usage, provider);
281
+ const cache = tokenDaCache(usage, provider);
282
+ if (!Number.isSafeInteger(ingresso) || ingresso <= 0 || !Number.isSafeInteger(cache) || cache < 0 || cache > ingresso) {
283
+ giriNonMisurati += 1;
284
+ continue;
285
+ }
286
+ tokenIngresso += ingresso; letti += cache; giriMisurati += 1;
287
+ }
288
+ const misurato = giriMisurati > 0 && Number.isSafeInteger(tokenIngresso) && Number.isSafeInteger(letti);
289
+ return {
290
+ percentuale: misurato ? letti / tokenIngresso * 100 : null,
291
+ tokenIngresso: misurato ? tokenIngresso : null,
292
+ tokenDaCache: misurato ? letti : null,
293
+ giriMisurati, giriNonMisurati, fonte: 'consumo-fornitore',
294
+ };
295
+ }
296
+
297
+ /**
298
+ * ⛔ 14/09 — quante chiamate al fornitore sono PARTITE e poi state fermate (`esito: 'fermato'`, runtime-owner-adapter).
299
+ * Sono giri veri senza consumo dichiarato: il conto dei giri li include, quello dei token no, e chi mostra i token lo dice.
300
+ * Solo gli stop: un guasto (`interrotto`, `traffico`) ha già la sua strada. `_sequenza` evita il doppio a un replay.
301
+ * @returns {number} mai `null`: nei registri di prima del 14/09 non c'è niente da contare, e zero è la verità di quel log.
302
+ */
303
+ export function giriFermatiDaEventi(eventi) {
304
+ let fermati = 0;
305
+ const viste = new Set();
306
+ for (const evento of Array.isArray(eventi) ? eventi : []) {
307
+ if (evento?.type !== 'CUSTOM' || evento.name !== 'consumo-fornitore' || evento.value?.esito !== 'fermato') continue;
308
+ if (Number.isSafeInteger(evento._sequenza)) {
309
+ if (viste.has(evento._sequenza)) continue;
310
+ viste.add(evento._sequenza);
311
+ }
312
+ fermati += 1;
313
+ }
314
+ return fermati;
315
+ }
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Browser di directory pre-sessione. La navigazione resta in sola lettura;
3
+ * l'unica mutazione esposta è `createFolder`, azione esplicita del picker e
4
+ * confinata dagli stessi gate di root/reparse usati da `browse`.
5
+ *
6
+ * È separato dal file tree operativo perché prima di avviare una sessione non
7
+ * devono esistere CRUD, drag/drop, allegati o letture di contenuto. Il browser
8
+ * legge un livello per richiesta e il vero gate di sicurezza resta
9
+ * `custom-task.mjs`: questo adapter non concede permessi.
10
+ */
11
+ import { lstat, mkdir, readdir, realpath } from 'node:fs/promises';
12
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
13
+
14
+ const MAX_PATH_LENGTH = 1024;
15
+ const MAX_NAME_LENGTH = 255;
16
+ const WINDOWS_RESERVED_NAME = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu;
17
+
18
+ export class WorkspaceBrowserError extends Error {
19
+ constructor(message, code = 'WORKSPACE_NOT_AVAILABLE') {
20
+ super(message);
21
+ this.name = 'WorkspaceBrowserError';
22
+ this.code = code;
23
+ }
24
+ }
25
+
26
+ function failQuery() {
27
+ throw new WorkspaceBrowserError('Scegli una cartella compresa nel disco mostrato.', 'QUERY_INVALID');
28
+ }
29
+
30
+ function containsTraversal(path) {
31
+ return path.split(/[\\/]+/).includes('..');
32
+ }
33
+
34
+ function validateFolderName(name) {
35
+ if (typeof name !== 'string'
36
+ || name.length === 0
37
+ || name.length > MAX_NAME_LENGTH
38
+ || name !== name.trim()
39
+ || name === '.'
40
+ || name === '..'
41
+ || /[\\/:*?"<>|\0]/u.test(name)
42
+ || /[. ]$/u.test(name)
43
+ || WINDOWS_RESERVED_NAME.test(name)) failQuery();
44
+ return name;
45
+ }
46
+
47
+ function inside(root, candidate) {
48
+ const difference = relative(root, candidate);
49
+ return difference === ''
50
+ || (difference !== '..' && !difference.startsWith(`..${sep}`) && !isAbsolute(difference));
51
+ }
52
+
53
+ function pathKey(path) {
54
+ return resolve(path).replace(/[\\/]+$/, '').toLocaleLowerCase('en-US');
55
+ }
56
+
57
+ function projectMap(projectDirectories) {
58
+ const result = new Map();
59
+ for (const project of projectDirectories ?? []) {
60
+ if (!project || typeof project.id !== 'string' || typeof project.percorso !== 'string' || !isAbsolute(project.percorso)) continue;
61
+ result.set(pathKey(project.percorso), { id: project.id, name: project.nome || project.id, path: resolve(project.percorso) });
62
+ }
63
+ return result;
64
+ }
65
+
66
+ async function assertNoSymbolicLinkSegments(root, candidate, lstatFn) {
67
+ const difference = relative(root, candidate);
68
+ const segments = difference === '' ? [] : difference.split(sep).filter(Boolean);
69
+ let current = root;
70
+ const paths = [root];
71
+ for (const segment of segments) {
72
+ current = join(current, segment);
73
+ paths.push(current);
74
+ }
75
+ try {
76
+ for (const path of paths) {
77
+ const stats = await lstatFn(path);
78
+ if (stats?.isSymbolicLink?.()) failQuery();
79
+ }
80
+ } catch (error) {
81
+ if (error instanceof WorkspaceBrowserError) throw error;
82
+ throw new WorkspaceBrowserError('Questa cartella non è disponibile. Scegline un’altra oppure controlla Doctor.');
83
+ }
84
+ }
85
+
86
+ /**
87
+ * @param {object} deps
88
+ * @param {string} deps.rootDir
89
+ * @param {Array<{id:string,nome?:string,percorso:string}>} [deps.projectDirectories]
90
+ * @param {()=>Array<{etichetta:string,percorso:string,tipo?:string}>} [deps.recommendedDirectoriesFn]
91
+ * @param {typeof lstat} [deps.lstatFn]
92
+ * @param {typeof mkdir} [deps.mkdirFn]
93
+ * @param {typeof readdir} [deps.readdirFn]
94
+ * @param {typeof realpath} [deps.realpathFn]
95
+ */
96
+ export function createWorkspaceBrowser({
97
+ rootDir,
98
+ projectDirectories = [],
99
+ recommendedDirectoriesFn = () => [],
100
+ lstatFn = lstat,
101
+ mkdirFn = mkdir,
102
+ readdirFn = readdir,
103
+ realpathFn = realpath,
104
+ } = {}) {
105
+ if (typeof rootDir !== 'string' || !isAbsolute(rootDir) || rootDir.includes('\0') || rootDir.length > MAX_PATH_LENGTH) failQuery();
106
+ const lexicalRoot = resolve(rootDir);
107
+ const projects = projectMap(projectDirectories);
108
+
109
+ function recommendations(root) {
110
+ const result = [];
111
+ const seen = new Set();
112
+ const add = ({ label, path, kind, projectId = null }) => {
113
+ if (typeof path !== 'string' || !isAbsolute(path) || path.length > MAX_PATH_LENGTH || !inside(root, resolve(path))) return;
114
+ const key = pathKey(path);
115
+ if (seen.has(key)) return;
116
+ seen.add(key);
117
+ result.push({ label, path: resolve(path), kind, projectId });
118
+ };
119
+ for (const project of projects.values()) add({ label: project.name, path: project.path, kind: 'project', projectId: project.id });
120
+ let suggested = [];
121
+ try { suggested = recommendedDirectoriesFn() ?? []; } catch { suggested = []; }
122
+ for (const item of suggested) {
123
+ if (!item || typeof item.etichetta !== 'string') continue;
124
+ const project = projects.get(typeof item.percorso === 'string' ? pathKey(item.percorso) : '');
125
+ add({
126
+ label: item.etichetta,
127
+ path: item.percorso,
128
+ kind: project ? 'project' : (item.tipo === 'recent' ? 'recent' : 'known'),
129
+ projectId: project?.id ?? null,
130
+ });
131
+ }
132
+ return result;
133
+ }
134
+
135
+ return {
136
+ async browse(requestedPath) {
137
+ const input = requestedPath === undefined ? lexicalRoot : requestedPath;
138
+ if (typeof input !== 'string'
139
+ || input.length === 0
140
+ || input.length > MAX_PATH_LENGTH
141
+ || input.includes('\0')
142
+ || !isAbsolute(input)
143
+ || containsTraversal(input)) failQuery();
144
+ const candidate = resolve(input);
145
+ if (!inside(lexicalRoot, candidate)) failQuery();
146
+ await assertNoSymbolicLinkSegments(lexicalRoot, candidate, lstatFn);
147
+
148
+ let canonicalRoot;
149
+ let canonicalCandidate;
150
+ try {
151
+ [canonicalRoot, canonicalCandidate] = await Promise.all([realpathFn(lexicalRoot), realpathFn(candidate)]);
152
+ } catch {
153
+ throw new WorkspaceBrowserError('Questa cartella non è disponibile. Scegline un’altra oppure controlla Doctor.');
154
+ }
155
+ if (!inside(canonicalRoot, canonicalCandidate)) failQuery();
156
+
157
+ let entries;
158
+ try {
159
+ entries = await readdirFn(canonicalCandidate, { withFileTypes: true });
160
+ } catch {
161
+ throw new WorkspaceBrowserError('Questa cartella non è disponibile. Scegline un’altra oppure controlla Doctor.');
162
+ }
163
+ const items = entries
164
+ .filter((entry) => entry?.isDirectory?.() && !entry?.isSymbolicLink?.())
165
+ .map((entry) => {
166
+ const path = join(canonicalCandidate, entry.name);
167
+ return { name: entry.name, path, projectId: projects.get(pathKey(path))?.id ?? null };
168
+ })
169
+ .sort((left, right) => left.name.localeCompare(right.name, 'it', { sensitivity: 'base' }));
170
+
171
+ return {
172
+ root: canonicalRoot,
173
+ path: canonicalCandidate,
174
+ parent: pathKey(canonicalCandidate) === pathKey(canonicalRoot) ? null : dirname(canonicalCandidate),
175
+ items,
176
+ recommended: recommendations(canonicalRoot),
177
+ };
178
+ },
179
+
180
+ async createFolder(parentPath, name) {
181
+ validateFolderName(name);
182
+ if (typeof parentPath !== 'string'
183
+ || parentPath.length === 0
184
+ || parentPath.length > MAX_PATH_LENGTH
185
+ || parentPath.includes('\0')
186
+ || !isAbsolute(parentPath)
187
+ || containsTraversal(parentPath)) failQuery();
188
+ const lexicalParent = resolve(parentPath);
189
+ if (!inside(lexicalRoot, lexicalParent)) failQuery();
190
+ await assertNoSymbolicLinkSegments(lexicalRoot, lexicalParent, lstatFn);
191
+
192
+ let canonicalRoot;
193
+ let canonicalParent;
194
+ try {
195
+ [canonicalRoot, canonicalParent] = await Promise.all([realpathFn(lexicalRoot), realpathFn(lexicalParent)]);
196
+ } catch {
197
+ throw new WorkspaceBrowserError('Questa cartella non è disponibile. Scegline un’altra oppure controlla Doctor.');
198
+ }
199
+ if (!inside(canonicalRoot, canonicalParent)) failQuery();
200
+ const candidate = join(canonicalParent, name);
201
+ if (!inside(canonicalRoot, candidate) || candidate.length > MAX_PATH_LENGTH) failQuery();
202
+ try {
203
+ await mkdirFn(candidate, { recursive: false });
204
+ } catch (error) {
205
+ if (error?.code === 'EEXIST') {
206
+ throw new WorkspaceBrowserError('Esiste già un file o una cartella con questo nome.', 'WORKSPACE_ALREADY_EXISTS');
207
+ }
208
+ throw new WorkspaceBrowserError('Non riesco a creare la cartella qui. Controlla i permessi e riprova.');
209
+ }
210
+ return { name, path: candidate };
211
+ },
212
+ };
213
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * workspace-context.mjs — cosa mostrare nel pannello "Ambiente" del Context
3
+ * Rail per una sessione VERA. Piano `elegant-spinning-dongarra.md`, FASE 1
4
+ * (§1.3, riga "Contesto workspace" — prima "Parziale", ora vera).
5
+ *
6
+ * ⛔ Onesto, non forzato: i task di `progetti/` sono `cpSync` di una cartella
7
+ * di tre file, **mai un repository git** (verificato: nessuna `.git/` dentro
8
+ * `TALOS-BANCO/progetti/listino`, né altrove nel corpus). `branch` è quindi
9
+ * `null` per ogni task oggi lanciabile — non un difetto di questa funzione,
10
+ * è la verità sul corpus. Quando `storia/` (via `preparaDaCommit`, `git
11
+ * archive`) arriverà come opzione lanciabile, `git archive` produce un
12
+ * albero di file SENZA metadati git nemmeno lì — quindi anche allora
13
+ * `branch` resterà `null`. Dichiarato qui perché non sembri un bug futuro.
14
+ */
15
+ import { existsSync, readdirSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+
18
+ import { createProcessPolicy } from './process-policy.mjs';
19
+
20
+ const GIT_PROCESS_POLICY = createProcessPolicy({ allowedExecutables: ['git'] });
21
+
22
+ /** Profondità massima e tetto della scansione: un workspace da migliaia di cartelle non deve rallentare l'avvio della sessione. */
23
+ const PROFONDITA_REPO_ANNIDATI = 2;
24
+ const TETTO_CARTELLE_LETTE = 400;
25
+ const CARTELLE_SALTATE = new Set(['node_modules', '.git', 'dist', 'build', '.cache', 'target', 'vendor']);
26
+
27
+ /**
28
+ * ⭐ 04/9 — W1-13 (ricerca: un repository git dentro il workspace
29
+ * ha una fiducia PROPRIA — i suoi file di controllo/hook non sono quelli del
30
+ * workspace). Elenca le sottocartelle (profondità ≤ 2, tetto di letture)
31
+ * che contengono una `.git/` propria. Mai un'eccezione: una cartella
32
+ * illeggibile è saltata, e l'avvio della sessione non si ferma per questo.
33
+ * @returns {string[]} percorsi relativi al workspace, ordinati
34
+ */
35
+ export function repoAnnidati(cartella, { profondita = PROFONDITA_REPO_ANNIDATI, tetto = TETTO_CARTELLE_LETTE } = {}) {
36
+ const trovati = [];
37
+ let lette = 0;
38
+ const visita = (assoluto, relativo, livello) => {
39
+ if (livello > profondita || lette >= tetto) return;
40
+ let voci;
41
+ try { voci = readdirSync(assoluto, { withFileTypes: true }); } catch { return; }
42
+ lette += 1;
43
+ for (const voce of voci) {
44
+ if (!voce.isDirectory() || CARTELLE_SALTATE.has(voce.name)) continue;
45
+ const sotto = join(assoluto, voce.name);
46
+ const sottoRelativo = relativo ? `${relativo}/${voce.name}` : voce.name;
47
+ if (existsSync(join(sotto, '.git'))) { trovati.push(sottoRelativo); continue; } // un repo annidato non si scava: la sua fiducia è sua
48
+ visita(sotto, sottoRelativo, livello + 1);
49
+ }
50
+ };
51
+ if (typeof cartella === 'string' && cartella.length > 0) visita(cartella, '', 1);
52
+ return trovati.sort();
53
+ }
54
+
55
+ function ramoGit(cartella, exec) {
56
+ try {
57
+ const uscita = exec('git', ['-C', cartella, 'rev-parse', '--abbrev-ref', 'HEAD'], {
58
+ encoding: 'utf8',
59
+ stdio: ['ignore', 'pipe', 'ignore'],
60
+ });
61
+ const ramo = String(uscita).trim();
62
+ return ramo.length > 0 ? ramo : null;
63
+ } catch {
64
+ // ⛔ Non un repository git, o git non installato: stato onesto (null),
65
+ // mai un errore che interrompe l'avvio della sessione per questo.
66
+ return null;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Il nome del worktree collegato, o `null` se questa cartella e' il worktree PRINCIPALE.
72
+ *
73
+ * ⛔ Il modo affidabile non e' confrontare `--git-dir` con `--git-common-dir`: e' cercare il
74
+ * segmento `worktrees/` dentro `--git-dir` (git-scm.com/docs/git-worktree e la doc di
75
+ * `git-rev-parse`, lette il 10/09/2026). Misurato su questo repo: in un worktree collegato
76
+ * `--git-dir` vale `.../AVM/.git/worktrees/AVM-harness-desktop`, mentre nel principale vale `.git`
77
+ * — relativo, senza quel segmento. `$GIT_COMMON_DIR` puo' essere definito a mano e rendere il
78
+ * confronto fra i due bugiardo; il segmento no.
79
+ *
80
+ * ⛔⛔ Perche' questa funzione esiste: fino al 10/09 il pannello «Ambiente» scriveva `—` CABLATO,
81
+ * con un commento che diceva «mai un repository git nel corpus di oggi». Vero per i task del
82
+ * corpus (copie di tre file), FALSO per una sessione su una cartella dell'allowlist — e la riga
83
+ * sopra, «Ramo», lo smentiva gia' da sola mostrando `lane/harness-desktop`. Un trattino onesto
84
+ * quando non si sa; ma qui si sapeva, e non si guardava.
85
+ */
86
+ function worktreeGit(cartella, exec) {
87
+ try {
88
+ const uscita = exec('git', ['-C', cartella, 'rev-parse', '--git-dir'], {
89
+ encoding: 'utf8',
90
+ stdio: ['ignore', 'pipe', 'ignore'],
91
+ });
92
+ /* Windows e git mescolano le due barre nello stesso percorso: si separa su entrambe. */
93
+ const dentro = String(uscita).trim().split(/[/\\]+/);
94
+ const posizione = dentro.lastIndexOf('worktrees');
95
+ if (posizione < 0 || posizione === dentro.length - 1) return null;
96
+ return dentro[posizione + 1] || null;
97
+ } catch {
98
+ // Non un repository git, o git non installato: stesso stato onesto di ramoGit.
99
+ return null;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * @param {{cartella:string, progetto:string|null}} input
105
+ * @param {{exec?: Function}} [dipendenze] — SOLO per test: inietta
106
+ * un `exec` finto per provare "non è un repository git" senza spawnare un
107
+ * processo vero, o per provare un fallimento arbitrario.
108
+ * @returns {{progetto:string|null, cartella:string, branch:string|null, worktree:string|null}}
109
+ */
110
+ export function leggiContestoWorkspace({ cartella, progetto = null }, { exec } = {}) {
111
+ const execFn = exec ?? ((comando, argomenti, opzioni = {}) => GIT_PROCESS_POLICY.execFileSync(comando, argomenti, {
112
+ ...opzioni,
113
+ cwd: opzioni.cwd ?? cartella,
114
+ }));
115
+ return {
116
+ progetto,
117
+ cartella,
118
+ branch: ramoGit(cartella, execFn),
119
+ // ⭐ 10/09 — il nome del worktree collegato: la scheda Ambiente lo scriveva `—` cablato.
120
+ worktree: worktreeGit(cartella, execFn),
121
+ // ⭐ 04/9, W1-13 — repository dentro il workspace: fiducia separata (mostrato nella scheda Ambiente)
122
+ repoAnnidati: repoAnnidati(cartella),
123
+ };
124
+ }