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,626 @@
1
+ /**
2
+ * git-service.mjs — W1-05 (05/09). Lo stato Git di una sessione: **status,
3
+ * stage, unstage, commit, ramo**. Ledger: `.claude/LEDGER-W1-05-GIT-SERVICE-2026-09-05.md`.
4
+ *
5
+ * Serve a W1-06, la Review a **due sorgenti dichiarate**: «Ultimo giro
6
+ * agente» viene dagli eventi, «Non committato» viene da qui. Le due non si
7
+ * mescolano mai, e questo file risponde solo della seconda.
8
+ *
9
+ * ⛔⛔⛔ `push` NON ESISTE IN QUESTO FILE, in nessuna forma, e nemmeno
10
+ * `remote`. Non è una precauzione: è una regola dell'owner — il push si
11
+ * chiede a lui ogni volta. `tests/git-service.test.mjs` la **pinna** leggendo
12
+ * il sorgente: se qualcuno aggiungesse la parola, quel test diventa rosso.
13
+ *
14
+ * ═══════════════════════════════════════════════════════════════════════
15
+ * LA RICERCA CHE HA CAMBIATO IL DISEGNO (fonti + data, tutte del 05/09/2026)
16
+ * ═══════════════════════════════════════════════════════════════════════
17
+ *
18
+ * 1. ⛔ `git status` si legge SEMPRE con `-z` — git-scm.com/docs/git-status,
19
+ * letto il 05/09/2026: «Without the `-z` option, pathnames with "unusual"
20
+ * characters are quoted as explained for the configuration variable
21
+ * `core.quotePath`». MISURATO su git 2.55.0.windows.3: senza `-z`
22
+ * `àccento.txt` arriva come `"\303\240ccento.txt"` (ottale, fra
23
+ * virgolette) e `con spazio.txt` fra virgolette. Con `-z`: byte grezzi,
24
+ * nessun escape da disfare. Su un progetto italiano non è un caso limite.
25
+ *
26
+ * 2. ⛔ Nel formato `-z` una RINOMINA cambia forma e **ordine** — stessa
27
+ * pagina: «the `->` is omitted from rename entries and the field order is
28
+ * reversed (e.g. `from -> to` becomes `to` `from`)». MISURATO: senza `-z`
29
+ * → `R vecchio.txt -> nuovo.txt`; con `-z` → `R nuovo.txt<NUL>vecchio.txt`.
30
+ * Un parser che non lo sa mostra ogni rinomina AL CONTRARIO.
31
+ *
32
+ * 3. ⭐ Perché **v1** e non `--porcelain=v2` (la domanda era aperta nel brief).
33
+ * Stessa pagina: «Version 1 porcelain format is similar to the short
34
+ * format, but is **guaranteed not to change in a backwards-incompatible
35
+ * way** between Git versions or based on user configuration. This makes it
36
+ * ideal for parsing by scripts.» Per v2 la stessa garanzia NON è scritta;
37
+ * dice solo che gli header sono estensibili e che «Parsers should ignore
38
+ * headers they don't recognize». ⇒ v1 è la scelta con la promessa scritta,
39
+ * e v2 non porterebbe niente che ci serva: i suoi campi in più sono modi,
40
+ * oggetti e punteggio di somiglianza, che la Review non mostra.
41
+ * ⛔ E v2 NON risolve la rinomina: MISURATO, anche lì l'ordine è
42
+ * `<path><sep><origPath>`, cioè di nuovo «prima il DOVE, poi il DA».
43
+ *
44
+ * 4. ⛔⛔⛔ **`git commit -- <percorsi>` NON committa quello che è in STAGE.**
45
+ * git-scm.com/docs/git-commit, `--only`, letto il 05/09/2026: «Make a
46
+ * commit by taking the **updated working tree contents** of the paths
47
+ * specified on the command line, disregarding any contents that have been
48
+ * staged for other paths. This is the default mode of operation of git
49
+ * commit if any paths are given on the command line». MISURATO: messo in
50
+ * stage `v1-STAGED`, poi cambiato il file in `v2-WORKTREE`, poi
51
+ * `git commit -F msg -- a.txt` ⇒ nel commit c'è **`v2-WORKTREE`**.
52
+ * ⇒ Due conseguenze opposte, ed entrambe contano:
53
+ * ⭐ BUONA — un percorso esplicito **non trascina** ciò che un'altra
54
+ * sessione ha messo in stage: MISURATO, `altro.txt` è rimasto in stage
55
+ * e fuori dal commit. È la cura vera del difetto «due sessioni, stessa
56
+ * cartella, intrecciano i commit», ed è git stesso a garantirla.
57
+ * ⛔ CATTIVA — se il file è cambiato DOPO lo stage, finisce nel commit
58
+ * la versione nuova, cioè **non quella che la Review ha mostrato**.
59
+ * Silenziosa. Per questo `commit()` **rifiuta** quel caso per nome
60
+ * (`GIT_WORKTREE_DIFFERS`) invece di committare la cosa sbagliata.
61
+ *
62
+ * 5. ⛔⛔ La magia dei pathspec **sopravvive a `--`**, e scavalca lo scoping.
63
+ * MISURATO: da una sottocartella, `git add -- :/` ha messo in stage
64
+ * **l'intero repository** (`:/` = "dalla radice del repo"), e
65
+ * `:(exclude)…` filtra a piacere. La cura è `--literal-pathspecs` —
66
+ * git-scm.com/docs/git, letto il 05/09/2026: «Treat pathspecs literally
67
+ * (i.e. no globbing, no pathspec magic)». MISURATO: con quel flag `:/`
68
+ * diventa un nome di file letterale che non esiste e git rifiuta.
69
+ * ⇒ Ogni comando di questo file lo porta, SEMPRE, anche in lettura.
70
+ *
71
+ * 6. ⛔⛔ L'indice è CONDIVISO con l'owner e con le altre sessioni, e
72
+ * `git status` di suo **scrive**: git-scm.com/docs/git, letto il
73
+ * 05/09/2026 — «this will prevent `git status` from refreshing the index
74
+ * as a side effect. This is useful for processes running in the background
75
+ * which do not want to cause lock contention». ⇒ tutto ciò che LEGGE porta
76
+ * `--no-optional-locks`: un pannello Review che si aggiorna non deve
77
+ * prendersi `index.lock` sotto le mani di chi sta lavorando.
78
+ *
79
+ * 7. ⛔ `git branch --show-current`, non `rev-parse --abbrev-ref HEAD`.
80
+ * MISURATO, due casi in cui il secondo mente o muore:
81
+ * · repo appena creato, nessun commit ⇒ `rev-parse` esce **128**
82
+ * («ambiguous argument 'HEAD'»), `--show-current` risponde `master`;
83
+ * · HEAD staccata ⇒ `rev-parse` risponde la **stringa `HEAD`**, che
84
+ * sembra un nome di ramo; `--show-current` risponde `''`, cioè la
85
+ * verità: nessun ramo.
86
+ * ⛔ `src/workspace-context.mjs` usa ancora la prima forma: è un rilievo
87
+ * REGISTRATO nel ledger, non corretto qui — non è la mia riga.
88
+ *
89
+ * 8. ⛔ Su Windows `spawn`/`execFile` con argomenti non fidati è stato una
90
+ * RCE quando l'eseguibile è un `.bat`/`.cmd`: CVE-2024-27980, e il suo
91
+ * bypass CVE-2024-36138 (nodejs.org/en/blog/vulnerability/april-2024-security-releases-2,
92
+ * letto il 05/09/2026) — «command injection … even if the shell option is
93
+ * not enabled». Qui l'eseguibile è `git` (che risolve a `git.exe`) e
94
+ * `process-policy` impone `shell:false`; in più nessun argomento è mai
95
+ * concatenato in una stringa, i percorsi vanno dopo `--`, e i nomi che
96
+ * cominciano per `-` o `:` sono rifiutati a monte da `normalizzaPercorso`.
97
+ *
98
+ * ═══════════════════════════════════════════════════════════════════════
99
+ * I CONFINI — misurati su questo repository, non presunti
100
+ * ═══════════════════════════════════════════════════════════════════════
101
+ *
102
+ * ⛔ **Questa cartella è un WORKTREE**: `.git` è un FILE
103
+ * (`gitdir: …/AVM/.git/worktrees/AVM-harness-desktop`), non una directory.
104
+ * Per questo NIENTE qui dentro cerca `.git` come cartella per decidere se
105
+ * un posto è un repository: la domanda si fa a git, con
106
+ * `rev-parse --show-toplevel --show-prefix`. VERIFICATO: da qui
107
+ * `--show-toplevel` risponde `…/AVM-harness-desktop`, cioè la radice del
108
+ * worktree, non quella del clone originale.
109
+ *
110
+ * ⛔ **La cartella della sessione può essere una SOTTOCARTELLA del repo.**
111
+ * MISURATO: da una sottocartella, `git status` **senza** pathspec elenca
112
+ * TUTTO il repository padre — cioè file che il workspace non contiene. Per
113
+ * questo ogni lettura è ristretta con `-- .`, e i percorsi che git restituisce
114
+ * (sempre relativi alla RADICE del repo: «paths shown will always be relative
115
+ * to the repository root») vengono riportati al vocabolario della sessione
116
+ * togliendo `--show-prefix`. ⇒ Fuori di qui esiste **un solo vocabolario**:
117
+ * percorsi relativi alla cartella della sessione.
118
+ *
119
+ * ⛔ **Repo annidati — W1-13**: un repository dentro il workspace ha fiducia
120
+ * PROPRIA e non si attraversa (`src/workspace-context.mjs`, che li elenca
121
+ * fermandosi su `existsSync(join(sotto,'.git'))`). Qui il confine lo tiene
122
+ * git stesso, ed è MISURATO: il padre vede `?? annidato/` — la cartella, mai
123
+ * i file dentro — **anche con `-uall`**. Questo file non fa niente per
124
+ * scavalcarlo; si limita a **dirlo**, marcando quella voce `repoAnnidato:true`
125
+ * con lo stesso identico test del modulo di W1-13, così la Review non
126
+ * presenta come «file da aggiungere» il contenuto di un altro repository.
127
+ */
128
+ import { execFile } from 'node:child_process';
129
+ import { existsSync } from 'node:fs';
130
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
131
+ import { tmpdir } from 'node:os';
132
+ import { isAbsolute, join, resolve } from 'node:path';
133
+
134
+ import { isPathInside } from './path-policy.mjs';
135
+ import { createProcessPolicy } from './process-policy.mjs';
136
+
137
+ /**
138
+ * ⛔ Le chiavi d'ambiente che git può vedere. `HOME`/`USERPROFILE` NON sono
139
+ * di comodo: senza, git non trova `~/.gitconfig` e quindi non trova
140
+ * `user.name`/`user.email` — cioè `commit()` fallirebbe sull'identità.
141
+ * ⛔ Nessuna variabile `GIT_*`: le due che ci servono (`GIT_OPTIONAL_LOCKS`,
142
+ * `GIT_LITERAL_PATHSPECS`) sono passate come FLAG sulla riga di comando, dove
143
+ * si vedono in un test e in un log, invece che come ambiente invisibile.
144
+ */
145
+ const AMBIENTE_GIT = Object.freeze([
146
+ 'PATH', 'Path', 'PATHEXT', 'SystemRoot', 'WINDIR', 'COMSPEC',
147
+ 'HOME', 'USERPROFILE', 'TMP', 'TEMP', 'LANG', 'LC_ALL',
148
+ ]);
149
+
150
+ /**
151
+ * ⛔ Davanti a OGNI sottocomando, senza eccezioni. Il primo tiene le mani
152
+ * lontane da `index.lock` su un indice condiviso (ricerca 6), il secondo
153
+ * toglie ai percorsi del client ogni potere di pathspec (ricerca 5).
154
+ */
155
+ const FLAG_SEMPRE = Object.freeze(['--no-optional-locks', '--literal-pathspecs']);
156
+
157
+ const TIMEOUT_LETTURA_MS = 15_000;
158
+ const TIMEOUT_SCRITTURA_MS = 60_000;
159
+ /** Un `git status` di un monorepo enorme non deve diventare un errore misterioso: oltre questo si dice PERCHÉ. */
160
+ const TETTO_USCITA_BYTE = 32 * 1024 * 1024;
161
+ const TETTO_MESSAGGIO_BYTE = 64 * 1024;
162
+ /** Le combinazioni XY che git documenta come "unmerged": un conflitto non è né staged né non-staged, è una terza cosa. */
163
+ const CONFLITTO = new Set(['DD', 'AU', 'UD', 'UA', 'DU', 'AA', 'UU']);
164
+
165
+ export class GitServiceError extends Error {
166
+ constructor(message, code = 'GIT_COMMAND_FAILED') {
167
+ super(message);
168
+ this.name = 'GitServiceError';
169
+ this.code = code;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Un percorso arrivato dal client è **testo non fidato**. Qui si decide, in
175
+ * un posto solo, cosa può essere — e ogni rifiuto è per nome.
176
+ *
177
+ * ⛔ AL CONTRARIO, esplicitamente: assoluto no (porterebbe fuori dalla
178
+ * sessione), `..` no (stessa cosa per un'altra strada), iniziale `-` no (un
179
+ * argomento che sembra un'opzione), iniziale `:` no (è la magia di pathspec
180
+ * MISURATA alla ricerca 5 — `--literal-pathspecs` la disinnesca già, e questa
181
+ * è la seconda serratura sulla stessa porta), NUL no.
182
+ *
183
+ * @returns {string} il percorso normalizzato con `/`, relativo alla cartella
184
+ */
185
+ export function normalizzaPercorso(cartella, percorso) {
186
+ if (typeof percorso !== 'string' || percorso.trim() === '' || percorso.includes('\0')) {
187
+ throw new GitServiceError('Percorso non valido', 'GIT_PATH_INVALID');
188
+ }
189
+ if (isAbsolute(percorso) || /^[a-zA-Z]:/u.test(percorso)) {
190
+ throw new GitServiceError('Il percorso deve essere relativo alla cartella della sessione', 'GIT_PATH_INVALID');
191
+ }
192
+ if (percorso.startsWith('-')) {
193
+ throw new GitServiceError('Un percorso non può cominciare con «-»', 'GIT_PATH_INVALID');
194
+ }
195
+ if (percorso.startsWith(':')) {
196
+ throw new GitServiceError('Un percorso non può cominciare con «:»', 'GIT_PATH_INVALID');
197
+ }
198
+ const pezzi = percorso.split(/[\\/]+/u);
199
+ if (pezzi.some((p) => p === '..')) {
200
+ throw new GitServiceError('Il percorso esce dalla cartella della sessione', 'GIT_PATH_INVALID');
201
+ }
202
+ const pulito = pezzi.filter((p) => p !== '' && p !== '.').join('/');
203
+ if (pulito === '') throw new GitServiceError('Percorso non valido', 'GIT_PATH_INVALID');
204
+ /* ⛔ Terza serratura: il controllo lessicale di containment che usa già il resto del server (`path-policy.isPathInside`), su percorsi REALI — così un giorno in cui una delle due regole sopra cambiasse, questa continuerebbe a mordere. */
205
+ if (!isPathInside(cartella, resolve(cartella, pulito))) {
206
+ throw new GitServiceError('Il percorso esce dalla cartella della sessione', 'GIT_PATH_INVALID');
207
+ }
208
+ return pulito;
209
+ }
210
+
211
+ /**
212
+ * Analizza l'uscita di `git status --porcelain=v1 -z`.
213
+ *
214
+ * ⛔ La forma è: `XY<spazio><percorso><NUL>`, e per una rinomina/copia
215
+ * **due** campi — `XY<spazio><DOVE><NUL><DA><NUL>` — con l'ordine INVERTITO
216
+ * rispetto alla forma leggibile (ricerca 2, misurata). Chi legge questa
217
+ * funzione deve sapere che `da` viene DOPO nel flusso ma è il PRIMA nel tempo.
218
+ *
219
+ * @param {string} testo uscita grezza, già decodificata una volta sola
220
+ * @returns {Array<{x:string,y:string,percorsoRepo:string,daRepo:string|null}>}
221
+ */
222
+ export function analizzaStatoPorcelain(testo) {
223
+ if (typeof testo !== 'string') throw new GitServiceError('Uscita di git non leggibile', 'GIT_COMMAND_FAILED');
224
+ const campi = testo.split('\0');
225
+ const voci = [];
226
+ let i = 0;
227
+ while (i < campi.length) {
228
+ const riga = campi[i];
229
+ i += 1;
230
+ if (riga === '') continue; // la coda dopo l'ultimo NUL, e nient'altro
231
+ if (riga.length < 4 || riga[2] !== ' ') {
232
+ throw new GitServiceError('Riga di stato git non riconosciuta', 'GIT_COMMAND_FAILED');
233
+ }
234
+ const x = riga[0];
235
+ const y = riga[1];
236
+ const percorsoRepo = riga.slice(3);
237
+ let daRepo = null;
238
+ if (x === 'R' || x === 'C' || y === 'R' || y === 'C') {
239
+ /* ⛔ Il campo in più c'è SOLO qui. Consumarlo sempre spezzerebbe l'allineamento di tutte le voci successive. */
240
+ if (i >= campi.length) throw new GitServiceError('Rinomina senza percorso di origine', 'GIT_COMMAND_FAILED');
241
+ daRepo = campi[i];
242
+ i += 1;
243
+ }
244
+ voci.push({ x, y, percorsoRepo, daRepo });
245
+ }
246
+ return voci;
247
+ }
248
+
249
+ /** Dalla coppia XY al vocabolario che la Review mostra. `X` = indice, `Y` = albero di lavoro (git-status(1)). */
250
+ function descrivi(x, y) {
251
+ const xy = `${x}${y}`;
252
+ if (CONFLITTO.has(xy)) return { tipo: 'conflitto', staged: false, nonStaged: false, conflitto: true };
253
+ if (x === '?' && y === '?') return { tipo: 'nonTracciato', staged: false, nonStaged: true, conflitto: false };
254
+ if (x === '!' && y === '!') return { tipo: 'ignorato', staged: false, nonStaged: false, conflitto: false };
255
+ const lettera = x !== ' ' && x !== '?' && x !== '!' ? x : y;
256
+ const tipo = { M: 'modificato', A: 'aggiunto', D: 'eliminato', R: 'rinominato', C: 'copiato', T: 'tipoCambiato' }[lettera] ?? 'modificato';
257
+ return {
258
+ tipo,
259
+ staged: x !== ' ' && x !== '?' && x !== '!',
260
+ nonStaged: y !== ' ' && y !== '?' && y !== '!',
261
+ conflitto: false,
262
+ };
263
+ }
264
+
265
+ /**
266
+ * @param {object} deps
267
+ * @param {(sessionId:string)=>string|null} deps.cartellaDiSessione ⛔ L'UNICA
268
+ * autorità sulla cartella, esattamente come in `terminal-registry.mjs`: il
269
+ * client NOMINA una sessione, non sceglie mai un percorso di lavoro. Un id
270
+ * sconosciuto torna `null` e qui non succede niente — nessun ripiego sul
271
+ * primo progetto configurato (era il difetto di `server.mjs:589`, W1-01).
272
+ * @param {Function} [deps.eseguiGitFn] SOLO per i test: sostituisce l'esecuzione vera.
273
+ */
274
+ export function creaServizioGit({
275
+ cartellaDiSessione,
276
+ eseguiGitFn = null,
277
+ timeoutLetturaMs = TIMEOUT_LETTURA_MS,
278
+ timeoutScritturaMs = TIMEOUT_SCRITTURA_MS,
279
+ esisteFn = existsSync,
280
+ } = {}) {
281
+ if (typeof cartellaDiSessione !== 'function') {
282
+ throw new GitServiceError('Serve l’autorità sulla cartella di sessione', 'GIT_STORE_UNAVAILABLE');
283
+ }
284
+
285
+ /**
286
+ * ⛔⛔ Perché `policy.execFile` e non `runApprovedProcess`, che è la porta
287
+ * che usano gli altri: `runApprovedProcess` accumula l'uscita decodificando
288
+ * **ogni pezzo separatamente** (`process-policy.mjs`, `appendBounded`:
289
+ * `chunk.toString('utf8')`). Un carattere UTF-8 multibyte che cade a
290
+ * cavallo di due pezzi si spezza, e `àccento.txt` tornerebbe corrotto —
291
+ * cioè esattamente la garanzia che `-z` esiste per dare. Qui l'uscita si
292
+ * prende in **Buffer** e si decodifica UNA volta sola, alla fine.
293
+ * ⛔ Resta tutto dentro `process-policy`: eseguibile in elenco, `shell:false`
294
+ * imposto, ambiente filtrato, e `cwdRoot` = la cartella della sessione,
295
+ * quindi la cartella di lavoro non può nemmeno essere un'altra.
296
+ */
297
+ async function eseguiVero(cartella, argomenti, timeoutMs) {
298
+ const policy = createProcessPolicy({
299
+ allowedExecutables: ['git'],
300
+ cwdRoot: cartella,
301
+ envAllowlist: AMBIENTE_GIT,
302
+ });
303
+ return new Promise((esci) => {
304
+ policy.execFile('git', argomenti, {
305
+ cwd: cartella,
306
+ encoding: 'buffer',
307
+ timeout: timeoutMs,
308
+ maxBuffer: TETTO_USCITA_BYTE,
309
+ windowsHide: true,
310
+ }, (errore, stdout, stderr) => {
311
+ const fuori = Buffer.isBuffer(stdout) ? stdout.toString('utf8') : String(stdout ?? '');
312
+ const errori = Buffer.isBuffer(stderr) ? stderr.toString('utf8') : String(stderr ?? '');
313
+ if (!errore) { esci({ codice: 0, stdout: fuori, stderr: errori }); return; }
314
+ esci({
315
+ codice: typeof errore.code === 'number' ? errore.code : null,
316
+ stdout: fuori,
317
+ stderr: errori,
318
+ scaduto: errore.killed === true || errore.signal != null,
319
+ troppoGrande: errore.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER',
320
+ avvioFallito: errore.code === 'ENOENT',
321
+ });
322
+ });
323
+ });
324
+ }
325
+
326
+ const eseguiGit = eseguiGitFn ?? eseguiVero;
327
+
328
+ /** Lancia con i flag obbligatori davanti, e traduce ogni fallimento in un codice NOMINATO. */
329
+ async function git(cartella, argomenti, { timeoutMs = timeoutLetturaMs, tollera = false } = {}) {
330
+ const esito = await eseguiGit(cartella, [...FLAG_SEMPRE, ...argomenti], timeoutMs);
331
+ if (esito.codice === 0) return esito;
332
+ if (tollera) return esito;
333
+ if (esito.troppoGrande) throw new GitServiceError('L’uscita di git supera il limite consentito', 'GIT_OUTPUT_TOO_LARGE');
334
+ if (esito.scaduto) throw new GitServiceError('git non ha risposto entro il tempo massimo', 'GIT_TIMEOUT');
335
+ if (esito.avvioFallito) throw new GitServiceError('git non è installato o non è raggiungibile', 'GIT_COMMAND_FAILED');
336
+ const detto = String(esito.stderr || '').trim();
337
+ if (/not a git repository/iu.test(detto)) {
338
+ throw new GitServiceError('Questa cartella non è un repository git', 'GIT_NOT_A_REPOSITORY');
339
+ }
340
+ throw new GitServiceError(detto || 'git ha risposto con un errore', 'GIT_COMMAND_FAILED');
341
+ }
342
+
343
+ /** La cartella della sessione, o un rifiuto. ⛔ Mai un ripiego: un id ignoto non prende NIENTE. */
344
+ function cartellaDi(sessionId) {
345
+ if (typeof sessionId !== 'string' || sessionId === '') {
346
+ throw new GitServiceError('Sessione non valida', 'QUERY_INVALID');
347
+ }
348
+ const cartella = cartellaDiSessione(sessionId);
349
+ if (typeof cartella !== 'string' || cartella === '') {
350
+ throw new GitServiceError('Sessione non trovata', 'NOT_FOUND');
351
+ }
352
+ return resolve(cartella);
353
+ }
354
+
355
+ /**
356
+ * Dove comincia il repository, e quanto è profonda la cartella della
357
+ * sessione al suo interno. ⛔ Una sola domanda a git per entrambe le cose:
358
+ * `rev-parse` stampa una riga per ciascun argomento, in ordine. Alla radice
359
+ * `--show-prefix` è la riga VUOTA, e va bene così.
360
+ */
361
+ async function collocazione(cartella) {
362
+ const esito = await git(cartella, ['rev-parse', '--show-toplevel', '--show-prefix']);
363
+ const righe = esito.stdout.split('\n');
364
+ const radiceRepo = (righe[0] ?? '').trim();
365
+ if (radiceRepo === '') throw new GitServiceError('Questa cartella non è un repository git', 'GIT_NOT_A_REPOSITORY');
366
+ const prefisso = (righe[1] ?? '').trim();
367
+ return {
368
+ radiceRepo,
369
+ prefisso,
370
+ /* ⛔ Un'informazione onesta, non un giudizio: la sessione è la radice del repo, oppure ci sta dentro. Il pannello lo deve poter dire a chi guarda. */
371
+ cartellaEradiceRepo: prefisso === '',
372
+ };
373
+ }
374
+
375
+ /** Dal vocabolario di git (relativo alla RADICE del repo) a quello di questo servizio (relativo alla cartella della sessione). */
376
+ function versoLaSessione(prefisso, percorsoRepo) {
377
+ if (prefisso === '') return percorsoRepo;
378
+ if (!percorsoRepo.startsWith(prefisso)) return null; // fuori dalla cartella della sessione: non esiste, per chi ci parla
379
+ return percorsoRepo.slice(prefisso.length);
380
+ }
381
+
382
+ async function statoInterno(cartella) {
383
+ const dove = await collocazione(cartella);
384
+ /*
385
+ * ⛔⛔ ONESTÀ SU CHI FA COSA — scoperto SABOTANDO, non ragionando.
386
+ * Tolto `-- .`, i test restavano tutti verdi: la mia prima stesura
387
+ * diceva in un commento che era `-- .` a impedire la fuga di file del
388
+ * repository padre, e NON È VERO. Il containment lo fa il **filtro sul
389
+ * prefisso** in `versoLaSessione()`, che scarta ogni percorso che non
390
+ * comincia per `--show-prefix`. Quello è il lucchetto, ed è provato
391
+ * dal test della sottocartella.
392
+ * ⇒ `-- .` resta, ma per la ragione VERA: MISURATO, senza pathspec da
393
+ * una sottocartella git percorre e riporta l'INTERO repository padre,
394
+ * che poi noi buttiamo. Su un monorepo è lavoro (e pressione sui lock
395
+ * dell'indice condiviso) pagato a ogni aggiornamento della Review per
396
+ * niente, ed è anche il modo più facile di sbattere contro il tetto di
397
+ * uscita. È una cura di COSTO, non di sicurezza, e il test che la pinna
398
+ * guarda gli argomenti passati a git, non l'elenco che ne esce.
399
+ */
400
+ let esito = await git(cartella, ['status', '--porcelain=v1', '-z', '--', '.']);
401
+ let grezze = analizzaStatoPorcelain(esito.stdout);
402
+ /*
403
+ * ⛔⛔⛔ LA CARTELLA COLLASSATA. git riassume una cartella interamente
404
+ * non tracciata con la sola cartella (`?? sessione/`): se quella cartella
405
+ * è proprio la sessione, il percorso coincide con il prefisso e togliendo
406
+ * il prefisso resta la STRINGA VUOTA — una riga senza nome, che non si
407
+ * può né mostrare né mettere in stage. Succede tutte le volte che si apre
408
+ * una sessione su una cartella nuova dentro un repo che esiste già.
409
+ * ⇒ Solo in quel caso si richiede con `-uall`, che apre il riassunto.
410
+ * ⛔ MISURATO: `-uall` NON scavalca il confine di W1-13 — un repository
411
+ * annidato resta una cartella sola anche così, il suo contenuto non esce.
412
+ */
413
+ if (dove.prefisso !== '' && grezze.some((r) => r.percorsoRepo === dove.prefisso)) {
414
+ esito = await git(cartella, ['status', '--porcelain=v1', '-z', '-uall', '--', '.']);
415
+ grezze = analizzaStatoPorcelain(esito.stdout);
416
+ }
417
+ const voci = [];
418
+ for (const riga of grezze) {
419
+ const percorso = versoLaSessione(dove.prefisso, riga.percorsoRepo);
420
+ if (percorso === null) continue;
421
+ const da = riga.daRepo === null ? null : versoLaSessione(dove.prefisso, riga.daRepo);
422
+ const forma = descrivi(riga.x, riga.y);
423
+ const cartellaVoce = percorso.endsWith('/');
424
+ voci.push({
425
+ percorso,
426
+ percorsoRepo: riga.percorsoRepo,
427
+ /* ⛔ Ricerca 2: `da` è il nome VECCHIO, e nel flusso `-z` arriva DOPO quello nuovo. Qui è già nel verso giusto. */
428
+ da,
429
+ x: riga.x,
430
+ y: riga.y,
431
+ ...forma,
432
+ cartella: cartellaVoce,
433
+ /*
434
+ * ⭐ W1-13 — un repository dentro il workspace ha fiducia PROPRIA.
435
+ * git lo mostra come una cartella non tracciata e non ne elenca il
436
+ * contenuto (MISURATO, anche con `-uall`): qui lo si DICE, con lo
437
+ * stesso identico test di `workspace-context.repoAnnidati()`, così la
438
+ * Review non lo presenta come «una cartella di file da aggiungere».
439
+ */
440
+ repoAnnidato: cartellaVoce && forma.tipo === 'nonTracciato'
441
+ ? esisteFn(join(cartella, percorso, '.git'))
442
+ : false,
443
+ });
444
+ }
445
+ const riepilogo = {
446
+ totale: voci.length,
447
+ staged: voci.filter((v) => v.staged).length,
448
+ nonStaged: voci.filter((v) => v.nonStaged && v.tipo !== 'nonTracciato').length,
449
+ nonTracciati: voci.filter((v) => v.tipo === 'nonTracciato').length,
450
+ conflitti: voci.filter((v) => v.conflitto).length,
451
+ };
452
+ return { ...dove, voci, riepilogo };
453
+ }
454
+
455
+ /** `git branch --show-current`, mai `rev-parse --abbrev-ref HEAD` — ricerca 7, due bugie misurate. */
456
+ async function ramoInterno(cartella) {
457
+ const dove = await collocazione(cartella);
458
+ const esito = await git(cartella, ['branch', '--show-current']);
459
+ const ramo = esito.stdout.trim();
460
+ return {
461
+ ...dove,
462
+ ramo: ramo === '' ? null : ramo,
463
+ /* ⛔ `null` + `staccata:true` è la verità; la stringa «HEAD» che dà `rev-parse` sembrerebbe un ramo che si chiama così. */
464
+ staccata: ramo === '',
465
+ };
466
+ }
467
+
468
+ /** Ogni percorso passa dalla stessa serratura, e l'elenco non può essere vuoto o furbo. */
469
+ function normalizzaElenco(cartella, percorsi) {
470
+ if (!Array.isArray(percorsi) || percorsi.length === 0) {
471
+ throw new GitServiceError('Serve almeno un percorso esplicito', 'GIT_PATHS_REQUIRED');
472
+ }
473
+ if (percorsi.length > 1000) {
474
+ throw new GitServiceError('Troppi percorsi in una sola richiesta', 'GIT_PATHS_REQUIRED');
475
+ }
476
+ const puliti = percorsi.map((p) => normalizzaPercorso(cartella, p));
477
+ return [...new Set(puliti)];
478
+ }
479
+
480
+ return Object.freeze({
481
+ /** Lo stato «Non committato» della Review: una voce per file, nel vocabolario della sessione. */
482
+ async stato({ sessionId } = {}) {
483
+ try {
484
+ const cartella = cartellaDi(sessionId);
485
+ return await statoInterno(cartella);
486
+ } catch (errore) { return rifiuto(errore); }
487
+ },
488
+
489
+ /** Il ramo corrente. `ramo:null` + `staccata:true` quando HEAD è staccata. */
490
+ async ramo({ sessionId } = {}) {
491
+ try {
492
+ const cartella = cartellaDi(sessionId);
493
+ return await ramoInterno(cartella);
494
+ } catch (errore) { return rifiuto(errore); }
495
+ },
496
+
497
+ /**
498
+ * Mette in stage percorsi ESPLICITI.
499
+ * ⛔ Non esiste una forma «tutto»: niente `-A`, niente `.`, niente
500
+ * `--update`. L'indice è condiviso con l'owner e con le altre sessioni, e
501
+ * una scrittura larga da qui raccoglierebbe lavoro non nostro — è il
502
+ * difetto già pagato una volta («git add -A raccoglie lavoro NON MIO»).
503
+ */
504
+ async stage({ sessionId, percorsi } = {}) {
505
+ try {
506
+ const cartella = cartellaDi(sessionId);
507
+ const puliti = normalizzaElenco(cartella, percorsi);
508
+ await git(cartella, ['add', '--', ...puliti], { timeoutMs: timeoutScritturaMs });
509
+ return { ok: true, percorsi: puliti, stato: await statoInterno(cartella) };
510
+ } catch (errore) { return rifiuto(errore); }
511
+ },
512
+
513
+ /**
514
+ * Toglie dallo stage percorsi ESPLICITI.
515
+ * ⛔ `git reset -- <percorsi>` e non `git restore --staged`: MISURATO, su
516
+ * un repository senza nemmeno un commit `reset` funziona e `restore`
517
+ * fallisce (non c'è un HEAD da cui ripristinare). Il primo `git add` di un
518
+ * progetto nuovo è esattamente il momento in cui uno vuole poter tornare
519
+ * indietro.
520
+ */
521
+ async unstage({ sessionId, percorsi } = {}) {
522
+ try {
523
+ const cartella = cartellaDi(sessionId);
524
+ const puliti = normalizzaElenco(cartella, percorsi);
525
+ /* ⛔ `-q`: senza, `git reset` scrive su stdout l'elenco delle differenze residue ed esce comunque 0 — rumore, non un esito. */
526
+ await git(cartella, ['reset', '-q', '--', ...puliti], { timeoutMs: timeoutScritturaMs });
527
+ return { ok: true, percorsi: puliti, stato: await statoInterno(cartella) };
528
+ } catch (errore) { return rifiuto(errore); }
529
+ },
530
+
531
+ /**
532
+ * Commit di percorsi ESPLICITI, con il messaggio letto da un FILE.
533
+ *
534
+ * ⛔⛔ I percorsi espliciti sono la cura del difetto «due sessioni, stessa
535
+ * cartella, intrecciano i commit»: MISURATO alla ricerca 4, ciò che
536
+ * un'altra sessione ha messo in stage su un ALTRO percorso resta fuori dal
537
+ * commit e resta in stage. Un `git commit` senza percorsi fotograferebbe
538
+ * l'intero indice condiviso — per questo qui non è nemmeno rappresentabile.
539
+ *
540
+ * ⛔⛔ E il prezzo di quella scelta è dichiarato invece che subìto: con i
541
+ * percorsi git prende il contenuto dell'ALBERO DI LAVORO, non quello in
542
+ * stage. Se il file è cambiato dopo lo stage, quello che finisce nel
543
+ * commit **non è quello che la Review ha mostrato** ⇒ si rifiuta per nome
544
+ * (`GIT_WORKTREE_DIFFERS`), dicendo quali file rimettere in stage.
545
+ *
546
+ * ⛔ Il messaggio non passa MAI dalla riga di comando: va su un file
547
+ * temporaneo e si passa `-F`. Motivi misurati, non estetici: su Windows la
548
+ * riga di comando ha un tetto duro, e un messaggio lungo o multilinea è
549
+ * normale. `--cleanup=whitespace` è esplicito perché il default (`default`)
550
+ * cambia comportamento a seconda che git creda di dover aprire un editor —
551
+ * qui non lo apre mai, ma il comportamento non deve dipendere da una
552
+ * deduzione di git (git-commit(1), letto il 05/09/2026).
553
+ */
554
+ async commit({ sessionId, percorsi, messaggio } = {}) {
555
+ let cartellaMessaggio = null;
556
+ try {
557
+ const cartella = cartellaDi(sessionId);
558
+ const puliti = normalizzaElenco(cartella, percorsi);
559
+ if (typeof messaggio !== 'string' || messaggio.trim() === '') {
560
+ throw new GitServiceError('Il commit vuole un messaggio', 'GIT_MESSAGE_REQUIRED');
561
+ }
562
+ if (Buffer.byteLength(messaggio, 'utf8') > TETTO_MESSAGGIO_BYTE) {
563
+ throw new GitServiceError('Messaggio di commit troppo lungo', 'GIT_MESSAGE_REQUIRED');
564
+ }
565
+
566
+ const prima = await statoInterno(cartella);
567
+ const perPercorso = new Map(prima.voci.map((v) => [v.percorso, v]));
568
+ /* ⛔ Un percorso che git non ha niente da committare: lo diciamo NOI, invece di lasciare a git un «pathspec did not match any file(s)» che nessuno sa leggere. */
569
+ const senzaNiente = puliti.filter((p) => !perPercorso.has(p));
570
+ if (senzaNiente.length > 0) {
571
+ throw new GitServiceError(`Niente da committare per: ${senzaNiente.join(', ')}`, 'GIT_NOTHING_TO_COMMIT');
572
+ }
573
+ /* ⛔⛔ La guardia della ricerca 4: staged E modificato dopo ⇒ il commit prenderebbe la versione nuova, in silenzio. */
574
+ const divergenti = puliti.filter((p) => {
575
+ const v = perPercorso.get(p);
576
+ return v && v.staged && v.nonStaged;
577
+ });
578
+ if (divergenti.length > 0) {
579
+ throw new GitServiceError(
580
+ `Questi file sono cambiati dopo essere stati messi in stage, e il commit prenderebbe la versione nuova: ${divergenti.join(', ')}. Rimettili in stage e riprova.`,
581
+ 'GIT_WORKTREE_DIFFERS',
582
+ );
583
+ }
584
+ const conflitti = puliti.filter((p) => perPercorso.get(p)?.conflitto);
585
+ if (conflitti.length > 0) {
586
+ throw new GitServiceError(`Prima vanno risolti i conflitti su: ${conflitti.join(', ')}`, 'GIT_NOTHING_TO_COMMIT');
587
+ }
588
+
589
+ cartellaMessaggio = await mkdtemp(join(tmpdir(), 'talos-git-msg-'));
590
+ const fileMessaggio = join(cartellaMessaggio, 'messaggio.txt');
591
+ await writeFile(fileMessaggio, messaggio, 'utf8');
592
+ await git(
593
+ cartella,
594
+ ['commit', '--cleanup=whitespace', '-F', fileMessaggio, '--', ...puliti],
595
+ { timeoutMs: timeoutScritturaMs },
596
+ );
597
+ const dopo = await statoInterno(cartella);
598
+ const testa = await git(cartella, ['rev-parse', 'HEAD']);
599
+ return { ok: true, percorsi: puliti, commit: testa.stdout.trim(), stato: dopo };
600
+ } catch (errore) {
601
+ return rifiuto(errore);
602
+ } finally {
603
+ if (cartellaMessaggio) await rm(cartellaMessaggio, { recursive: true, force: true }).catch(() => {});
604
+ }
605
+ },
606
+ });
607
+ }
608
+
609
+ /**
610
+ * Ogni porta risponde nella stessa forma del resto del server
611
+ * (`{erroreAvvio, code}`, come `terminal-registry.mjs`), così `http-app.mjs`
612
+ * la traduce senza sapere niente di git.
613
+ * ⛔ Un errore che NON è nostro non diventa un codice inventato: resta
614
+ * `GIT_COMMAND_FAILED`, che http-app mappa a 500 — mai un 200 con un esito
615
+ * finto.
616
+ */
617
+ function rifiuto(errore) {
618
+ if (errore instanceof GitServiceError) return { erroreAvvio: errore.message, code: errore.code };
619
+ if (typeof errore?.code === 'string' && errore.code.startsWith('PROCESS_POLICY')) {
620
+ return { erroreAvvio: 'Comando git rifiutato dalla policy di processo', code: 'GIT_COMMAND_FAILED' };
621
+ }
622
+ if (typeof errore?.code === 'string' && (errore.code === 'CWD_NOT_ALLOWED' || errore.code === 'CWD_REQUIRED' || errore.code === 'CWD_NOT_FOUND' || errore.code === 'EXECUTABLE_NOT_ALLOWED')) {
623
+ return { erroreAvvio: 'Cartella di lavoro non autorizzata per git', code: 'GIT_COMMAND_FAILED' };
624
+ }
625
+ return { erroreAvvio: errore?.message || 'git non è riuscito', code: 'GIT_COMMAND_FAILED' };
626
+ }