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,245 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { createReadStream } from 'node:fs';
3
+ import { copyFile, mkdir, rename, rm, stat } from 'node:fs/promises';
4
+ import { isAbsolute, join, relative, resolve } from 'node:path';
5
+ import { createProcessPolicy } from './process-policy.mjs';
6
+
7
+ const REVISION = /^[a-f0-9]{40}$/iu;
8
+ const SHA256 = /^[a-f0-9]{64}$/iu;
9
+ const ID = /^[a-z0-9][a-z0-9._-]{0,127}$/iu;
10
+
11
+ export class HfModelTransferError extends Error {
12
+ constructor(message, code = 'HF_TRANSFER_FAILED') {
13
+ super(message);
14
+ this.name = 'HfModelTransferError';
15
+ this.code = code;
16
+ }
17
+ }
18
+
19
+ function fail(message, code = 'HF_TRANSFER_INVALID') {
20
+ throw new HfModelTransferError(message, code);
21
+ }
22
+
23
+ function validateRequest(value) {
24
+ if (!value || typeof value !== 'object' || Array.isArray(value)
25
+ || !ID.test(value.id) || typeof value.repo !== 'string' || value.repo.trim() === ''
26
+ || !REVISION.test(value.revision) || !Array.isArray(value.files) || value.files.length === 0
27
+ || !Number.isSafeInteger(value.bytes) || value.bytes <= 0
28
+ || !SHA256.test(value.sha256) || typeof value.license !== 'string' || value.license.trim() === ''
29
+ || typeof value.path !== 'string' || value.path.trim() === '' || isAbsolute(value.path)) {
30
+ fail('model transfer request is invalid');
31
+ }
32
+ const files = value.files.map((file) => {
33
+ if (!file || typeof file.path !== 'string' || file.path.trim() === '' || isAbsolute(file.path)
34
+ || !Number.isSafeInteger(file.bytes) || file.bytes <= 0 || !SHA256.test(file.sha256)) fail('model transfer file is invalid');
35
+ return { path: file.path, bytes: file.bytes, sha256: file.sha256.toLowerCase() };
36
+ });
37
+ if (files.reduce((sum, file) => sum + file.bytes, 0) !== value.bytes) fail('model transfer byte count is invalid');
38
+ return { ...value, files, sha256: value.sha256.toLowerCase(), state: 'incomplete' };
39
+ }
40
+
41
+ function assertInside(rootDir, value, label) {
42
+ const absolute = resolve(rootDir, value);
43
+ const difference = relative(resolve(rootDir), absolute);
44
+ if (difference === '..' || difference.startsWith(`..${'\\'}`) || isAbsolute(difference)) fail(`${label} escapes model root`);
45
+ return absolute;
46
+ }
47
+
48
+ async function digestFile(path) {
49
+ const hash = createHash('sha256');
50
+ let bytes = 0;
51
+ const stream = createReadStream(path);
52
+ for await (const chunk of stream) {
53
+ bytes += chunk.length;
54
+ hash.update(chunk);
55
+ }
56
+ return { bytes, sha256: hash.digest('hex') };
57
+ }
58
+
59
+ function progressFrom(text) {
60
+ const matches = [...String(text).matchAll(/(?:^|\s)(\d{1,3}(?:\.\d+)?)%/gu)];
61
+ if (matches.length === 0) return null;
62
+ const value = Number(matches.at(-1)[1]);
63
+ return Number.isFinite(value) ? Math.min(100, Math.max(0, value)) : null;
64
+ }
65
+
66
+ export function createHfModelTransfer({
67
+ hfExecutable = 'hf',
68
+ spawnImpl,
69
+ modelStore,
70
+ rootDir,
71
+ fsImpl = {},
72
+ now = () => new Date(),
73
+ allowedRepositories = [],
74
+ } = {}) {
75
+ if (typeof spawnImpl !== 'function' || !modelStore || typeof modelStore.inspect !== 'function'
76
+ || typeof modelStore.register !== 'function' || typeof modelStore.setState !== 'function'
77
+ || typeof rootDir !== 'string' || !isAbsolute(rootDir)) {
78
+ fail('transfer dependencies are invalid', 'HF_TRANSFER_MISCONFIGURED');
79
+ }
80
+ const fs = { copyFile, createReadStream, mkdir, rename, rm, stat, ...fsImpl };
81
+ const processPolicy = createProcessPolicy({
82
+ allowedExecutables: [hfExecutable],
83
+ capabilities: { models: rootDir },
84
+ spawnFn: spawnImpl,
85
+ });
86
+ const allowed = new Set(allowedRepositories);
87
+ const transfers = new Map();
88
+
89
+ function checkRepo(request) {
90
+ if (!allowed.has(request.repo)) fail(`repository ${request.repo} is not allowed`, 'HF_REPOSITORY_NOT_ALLOWED');
91
+ }
92
+
93
+ function argsFor(request, dryRun = false) {
94
+ const args = ['download', request.repo, '--revision', request.revision];
95
+ for (const file of request.files) args.push('--include', file.path);
96
+ args.push('--local-dir', assertInside(rootDir, request.path, 'local-dir'), '--quiet');
97
+ if (dryRun) args.push('--dry-run');
98
+ return args;
99
+ }
100
+
101
+ function spawn(command, args) {
102
+ try {
103
+ return processPolicy.spawn(command, args, { cwd: rootDir, shell: false, stdio: ['ignore', 'pipe', 'pipe'] });
104
+ } catch (error) {
105
+ if (error?.code === 'ENOENT') fail('Hugging Face CLI is not installed', 'HF_CLI_MISSING');
106
+ throw new HfModelTransferError(`cannot start Hugging Face CLI: ${error.message}`, 'HF_SPAWN_FAILED');
107
+ }
108
+ }
109
+
110
+ function observeProgress(record, chunk) {
111
+ const value = progressFrom(chunk);
112
+ if (value !== null) record.progress = Math.max(record.progress ?? 0, value);
113
+ }
114
+
115
+ function wireProcess(record, child) {
116
+ record.child = child;
117
+ const onData = (chunk) => observeProgress(record, chunk);
118
+ child.stdout?.on('data', onData);
119
+ child.stderr?.on('data', onData);
120
+ child.once('error', (error) => {
121
+ if (error?.code === 'ENOENT') {
122
+ record.state = 'gated';
123
+ record.reason = 'HF_CLI_MISSING';
124
+ } else if (!['paused', 'cancelled'].includes(record.state)) {
125
+ record.state = 'failed';
126
+ record.reason = 'HF_CLI_ERROR';
127
+ }
128
+ });
129
+ child.once('close', (code) => {
130
+ if (record.state === 'paused' || record.state === 'cancelled') return;
131
+ if (code !== 0) {
132
+ record.state = 'failed';
133
+ record.reason = 'HF_DOWNLOAD_FAILED';
134
+ return;
135
+ }
136
+ record.state = 'verifying';
137
+ verify(record.request).then(() => {
138
+ record.state = 'ready';
139
+ record.progress = 100;
140
+ }).catch((error) => {
141
+ record.state = 'failed';
142
+ record.reason = error.code || 'CHECKSUM_MISMATCH';
143
+ });
144
+ });
145
+ }
146
+
147
+ async function inspect(value) {
148
+ const request = validateRequest(value);
149
+ checkRepo(request);
150
+ const child = spawn(hfExecutable, argsFor(request, true));
151
+ return new Promise((resolveResult, reject) => {
152
+ let missing = false;
153
+ child.once('error', (error) => {
154
+ if (error?.code === 'ENOENT') {
155
+ missing = true;
156
+ resolveResult({ state: 'gated', reason: 'HF_CLI_MISSING' });
157
+ } else reject(new HfModelTransferError(error.message, 'HF_CLI_ERROR'));
158
+ });
159
+ child.once('close', (code) => {
160
+ if (missing) return;
161
+ if (code !== 0) reject(new HfModelTransferError('Hugging Face dry-run failed', 'HF_INSPECT_FAILED'));
162
+ else resolveResult({ state: 'available', repo: request.repo, revision: request.revision, files: request.files, cli: { executable: hfExecutable, version: null } });
163
+ });
164
+ });
165
+ }
166
+
167
+ async function start(value) {
168
+ const request = validateRequest(value);
169
+ checkRepo(request);
170
+ const previous = transfers.get(request.id);
171
+ if (previous?.state === 'running' || previous?.state === 'verifying') return status(request.id);
172
+ const existing = await modelStore.inspect(request.id);
173
+ if (!existing) await modelStore.register({ ...request, updatedAt: now().toISOString() });
174
+ else if (existing.repo !== request.repo || existing.revision !== request.revision) fail('existing transfer has a different revision', 'HF_TRANSFER_COLLISION');
175
+ const record = { id: request.id, request, state: 'running', progress: previous?.progress ?? 0, reason: null, startedAt: now().toISOString(), child: null };
176
+ transfers.set(request.id, record);
177
+ try { wireProcess(record, spawn(hfExecutable, argsFor(request))); } catch (error) {
178
+ if (error.code === 'HF_CLI_MISSING') { record.state = 'gated'; record.reason = error.code; }
179
+ else throw error;
180
+ }
181
+ return status(request.id);
182
+ }
183
+
184
+ async function pause(id) {
185
+ const record = transfers.get(id);
186
+ if (!record || !['running', 'verifying'].includes(record.state)) return false;
187
+ record.state = 'paused';
188
+ record.reason = 'PAUSED_BY_OWNER';
189
+ record.child?.kill?.('SIGTERM');
190
+ return true;
191
+ }
192
+
193
+ async function resume(id) {
194
+ const record = transfers.get(id);
195
+ if (!record || !['paused', 'cancelled', 'incomplete'].includes(record.state)) return false;
196
+ return start(record.request);
197
+ }
198
+
199
+ async function cancel(id) {
200
+ const record = transfers.get(id);
201
+ if (!record || ['ready', 'failed', 'gated'].includes(record.state)) return false;
202
+ record.state = 'cancelled';
203
+ record.reason = 'CANCELLED_BY_OWNER';
204
+ record.child?.kill?.('SIGTERM');
205
+ return true;
206
+ }
207
+
208
+ function status(id) {
209
+ const record = transfers.get(id);
210
+ if (!record) return null;
211
+ return Object.freeze({ id: record.id, state: record.state, progress: record.progress, reason: record.reason, startedAt: record.startedAt });
212
+ }
213
+
214
+ async function verify(id) {
215
+ const manifest = await modelStore.inspect(typeof id === 'string' ? id : id.id);
216
+ if (!manifest) fail('model manifest not found', 'MODEL_NOT_FOUND');
217
+ for (const file of manifest.files) {
218
+ const path = assertInside(rootDir, join(manifest.path, file.path), 'model file');
219
+ let actual;
220
+ try { actual = await digestFile(path); } catch (error) { fail(`cannot read ${file.path}: ${error.message}`, 'MODEL_FILE_UNREADABLE'); }
221
+ if (actual.bytes !== file.bytes || actual.sha256.toLowerCase() !== file.sha256.toLowerCase()) {
222
+ fail(`checksum mismatch for ${file.path}`, 'CHECKSUM_MISMATCH');
223
+ }
224
+ }
225
+ return modelStore.setState(manifest.id, 'ready');
226
+ }
227
+
228
+ async function importLocal(value, sourcePath) {
229
+ const request = validateRequest(value);
230
+ if (!isAbsolute(sourcePath)) fail('local source must be absolute');
231
+ if (request.files.length !== 1) fail('local import currently supports one GGUF file', 'LOCAL_IMPORT_UNSUPPORTED');
232
+ checkRepo(request);
233
+ const source = resolve(sourcePath);
234
+ const destination = assertInside(rootDir, join(request.path, request.files[0].path), 'model file');
235
+ const existing = await modelStore.inspect(request.id);
236
+ if (!existing) await modelStore.register({ ...request, updatedAt: now().toISOString() });
237
+ await fs.mkdir(resolve(destination, '..'), { recursive: true });
238
+ const temporary = `${destination}.partial-${process.pid}-${Date.now()}`;
239
+ await fs.copyFile(source, temporary);
240
+ await fs.rename(temporary, destination);
241
+ return verify(request.id);
242
+ }
243
+
244
+ return Object.freeze({ inspect, start, pause, resume, cancel, status, verify, importLocal });
245
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * hook-registry.mjs — FASE A del piano `elegant-spinning-dongarra.md`
3
+ * ("Harness Desktop al 100%"), owner 28/8. Ricerca fatta prima di
4
+ * scrivere: lo stato dell'arte offre due pattern diversi — hook
5
+ * "universali e attivi di default" su ogni tool, oppure un sistema di
6
+ * *trust* persistito, dove un hook nuovo o modificato non gira finché non
7
+ * è esplicitamente fidato (un flag esplicito esiste solo per bypassarlo).
8
+ * Questo file implementa entrambi i pattern, adattati.
9
+ *
10
+ * ⛔⛔ Due cartelle DIVERSE, per un motivo preciso:
11
+ * - `hooks.json` vive DENTRO il workspace del progetto (`cartella`
12
+ * della sessione) — un hook è dichiarato "per-progetto", come
13
+ * AGENTS.md o un file di config di build: chi clona il progetto
14
+ * vede quali hook esistono.
15
+ * - il registro di TRUST vive FUORI, accanto a `server.mjs` (stesso
16
+ * pattern già in uso per `.automations/`, verificato in
17
+ * `automation-store.mjs`/`server.mjs`: `cartella:
18
+ * fileURLToPath(new URL('.automations/', import.meta.url))`) — il
19
+ * trust è una decisione dell'OWNER su questa macchina, mai qualcosa
20
+ * che un progetto clonato può auto-concedersi scrivendo un file.
21
+ *
22
+ * ⛔ Correzione di una nota nel piano madre: quella sezione ipotizzava
23
+ * un trust "accanto a .sessions/" — verificato oggi che `.sessions/`
24
+ * non esiste ancora (le sessioni vivono solo in memoria, dichiarato
25
+ * in `automation-store.mjs`: "diverso da session-registry.mjs
26
+ * apposta"). Il trust usa lo stesso pattern REALE di `.automations/`.
27
+ */
28
+ import { createHash } from 'node:crypto';
29
+ import { promises as fsp } from 'node:fs';
30
+ import { join } from 'node:path';
31
+ import { createProcessPolicy, parseProcessCommand } from './process-policy.mjs';
32
+
33
+ export class HookRegistryError extends Error {
34
+ constructor(message, code = 'HOOK_INVALID') {
35
+ super(message);
36
+ this.name = 'HookRegistryError';
37
+ this.code = code;
38
+ }
39
+ }
40
+
41
+ const NOME_FILE_HOOKS = '.harness-ui-hooks.json';
42
+ /**
43
+ * ⭐ 29/8 — esportata (era locale a caricaHooks) perché FASE G
44
+ * (plugin-registry.mjs) valida gli hook dichiarati DENTRO un
45
+ * plugin.json con la STESSA regola — un import qui, non una seconda
46
+ * lista che potrebbe divergere in silenzio se un evento nuovo si
47
+ * aggiunge un domani.
48
+ */
49
+ export const EVENTI_VALIDI = new Set(['pre_tool_call', 'post_tool_call', 'session_start', 'session_end']);
50
+
51
+ /**
52
+ * Legge `<cartella>/.harness-ui-hooks.json`. Un progetto senza hook è
53
+ * uno stato valido — mai un errore, torna `{ hooks: [] }`. Uno schema
54
+ * malformato invece È un errore dichiarato (`HookRegistryError`, mai
55
+ * un hook fantasma silenziosamente ignorato): un file che l'owner ha
56
+ * scritto a mano e sbagliato deve saperlo, non vedere l'hook
57
+ * semplicemente non funzionare.
58
+ */
59
+ export async function caricaHooks({ cartella }, deps = {}) {
60
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
61
+ const percorso = join(cartella, NOME_FILE_HOOKS);
62
+ let testo;
63
+ try {
64
+ testo = await readFileFn(percorso, 'utf8');
65
+ } catch (errore) {
66
+ if (errore?.code === 'ENOENT') return { hooks: [] };
67
+ throw new HookRegistryError(`Impossibile leggere ${NOME_FILE_HOOKS}: ${errore.message}`, 'HOOK_READ_FAILED');
68
+ }
69
+ let dati;
70
+ try {
71
+ dati = JSON.parse(testo);
72
+ } catch {
73
+ throw new HookRegistryError(`${NOME_FILE_HOOKS} non è un JSON valido`, 'HOOK_MALFORMED');
74
+ }
75
+ if (!dati || !Array.isArray(dati.hooks)) {
76
+ throw new HookRegistryError(`${NOME_FILE_HOOKS} deve avere un campo "hooks" (array)`, 'HOOK_MALFORMED');
77
+ }
78
+ const hooks = dati.hooks.map((voce, indice) => {
79
+ if (typeof voce?.id !== 'string' || voce.id.length === 0) {
80
+ throw new HookRegistryError(`hooks[${indice}] manca di "id" (stringa non vuota)`, 'HOOK_MALFORMED');
81
+ }
82
+ if (!Array.isArray(voce.eventi) || voce.eventi.length === 0 || !voce.eventi.every((e) => EVENTI_VALIDI.has(e))) {
83
+ throw new HookRegistryError(`hooks[${indice}] ("${voce.id}") ha "eventi" non valido — atteso un array non vuoto fra ${[...EVENTI_VALIDI].join('/')}`, 'HOOK_MALFORMED');
84
+ }
85
+ if (typeof voce.comando !== 'string' || voce.comando.length === 0) {
86
+ throw new HookRegistryError(`hooks[${indice}] ("${voce.id}") manca di "comando" (stringa non vuota)`, 'HOOK_MALFORMED');
87
+ }
88
+ const hash = createHash('sha256').update(voce.comando).digest('hex');
89
+ return { id: voce.id, eventi: voce.eventi, comando: voce.comando, hash };
90
+ });
91
+ return { hooks };
92
+ }
93
+
94
+ function percorsoTrust(cartellaTrust, hookId) {
95
+ // ⛔ hookId arriva da un file scritto dall'owner (hooks.json), ma è
96
+ // comunque un input esterno: mai costruire un percorso da una
97
+ // stringa non validata come nome file — stesso principio già in uso
98
+ // in workspace-files.mjs per un "nome, non un percorso".
99
+ if (typeof hookId !== 'string' || hookId.length === 0 || /[\\/]|\.\./.test(hookId)) {
100
+ throw new HookRegistryError('hookId non valido — un nome, non un percorso', 'HOOK_ID_INVALID');
101
+ }
102
+ return join(cartellaTrust, `${hookId}.json`);
103
+ }
104
+
105
+ /**
106
+ * Un hook è fidato SOLO se il registro persistito porta lo STESSO hash
107
+ * del comando attuale — un hook il cui contenuto è cambiato (hash
108
+ * diverso) torna automaticamente "non fidato", senza bisogno di
109
+ * un'azione esplicita di sfiducia: il trust è legato al CONTENUTO, non
110
+ * al nome.
111
+ */
112
+ export async function verificaTrust({ cartellaTrust, hookId, hash }, deps = {}) {
113
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
114
+ let testo;
115
+ try {
116
+ testo = await readFileFn(percorsoTrust(cartellaTrust, hookId), 'utf8');
117
+ } catch {
118
+ return false; // mai fidato per default: assente = non fidato, non un errore
119
+ }
120
+ try {
121
+ const dati = JSON.parse(testo);
122
+ return dati?.hash === hash;
123
+ } catch {
124
+ return false;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Registra il trust — chiamata SOLO da un'azione owner esplicita (la
130
+ * rotta HTTP dedicata, mai automatica dal ciclo dell'agente).
131
+ */
132
+ export async function fidaHook({ cartellaTrust, hookId, hash }, deps = {}) {
133
+ const mkdirFn = deps.mkdirFn ?? fsp.mkdir;
134
+ const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
135
+ await mkdirFn(cartellaTrust, { recursive: true });
136
+ await writeFileFn(percorsoTrust(cartellaTrust, hookId), JSON.stringify({ hash, fidatoIl: new Date().toISOString() }), 'utf8');
137
+ return { fidato: true };
138
+ }
139
+
140
+ /**
141
+ * Esegue un hook come sottoprocesso shell — stesso pattern di
142
+ * `eseguiComandoSandboxato`/`eseguiProva` in talosHarness.mjs (spawn,
143
+ * cwd della sessione, windowsHide, timeout), qui duplicato
144
+ * deliberatamente invece di importato: `hook-registry.mjs` vive nel
145
+ * backend (`harness-ui/`), `talosHarness.mjs` nel kernel benchmarkato
146
+ * (`AVM-harness/`) — repository diversi, la doc di `talosHarness.mjs`
147
+ * vieta esplicitamente una copia del KERNEL stesso ("divergerebbe in
148
+ * silenzio"), ma questa è solo una utility di spawn, non il kernel.
149
+ *
150
+ * Contratto d'uscita, dichiarato non presunto: se stdout è un JSON
151
+ * valido `{consentito, motivo?}`, quello è l'esito. Altrimenti
152
+ * `exit 0` → consentito, qualunque altro codice → rifiutato (stesso
153
+ * principio "un contratto d'uscita chiaro" già usato per
154
+ * `rivelaInEsploraFile`/explorer.exe).
155
+ */
156
+ export async function eseguiHook({ hook, evento, cartella }, deps = {}) {
157
+ try {
158
+ const [executable, ...args] = parseProcessCommand(hook.comando);
159
+ const policy = createProcessPolicy({
160
+ allowedExecutables: deps.allowedExecutables ?? ['node', 'node.exe', 'echo', 'echo.exe'],
161
+ capabilities: { hook: cartella },
162
+ envAllowlist: ['PATH', 'Path', 'PATHEXT', 'SystemRoot', 'WINDIR', 'COMSPEC', 'TALOS_HOOK_EVENT'],
163
+ spawnFn: deps.spawnFn,
164
+ });
165
+ const result = await policy.runApprovedProcess({
166
+ executable,
167
+ args,
168
+ cwd: cartella,
169
+ capability: 'hook',
170
+ timeoutMs: 10_000,
171
+ envKeys: ['PATH', 'Path', 'PATHEXT', 'SystemRoot', 'WINDIR', 'COMSPEC', 'TALOS_HOOK_EVENT'],
172
+ env: { TALOS_HOOK_EVENT: JSON.stringify(evento) },
173
+ captureLimitBytes: 64 * 1024,
174
+ });
175
+ const testoFuori = result.stdout.trim();
176
+ try {
177
+ const dati = JSON.parse(testoFuori);
178
+ if (typeof dati?.consentito === 'boolean') {
179
+ return { consentito: dati.consentito, motivo: typeof dati.motivo === 'string' ? dati.motivo : undefined };
180
+ }
181
+ } catch { /* non è JSON: si ricade sul codice di uscita */ }
182
+ return { consentito: result.code === 0, motivo: result.code === 0 ? undefined : (result.stderr.trim() || testoFuori || `hook exited ${result.code}`) };
183
+ } catch (error) {
184
+ return { consentito: false, motivo: String(error?.message || error) };
185
+ }
186
+ }