talos-code 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +661 -0
  3. package/README.md +116 -3
  4. package/THIRD_PARTY_NOTICES.md +40 -0
  5. package/dist/archive/zip.js +73 -0
  6. package/dist/args.js +100 -0
  7. package/dist/automations/history-store.js +34 -0
  8. package/dist/automations/policy-store.js +43 -0
  9. package/dist/automations/runner.js +107 -0
  10. package/dist/automations/schedule.js +85 -0
  11. package/dist/automations/windows-task.js +53 -0
  12. package/dist/commands/advanced-cli.js +233 -0
  13. package/dist/commands/checkpoint-cli.js +45 -0
  14. package/dist/commands/config-cli.js +107 -0
  15. package/dist/commands/context.js +17 -0
  16. package/dist/commands/extensions-cli.js +207 -0
  17. package/dist/commands/project-cli.js +74 -0
  18. package/dist/commands/project-commands.js +143 -0
  19. package/dist/commands/provider-cli.js +244 -0
  20. package/dist/commands/services-cli.js +248 -0
  21. package/dist/commands/session-cli.js +171 -0
  22. package/dist/commands/system-cli.js +405 -0
  23. package/dist/config/commands.js +60 -0
  24. package/dist/config/load.js +382 -0
  25. package/dist/config/migrations.js +48 -0
  26. package/dist/config/types.js +11 -0
  27. package/dist/diagnostics/development-log.js +154 -0
  28. package/dist/diagnostics/doctor.js +104 -0
  29. package/dist/diagnostics/redact.js +80 -0
  30. package/dist/diagnostics/zip.js +52 -0
  31. package/dist/errors.js +156 -0
  32. package/dist/events/bridge.js +130 -0
  33. package/dist/extensions/installer.js +178 -0
  34. package/dist/extensions/package-schema.js +24 -0
  35. package/dist/headless/result.js +84 -0
  36. package/dist/headless/run.js +230 -0
  37. package/dist/i18n/en/approval.js +46 -0
  38. package/dist/i18n/en/common.js +9 -0
  39. package/dist/i18n/en/credentials.js +92 -0
  40. package/dist/i18n/en/errors.js +267 -0
  41. package/dist/i18n/en/firstrun.js +101 -0
  42. package/dist/i18n/en/providers.js +37 -0
  43. package/dist/i18n/en/screen.js +774 -0
  44. package/dist/i18n/en/tools.js +127 -0
  45. package/dist/i18n/en.js +14 -0
  46. package/dist/i18n/error-view.js +307 -0
  47. package/dist/i18n/index.js +19 -0
  48. package/dist/i18n/kernel-map.js +139 -0
  49. package/dist/io.js +45 -0
  50. package/dist/main.js +331 -0
  51. package/dist/paths.js +41 -0
  52. package/dist/protocol/v2/codec.js +9 -0
  53. package/dist/protocol/v2/events.js +555 -0
  54. package/dist/protocol/v2/index.js +4 -0
  55. package/dist/protocol/v2/replay.js +28 -0
  56. package/dist/protocol/v2/types.js +1 -0
  57. package/dist/provider/control-plane.js +135 -0
  58. package/dist/provider/environment-keys.js +275 -0
  59. package/dist/provider/health.js +110 -0
  60. package/dist/provider/missing-key.js +48 -0
  61. package/dist/provider/model-catalog.js +168 -0
  62. package/dist/provider/openrouter-login.js +172 -0
  63. package/dist/provider/provider-text.js +13 -0
  64. package/dist/provider/store.js +95 -0
  65. package/dist/provider/system-keyring.js +214 -0
  66. package/dist/runtime/active-run.js +63 -0
  67. package/dist/runtime/agent-tree.js +74 -0
  68. package/dist/runtime/attachments.js +84 -0
  69. package/dist/runtime/brokered-executor.js +395 -0
  70. package/dist/runtime/context-archive.js +69 -0
  71. package/dist/runtime/context-status.js +76 -0
  72. package/dist/runtime/create-runtime.js +111 -0
  73. package/dist/runtime/model-profile.js +361 -0
  74. package/dist/runtime/output-store.js +137 -0
  75. package/dist/runtime/provider-attempts.js +185 -0
  76. package/dist/runtime/replay-buffer.js +153 -0
  77. package/dist/runtime/repo.js +95 -0
  78. package/dist/runtime/session-facade.js +135 -0
  79. package/dist/runtime/session-summary.js +125 -0
  80. package/dist/runtime/supervisor.js +265 -0
  81. package/dist/runtime/talos-composition.js +1011 -0
  82. package/dist/runtime/types.js +5 -0
  83. package/dist/runtime/usage-snapshot.js +82 -0
  84. package/dist/security/auto-classifier.js +38 -0
  85. package/dist/security/credential-free-environment.js +54 -0
  86. package/dist/security/evaluate.js +243 -0
  87. package/dist/security/execution-backends.js +236 -0
  88. package/dist/security/execution-broker.js +102 -0
  89. package/dist/security/forge-scan.js +74 -0
  90. package/dist/security/from-approval.js +39 -0
  91. package/dist/security/mxc-execution-backend.js +281 -0
  92. package/dist/security/permission-engine.js +138 -0
  93. package/dist/security/permission-explanation.js +54 -0
  94. package/dist/security/persist.js +179 -0
  95. package/dist/security/plugin-guard.js +230 -0
  96. package/dist/security/process-tree-evidence-store.js +74 -0
  97. package/dist/security/process-tree-probe.js +554 -0
  98. package/dist/security/project-resource-inventory.js +186 -0
  99. package/dist/security/project-trust-gate.js +38 -0
  100. package/dist/security/project-trust.js +367 -0
  101. package/dist/security/rule-parser.js +201 -0
  102. package/dist/security/shell-segmentation.js +68 -0
  103. package/dist/security/trust-authority.js +324 -0
  104. package/dist/security/types.js +1 -0
  105. package/dist/security/workspace-identity.js +102 -0
  106. package/dist/services/automation-facade.js +59 -0
  107. package/dist/services/forge-facade.js +77 -0
  108. package/dist/services/hook-facade.js +240 -0
  109. package/dist/services/index.js +16 -0
  110. package/dist/services/library-facade.js +174 -0
  111. package/dist/services/mcp-facade.js +348 -0
  112. package/dist/services/memory-facade.js +126 -0
  113. package/dist/services/notes-facade.js +157 -0
  114. package/dist/services/plugin-facade.js +308 -0
  115. package/dist/services/research-facade.js +110 -0
  116. package/dist/services/task-board-facade.js +236 -0
  117. package/dist/services/workflow-catalog.js +246 -0
  118. package/dist/sessions/export-format.js +98 -0
  119. package/dist/sessions/metadata-store.js +160 -0
  120. package/dist/sessions/share.js +122 -0
  121. package/dist/sessions/transfer.js +16 -0
  122. package/dist/subcommands.js +62 -0
  123. package/dist/tui/agent-roster.js +36 -0
  124. package/dist/tui/agent-view.js +51 -0
  125. package/dist/tui/app.js +4098 -0
  126. package/dist/tui/approval.js +36 -0
  127. package/dist/tui/boot/boot-sequence.js +76 -0
  128. package/dist/tui/boot/cinematic.js +243 -0
  129. package/dist/tui/boot/desktop-logo.js +82 -0
  130. package/dist/tui/boot.js +3 -0
  131. package/dist/tui/busy-input.js +58 -0
  132. package/dist/tui/catalog-service.js +560 -0
  133. package/dist/tui/components/assistant-stream.js +1 -0
  134. package/dist/tui/components/command-menu.js +68 -0
  135. package/dist/tui/components/composer.js +219 -0
  136. package/dist/tui/components/diff.js +35 -0
  137. package/dist/tui/components/footer.js +48 -0
  138. package/dist/tui/components/header.js +6 -0
  139. package/dist/tui/components/markdown.js +305 -0
  140. package/dist/tui/components/status-indicator.js +38 -0
  141. package/dist/tui/components/terminal-shell.js +214 -0
  142. package/dist/tui/components/thinking-row.js +19 -0
  143. package/dist/tui/components/tool-row.js +97 -0
  144. package/dist/tui/components/transcript-virtualizer.js +165 -0
  145. package/dist/tui/components/transcript.js +43 -0
  146. package/dist/tui/descendant-approvals.js +104 -0
  147. package/dist/tui/diff-model.js +77 -0
  148. package/dist/tui/editor-history.js +21 -0
  149. package/dist/tui/editor.js +210 -0
  150. package/dist/tui/event-adapter.js +436 -0
  151. package/dist/tui/exit-output.js +58 -0
  152. package/dist/tui/external-editor.js +53 -0
  153. package/dist/tui/file-completion.js +89 -0
  154. package/dist/tui/focus-manager.js +5 -0
  155. package/dist/tui/highlight.js +30 -0
  156. package/dist/tui/input-router.js +18 -0
  157. package/dist/tui/interrupt.js +99 -0
  158. package/dist/tui/keybindings.js +194 -0
  159. package/dist/tui/keymap-resolver.js +91 -0
  160. package/dist/tui/launch-state.js +148 -0
  161. package/dist/tui/line-diff.js +57 -0
  162. package/dist/tui/live-activity.js +56 -0
  163. package/dist/tui/metrics.js +123 -0
  164. package/dist/tui/onboarding.js +36 -0
  165. package/dist/tui/overlays/agent-tree.js +43 -0
  166. package/dist/tui/overlays/approval-dialog.js +642 -0
  167. package/dist/tui/overlays/automation-center.js +51 -0
  168. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  169. package/dist/tui/overlays/context-inspector.js +36 -0
  170. package/dist/tui/overlays/effort-line.js +110 -0
  171. package/dist/tui/overlays/forge-center.js +46 -0
  172. package/dist/tui/overlays/help-dialog.js +16 -0
  173. package/dist/tui/overlays/history-picker.js +37 -0
  174. package/dist/tui/overlays/hook-center.js +70 -0
  175. package/dist/tui/overlays/library-center.js +55 -0
  176. package/dist/tui/overlays/mcp-center.js +60 -0
  177. package/dist/tui/overlays/memory-center.js +63 -0
  178. package/dist/tui/overlays/model-picker.js +77 -0
  179. package/dist/tui/overlays/notes-center.js +53 -0
  180. package/dist/tui/overlays/overlay-host.js +8 -0
  181. package/dist/tui/overlays/plugin-center.js +76 -0
  182. package/dist/tui/overlays/provider-picker.js +171 -0
  183. package/dist/tui/overlays/queue-editor.js +32 -0
  184. package/dist/tui/overlays/research-center.js +59 -0
  185. package/dist/tui/overlays/scroll-window.js +234 -0
  186. package/dist/tui/overlays/session-picker.js +115 -0
  187. package/dist/tui/overlays/tasks-center.js +83 -0
  188. package/dist/tui/overlays/theme-picker.js +21 -0
  189. package/dist/tui/overlays/transcript-search.js +58 -0
  190. package/dist/tui/overlays/trust-center.js +29 -0
  191. package/dist/tui/overlays/workflow-center.js +46 -0
  192. package/dist/tui/project-file-index.js +214 -0
  193. package/dist/tui/project-references.js +85 -0
  194. package/dist/tui/project-trust-prompt.js +289 -0
  195. package/dist/tui/prompt-history-store.js +116 -0
  196. package/dist/tui/queue-store.js +110 -0
  197. package/dist/tui/regions/budget.js +59 -0
  198. package/dist/tui/regions/views.js +96 -0
  199. package/dist/tui/render-coordinator.js +148 -0
  200. package/dist/tui/render-scheduler.js +4 -0
  201. package/dist/tui/run.js +69 -0
  202. package/dist/tui/selection-list.js +29 -0
  203. package/dist/tui/session-controller.js +791 -0
  204. package/dist/tui/session-export.js +54 -0
  205. package/dist/tui/setup-wizard.js +46 -0
  206. package/dist/tui/shell-input.js +35 -0
  207. package/dist/tui/shell-layout.js +48 -0
  208. package/dist/tui/shell-model.js +98 -0
  209. package/dist/tui/slash-commands.js +82 -0
  210. package/dist/tui/state.js +151 -0
  211. package/dist/tui/status-bar.js +125 -0
  212. package/dist/tui/status-view.js +40 -0
  213. package/dist/tui/terminal-capabilities.js +9 -0
  214. package/dist/tui/terminal-session.js +11 -0
  215. package/dist/tui/text-width.js +66 -0
  216. package/dist/tui/theme-catalog.js +27 -0
  217. package/dist/tui/theme-store.js +23 -0
  218. package/dist/tui/theme.js +23 -0
  219. package/dist/tui/tool-display.js +135 -0
  220. package/dist/tui/tool-facts.js +1 -0
  221. package/dist/tui/tools/bash-renderer.js +32 -0
  222. package/dist/tui/tools/change.js +61 -0
  223. package/dist/tui/tools/edit-renderer.js +17 -0
  224. package/dist/tui/tools/generic-renderer.js +13 -0
  225. package/dist/tui/tools/list-renderer.js +14 -0
  226. package/dist/tui/tools/read-renderer.js +13 -0
  227. package/dist/tui/tools/registry.js +20 -0
  228. package/dist/tui/tools/row-format.js +187 -0
  229. package/dist/tui/tools/search-renderer.js +38 -0
  230. package/dist/tui/tools/shared.js +97 -0
  231. package/dist/tui/tools/write-renderer.js +15 -0
  232. package/dist/tui/transcript-model.js +441 -0
  233. package/dist/tui/ui-preferences.js +79 -0
  234. package/dist/tui/usage-view.js +77 -0
  235. package/dist/tui/vim-mode.js +99 -0
  236. package/dist/update/check.js +15 -0
  237. package/dist/update/npm.js +51 -0
  238. package/dist/update/run.js +129 -0
  239. package/dist/version.js +2 -0
  240. package/dist/workspace/checkpoint-store.js +210 -0
  241. package/dist/workspace/checkpoint.js +413 -0
  242. package/dist/workspace/restore.js +232 -0
  243. package/package.json +64 -5
  244. package/vendor/context-engine/package.json +11 -0
  245. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  246. package/vendor/context-engine/src/contracts.mjs +48 -0
  247. package/vendor/context-engine/src/engine.mjs +445 -0
  248. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  249. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  250. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  251. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  252. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  253. package/vendor/context-engine/src/profiles.mjs +10 -0
  254. package/vendor/context-engine/src/retrieval.mjs +104 -0
  255. package/vendor/context-engine/src/summary.mjs +97 -0
  256. package/vendor/context-engine/src/usage.mjs +34 -0
  257. package/vendor/harness-ui/package.json +38 -0
  258. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  259. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  260. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  261. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  262. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  263. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  264. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  265. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  266. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  267. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  268. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  269. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  270. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  271. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  272. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  273. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  274. package/vendor/harness-ui/src/config.mjs +697 -0
  275. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  276. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  277. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  278. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  279. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  280. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  281. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  282. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  283. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  284. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  285. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  286. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  287. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  288. package/vendor/harness-ui/src/doctor.mjs +142 -0
  289. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  290. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  291. package/vendor/harness-ui/src/document-report.mjs +331 -0
  292. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  293. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  294. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  295. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  296. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  297. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  298. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  299. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  300. package/vendor/harness-ui/src/git-service.mjs +626 -0
  301. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  302. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  303. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  304. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  305. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  306. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  307. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  308. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  309. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  310. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  311. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  312. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  313. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  314. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  315. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  316. package/vendor/harness-ui/src/library-store.mjs +652 -0
  317. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  318. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  319. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  320. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  321. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  322. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  323. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  324. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  325. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  326. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  327. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  328. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  329. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  330. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  331. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  332. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  333. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  334. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  335. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  336. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  337. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  338. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  339. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  340. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  341. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  342. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  343. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  344. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  345. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  346. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  347. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  348. package/vendor/harness-ui/src/research/card.mjs +235 -0
  349. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  350. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  351. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  352. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  353. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  354. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  355. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  356. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  357. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  358. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  359. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  360. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  361. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  362. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  363. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  364. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  365. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  366. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  367. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  368. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  369. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  370. package/vendor/harness-ui/src/research/report.mjs +203 -0
  371. package/vendor/harness-ui/src/research/run.mjs +527 -0
  372. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  373. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  374. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  375. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  376. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  377. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  378. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  379. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  380. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  381. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  382. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  383. package/vendor/harness-ui/src/session-store.mjs +220 -0
  384. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  385. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  386. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  387. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  388. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  389. package/vendor/harness-ui/src/static-files.mjs +96 -0
  390. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  391. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  392. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  393. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  394. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  395. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  396. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  397. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  398. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  399. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  400. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  401. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  402. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  403. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  404. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  405. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  406. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  407. package/vendor/manifest.json +170 -0
@@ -0,0 +1,442 @@
1
+ import { open, realpath } from 'node:fs/promises';
2
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
3
+
4
+ /*
5
+ * ⛔⛔⛔ 30/8 — questo file portava anche `createPathPolicy()`, la fabbrica
6
+ * che risolveva le campagne JSONL di TALOS-BANCO per la Board — RIMOSSA
7
+ * insieme a `campaign-service.mjs`/`report-source.mjs`/`cost-reader.mjs`
8
+ * (piano `elegant-spinning-dongarra.md`, "Board — da campagne TALOS-BANCO
9
+ * a cruscotto sessioni"): TALOS-BANCO è uno strumento di misura esterno,
10
+ * concettualmente estraneo al prodotto — l'intero server non deve più
11
+ * sapere dov'è. Restano SOLO le due primitive di sicurezza generali,
12
+ * usate da `workspace-files.mjs`/`workspace-tree.mjs` per il containment
13
+ * dei percorsi del workspace — quelle non erano MAI state campagna-specifiche,
14
+ * solo ospitate nello stesso file.
15
+ */
16
+
17
+ import { existsSync as esisteSync, realpathSync as realpathSyncNativa } from 'node:fs';
18
+ import { homedir } from 'node:os';
19
+ import { basename as nomeBase, dirname as cartellaDi, relative as relativoA, resolve as risolvi, sep as separatore } from 'node:path';
20
+
21
+ /**
22
+ * La home della persona, letta una volta per chiamata e SEMPRE sovrascrivibile dal chiamante
23
+ * (`{ home }`): una prova che dipende dalla macchina su cui gira non è una prova.
24
+ * ⛔ Se il sistema non sa dirla, si torna stringa vuota invece di lanciare: `~` e `$HOME`
25
+ * restano allora testo qualunque — nessuna espansione, e nessuna eccezione da un modulo che
26
+ * sta dentro un cancello di sicurezza.
27
+ */
28
+ function homeDiSistema() {
29
+ try { return homedir() ?? ''; } catch { return ''; }
30
+ }
31
+
32
+ /**
33
+ * ⭐⭐⭐ 04/9 — W1-13, I FILE DI CONTROLLO DI TALOS (review 03/09). Sono i file che decidono COSA
34
+ * l'agente può fare: hook, MCP, plugin, registro di fiducia, runtime del
35
+ * provider, istruzioni (`CLAUDE.md`/`AGENTS.md`/`.claude/`), skill, memoria.
36
+ * Una scrittura del modello su uno di questi non è una modifica al
37
+ * progetto: è una modifica alle REGOLE — e va approvata a mano ANCHE in
38
+ * Full access, anche con un permesso per-attrezzo «sempre».
39
+ *
40
+ * `file`: per nome, a qualunque profondità (un `CLAUDE.md` annidato è
41
+ * letto comunque). `cartelleOvunque`: per segmento, a qualunque profondità
42
+ * (anche FUORI dal workspace: `~/.claude/` è controllo per chiunque).
43
+ * `cartelleAllaRadice`: solo come primo segmento sotto il workspace — un
44
+ * progetto che si chiama `skills` non deve diventare tutto intoccabile.
45
+ */
46
+ export const FILE_DI_CONTROLLO = Object.freeze({
47
+ file: Object.freeze(['.harness-ui-hooks.json', '.harness-ui-mcp.json', '.provider-runtime.json', 'CLAUDE.md', 'AGENTS.md']),
48
+ // ⛔ 14/09 (F01/F02 della review, verificato): `.mcp-trust` e `.plugin-trust` sono i registri dove vivono i consensi ai
49
+ // server MCP e ai plugin (session-registry.mjs), esattamente come `.hooks-trust`. Mancavano da questo elenco: senza,
50
+ // un attrezzo di scrittura del modello poteva scriverci dentro e AUTO-CONCEDERSI la fiducia. `ePercorsoDiControllo`
51
+ // risolve già il realpath, quindi la classificazione vale anche per un alias/symlink verso queste cartelle.
52
+ cartelleOvunque: Object.freeze(['.harness-ui-plugins', '.hooks-trust', '.mcp-trust', '.plugin-trust', '.claude', '.memory-store']),
53
+ cartelleAllaRadice: Object.freeze(['skills']),
54
+ });
55
+
56
+ /** Il percorso REALE anche di un file che non esiste ancora: si risale al primo antenato esistente, lo si risolve (symlink/junction), si riattacca il resto. */
57
+ function percorsoRealeAncheSeManca(assoluto, realpathFn) {
58
+ const resto = [];
59
+ let corrente = assoluto;
60
+ while (!esisteSync(corrente)) {
61
+ const padre = cartellaDi(corrente);
62
+ if (padre === corrente) return assoluto; // radice del volume inesistente: niente da risolvere
63
+ resto.unshift(nomeBase(corrente));
64
+ corrente = padre;
65
+ }
66
+ return risolvi(realpathFn(corrente), ...resto);
67
+ }
68
+
69
+ /**
70
+ * True se `percorso` (relativo al workspace o assoluto) tocca un file di
71
+ * controllo. ⛔ Confronto sul percorso REALE: `../`, symlink e junction
72
+ * che puntano a uno di quei file non lo aggirano. Mai un'eccezione: un
73
+ * percorso che non si riesce a risolvere torna `true` (fallisce chiuso —
74
+ * meglio una card di troppo che una regola riscritta in silenzio).
75
+ */
76
+ export function ePercorsoDiControllo(cartella, percorso, { realpathFn = realpathSyncNativa } = {}) {
77
+ if (typeof cartella !== 'string' || cartella.length === 0 || typeof percorso !== 'string' || percorso.length === 0) return false;
78
+ let radice;
79
+ let reale;
80
+ try {
81
+ radice = percorsoRealeAncheSeManca(risolvi(cartella), realpathFn);
82
+ reale = percorsoRealeAncheSeManca(risolvi(cartella, percorso), realpathFn);
83
+ } catch {
84
+ return true;
85
+ }
86
+ if (FILE_DI_CONTROLLO.file.includes(nomeBase(reale))) return true;
87
+ const relativo = relativoA(radice, reale);
88
+ const dentro = relativo !== '' && !relativo.startsWith('..') && !relativo.includes(`..${separatore}`);
89
+ const segmenti = (dentro ? relativo : reale).split(/[\\/]+/).filter(Boolean);
90
+ const cartelle = segmenti.slice(0, -1);
91
+ if (cartelle.some((s) => FILE_DI_CONTROLLO.cartelleOvunque.includes(s))) return true;
92
+ if (dentro && cartelle.length > 0 && FILE_DI_CONTROLLO.cartelleAllaRadice.includes(cartelle[0])) return true;
93
+ return false;
94
+ }
95
+
96
+ /**
97
+ * ⛔⛔⛔⛔ F15, 17/09/2026 — LA CLASSE DEI PERCORSI SEGRETI. Owner, P0-bis corsia D:
98
+ * «entrambi stretto». È l'INNESCO di una domanda, non un confine.
99
+ *
100
+ * ⛔ LA DISTINZIONE CHE TIENE IN PIEDI TUTTO IL FILE, ed è la stessa che
101
+ * `custom-task.mjs` (28/8) dichiara nel verso opposto: una denylist usata come
102
+ * CONFINE («questi percorsi non si toccano») è la strategia che la ricerca 2026
103
+ * dà per fallita — gli escape documentati stanno proprio in quella categoria
104
+ * (Docker, «AI Coding Agent Horror Stories», 18/05/2026; Pillar, «The Week of
105
+ * Sandbox Escapes», 20/07/2026). Qui la lista NON è il confine: è l'innesco di
106
+ * una DOMANDA alla persona. Chi la aggira (offuscando il percorso, leggendolo
107
+ * da uno script, montandolo altrove) ottiene il comportamento di OGGI, non un
108
+ * permesso in più: un innesco incompleto perde una domanda, un confine
109
+ * incompleto perde una chiave. ⇒ Aggiungere una voce qui non può rompere
110
+ * niente; toglierla non può aprire niente che oggi sia chiuso.
111
+ *
112
+ * ⭐ Ricerca PRIMA di scrivere (fonte + data), per la FORMA dell'elenco, non per copiarlo:
113
+ * - Claude Code, «Configure permissions» (code.claude.com/docs/en/permissions, letta il
114
+ * 17/09/2026): le regole sui file si scrivono con la sintassi **gitignore**, con quattro
115
+ * ancoraggi distinti — `//assoluto`, `~/home`, `/relativo-alla-sorgente`, `relativo`; un
116
+ * nome nudo (`Read(.env)`) equivale allo stesso nome preceduto da un doppio asterisco,
117
+ * cioè vale a QUALUNQUE profondità; su
118
+ * Windows i percorsi sono normalizzati in forma POSIX prima del confronto; e una regola
119
+ * `!` (`Read(*.env)` poi `Read(!sample.env)`) ritaglia le eccezioni.
120
+ * ⇒ da qui tre scelte di questo file: (a) il confronto si fa sui SEGMENTI, non sulla
121
+ * stringa; (b) i separatori Windows si normalizzano PRIMA; (c) esistono le ESENZIONI
122
+ * (`.env.example`, `id_rsa.pub`), perché una domanda inutile addestra a cliccare sì.
123
+ * ⛔ Dalla stessa pagina, il vincolo che questo file eredita: le regole sui percorsi
124
+ * valgono per i comandi di lettura che il nome del file lo DICONO (`cat`, `head`, `sed`,
125
+ * e i bersagli di `>`/`<`), non per chi legge un file senza nominarlo (`grep -r` dalla
126
+ * cartella che lo contiene, o uno script che lo apre da solo). Stesso limite qui, ed è
127
+ * dichiarato: questo è un innesco, non una sandbox.
128
+ *
129
+ * ⛔ I LIMITI NOTI, scritti qui perché nessuno li riscopra credendoli difetti (17/09/2026, dal
130
+ * controllo avversariale — dichiarati, non curati, perché curarli vorrebbe dire smettere di
131
+ * essere un innesco lessicale e diventare una sandbox):
132
+ * - **i link simbolici non si vedono.** L'innesco guarda il TESTO del comando, non il disco:
133
+ * un `ln -s ~/.ssh/id_rsa ./chiave` e poi `cat ./chiave` non chiede. `ePercorsoDiControllo`
134
+ * qui sopra risolve il realpath perché giudica UN percorso; qui i pezzi sono molti e
135
+ * spesso non esistono affatto (glob, variabili, argomenti) — toccare il disco per ognuno
136
+ * sarebbe un costo per ogni comando e comunque non chiuderebbe il buco lessicale.
137
+ * - **falsi positivi plausibili e accettati**: `ls /usr/share/keyrings` (una cartella di
138
+ * chiavi APT pubbliche, che chiede per via del segmento `keyrings`) e un file `.key` di
139
+ * Keynote. Entrambi producono UNA domanda in più, mai un blocco: il prezzo dichiarato del
140
+ * lato conservativo su due nomi che nella stragrande maggioranza dei casi sono segreti.
141
+ * - **lo stato di `cd` non è modellato**: dopo un `cd ~/.ssh`, un `cat config` in un comando
142
+ * SUCCESSIVO non chiede — il pezzo `config` da solo non nomina niente di dichiarato, e
143
+ * questo modulo non sa dove la shell si trova. (Nello STESSO comando, `cd ~/.ssh && cat
144
+ * config` chiede, perché `~/.ssh` è lì da leggere.)
145
+ * - Il registro, sulla cosa da proteggere: la ricerca sulle credenziali degli agenti 2026
146
+ * (GitGuardian, «State of Secrets Sprawl 2026» — 24.008 segreti unici nei file di
147
+ * configurazione MCP pubblici, ~150 segreti per portatile di sviluppatore, e ~40% dentro
148
+ * le cartelle degli strumenti AI; Amazon Q, credenziali AWS vive caricate da
149
+ * `.amazonq/mcp.json` senza consenso) dice che il posto dove i segreti stanno DAVVERO
150
+ * sono le cartelle prevedibili della home e i file di ambiente — che è esattamente
151
+ * questa lista, e non un elenco di comandi pericolosi.
152
+ *
153
+ * `cartelle`: un segmento con questo nome, a QUALUNQUE profondità, rende segreto tutto ciò
154
+ * che sta dentro (e la cartella stessa: `ls ~/.ssh` chiede). `fileInCartella`: solo quel nome
155
+ * dentro quella cartella — `~/.docker/` contiene anche `daemon.json`, che segreto non è.
156
+ * `nomiFile`/`prefissiNome`/`estensioni`: il nome finale, a qualunque profondità.
157
+ * `esenzioni`: ciò che per CONVENZIONE è pubblico e non deve mai generare una domanda.
158
+ */
159
+ export const PERCORSI_SEGRETI = Object.freeze({
160
+ cartelle: Object.freeze(['.ssh', '.aws', '.gnupg', '.password-store', 'keyrings']),
161
+ fileInCartella: Object.freeze({
162
+ '.docker': Object.freeze(['config.json']),
163
+ '.kube': Object.freeze(['config']),
164
+ }),
165
+ // ⛔ `.provider-runtime.json` è il file dove TALOS custodisce le chiavi dei fornitori
166
+ // (`provider-credential-store.mjs`, `runtimeFile`): sta già in `FILE_DI_CONTROLLO.file`
167
+ // qui sopra, ma quella lista guarda le SCRITTURE del modello — non `shell` né `leggi`.
168
+ // Le due liste dicono due cose diverse sullo stesso file, e vanno tenute entrambe.
169
+ nomiFile: Object.freeze(['.netrc', '_netrc', '.npmrc', '.pypirc', '.pgpass', '.git-credentials', '.provider-runtime.json']),
170
+ prefissiNome: Object.freeze(['id_rsa', 'id_ed25519', 'id_ecdsa', 'id_dsa']),
171
+ estensioni: Object.freeze(['.pem', '.key', '.p12', '.pfx', '.jks', '.keystore', '.ppk']),
172
+ esenzioni: Object.freeze(['.pub', '.example', '.sample', '.template', '.dist']),
173
+ /*
174
+ * ⛔ Le esenzioni che valgono ANCHE dentro una cartella dichiarata segreta, E anche contro il
175
+ * CONFINE (l'unico posto in tutto il file dove un'esenzione scavalca il secondo innesco). È
176
+ * una sola, e per una ragione precisa: `~/.ssh/id_rsa.pub` è una chiave PUBBLICA — si incolla
177
+ * in un pannello di deploy, si stampa a schermo, ed è una delle cose che si chiedono più
178
+ * spesso dentro `~/.ssh`. Chiedere lì insegna a rispondere sì senza leggere, e la volta che
179
+ * conta (`id_rsa`, senza `.pub`) la persona clicca sì per abitudine.
180
+ * ⛔ Solo per il FILE, mai per la cartella: `ls ~/.ssh` resta un segreto — elenca anche ciò
181
+ * che pubblico non è.
182
+ * ⛔ Le altre quattro NON valgono dentro una cartella segreta né contro il confine:
183
+ * `credenziali.sample` dentro `~/.aws` è un file che qualcuno ha chiamato così, non una
184
+ * convenzione pubblica.
185
+ * ⛔ 17/09: questa riga è nata INERTE sul proprio esempio — l'esenzione c'era per la classe e
186
+ * il confine chiedeva lo stesso. Un'esenzione che non esenta il caso che il suo commento
187
+ * cita non è un'esenzione: è una frase. Curata, e provata su entrambi gli inneschi.
188
+ */
189
+ esenzioniOvunque: Object.freeze(['.pub']),
190
+ // Il portachiavi del sistema, nei tre sistemi operativi: si nomina per percorso…
191
+ coppiePortachiavi: Object.freeze([['library', 'keychains'], ['microsoft', 'credentials'], ['microsoft', 'vault']]),
192
+ // …oppure per il comando che lo apre. ⛔ Deliberatamente CORTO e non ambiguo: `security` da
193
+ // solo è una parola comune, quindi entra solo con il suo sottocomando vero.
194
+ comandiPortachiavi: Object.freeze(['cmdkey', 'vaultcmd', 'secret-tool']),
195
+ frasiPortachiavi: Object.freeze([/\bsecurity\s+(?:find-generic-password|find-internet-password|dump-keychain)\b/i]),
196
+ });
197
+
198
+ /** Le tre scritture della home, più `~`: un innesco che non le conosce non innesca quasi mai. */
199
+ function conLaHomeEspansa(grezzo, home) {
200
+ let t = String(grezzo).trim().replace(/^["'`]+/, '').replace(/["'`]+$/, '');
201
+ if (t === '') return '';
202
+ if (typeof home === 'string' && home !== '') {
203
+ t = t.replace(/^~(?=$|[\\/])/, home);
204
+ t = t.replace(/%USERPROFILE%|%HOME%|\$\{HOME\}|\$env:USERPROFILE|\$HOME(?![A-Za-z0-9_])/gi, home);
205
+ }
206
+ return t.replace(/\\/g, '/');
207
+ }
208
+
209
+ /** I segmenti veri di un percorso: via i vuoti, via `.` e `..` (che non sono nomi). */
210
+ function segmentiDi(normalizzato) {
211
+ return normalizzato.split('/').filter((s) => s !== '' && s !== '.' && s !== '..');
212
+ }
213
+
214
+ function eEsente(base) {
215
+ return PERCORSI_SEGRETI.esenzioni.some((e) => base.endsWith(e));
216
+ }
217
+
218
+ /** Il NOME finale, da solo, dice che è un segreto? (`.env`, `.env.local`, `prod.env`, `id_rsa`, `*.pem`…) */
219
+ function eNomeSegreto(base) {
220
+ const b = base.toLowerCase();
221
+ if (b === '') return false;
222
+ if (eEsente(b)) return false;
223
+ if (b === '.env' || b.startsWith('.env.') || b.endsWith('.env')) return true;
224
+ if (PERCORSI_SEGRETI.nomiFile.includes(b)) return true;
225
+ if (PERCORSI_SEGRETI.prefissiNome.some((p) => b.startsWith(p))) return true;
226
+ if (PERCORSI_SEGRETI.estensioni.some((e) => b.endsWith(e))) return true;
227
+ return false;
228
+ }
229
+
230
+ /** Un solo pezzo di testo (un percorso, o un token di un comando): appartiene alla classe dichiarata? */
231
+ function classeDelPezzo(pezzo, home) {
232
+ const normalizzato = conLaHomeEspansa(pezzo, home);
233
+ // Un indirizzo web non è un percorso: `https://x/.well-known/...` non deve chiedere niente.
234
+ if (normalizzato === '' || normalizzato.includes('://')) return null;
235
+ const segmenti = segmentiDi(normalizzato);
236
+ if (segmenti.length === 0) return null;
237
+ const bassi = segmenti.map((s) => s.toLowerCase());
238
+ const pubblicoOvunque = PERCORSI_SEGRETI.esenzioniOvunque.some((e) => bassi[bassi.length - 1].endsWith(e));
239
+ if (!pubblicoOvunque && bassi.some((s) => PERCORSI_SEGRETI.cartelle.includes(s))) return 'segreto';
240
+ for (const [primo, secondo] of PERCORSI_SEGRETI.coppiePortachiavi) {
241
+ for (let i = 0; i + 1 < bassi.length; i += 1) if (bassi[i] === primo && bassi[i + 1] === secondo) return 'portachiavi';
242
+ }
243
+ const base = segmenti[segmenti.length - 1];
244
+ const cartelle = bassi.slice(0, -1);
245
+ for (const [cartella, nomi] of Object.entries(PERCORSI_SEGRETI.fileInCartella)) {
246
+ if (cartelle.includes(cartella) && nomi.includes(base.toLowerCase())) return 'segreto';
247
+ }
248
+ if (eNomeSegreto(base)) return 'segreto';
249
+ if (PERCORSI_SEGRETI.comandiPortachiavi.includes(base.toLowerCase())) return 'portachiavi';
250
+ return null;
251
+ }
252
+
253
+ /**
254
+ * I pezzi di un comando di shell che possono essere un percorso. ⛔ Non è un parser di shell e
255
+ * non pretende di esserlo: si spezza su spazi e metacaratteri, perché quello che serve è
256
+ * ACCORGERSI di un nome, non capire il comando. Un percorso fra virgolette CON spazi dentro si
257
+ * spezza — dichiarato: è un innesco, e ciò che gli sfugge resta il comportamento di oggi.
258
+ */
259
+ export function pezziDelComando(comando) {
260
+ return String(comando ?? '').split(/[\s;|&<>()`"'=,]+/u).filter(Boolean);
261
+ }
262
+
263
+ /**
264
+ * ⛔⛔⛔ CASO 1 — il testo NOMINA un percorso della classe dichiarata?
265
+ * Vale sia per un comando intero (`cat ~/.ssh/id_rsa`) sia per un percorso solo
266
+ * (`~/.aws/credentials`): un percorso è un comando di un pezzo solo.
267
+ *
268
+ * @returns {null | {classe: 'segreto'|'portachiavi', percorso: string}} `percorso` è il pezzo
269
+ * COME LA PERSONA LO VEDRÀ, non risolto: chi risponde deve riconoscere ciò che ha davanti.
270
+ */
271
+ export function nominaUnSegreto(testo, { home = homeDiSistema() } = {}) {
272
+ const intero = String(testo ?? '');
273
+ if (intero.trim() === '') return null;
274
+ for (const frase of PERCORSI_SEGRETI.frasiPortachiavi) {
275
+ if (frase.test(intero)) return { classe: 'portachiavi', percorso: intero.trim() };
276
+ }
277
+ for (const pezzo of pezziDelComando(intero)) {
278
+ const classe = classeDelPezzo(pezzo, home);
279
+ if (classe) return { classe, percorso: pezzo.replace(/^["'`]+/, '').replace(/["'`]+$/, '') };
280
+ }
281
+ return null;
282
+ }
283
+
284
+ /**
285
+ * ⛔⛔⛔ CASO 2 — IL CONFINE STRETTO: il percorso esce dal workspace E finisce dentro qualcosa
286
+ * di NASCOSTO (un segmento che comincia con `.`).
287
+ *
288
+ * ⛔ Sono DUE condizioni, e servono entrambe. Non «tutto ciò che esce dal workspace» (un
289
+ * `cat ../fratello/note.txt` è lavoro normale) e non «tutto ciò che sta sotto la home»
290
+ * (`echo $HOME` non tocca niente). Ogni domanda in più addestra a rispondere sì senza
291
+ * leggere — e una domanda a cui si risponde sì senza leggere non protegge da niente.
292
+ */
293
+ export function esceDalWorkspaceVersoUnNascosto(percorso, { cartella, home = homeDiSistema() } = {}) {
294
+ const normalizzato = conLaHomeEspansa(percorso, home);
295
+ if (normalizzato === '' || normalizzato.includes('://')) return null;
296
+ // Senza una radice non si può dire «fuori»: nessun innesco, mai un falso allarme.
297
+ if (typeof cartella !== 'string' || cartella === '') return null;
298
+ // Un pezzo che non porta un separatore non è un percorso: `$HOME` non espansa, `--flag`, `npm`.
299
+ if (!/[\\/]/u.test(String(percorso ?? '')) && !String(percorso ?? '').trim().startsWith('~')) return null;
300
+ let radice;
301
+ let assoluto;
302
+ try {
303
+ radice = risolvi(cartella);
304
+ assoluto = risolvi(radice, normalizzato);
305
+ } catch { return null; }
306
+ if (isPathInside(radice, assoluto)) return null;
307
+ const segmenti = segmentiDi(assoluto.replace(/\\/g, '/'));
308
+ /*
309
+ * ⛔⛔⛔ B3 (17/09/2026) — l'esenzione `.pub` vince ANCHE qui, non solo sulla classe.
310
+ * Prima no, e il risultato era una incoerenza: il commento di `esenzioniOvunque` portava come
311
+ * esempio `~/.ssh/id_rsa.pub`, e proprio lì l'esenzione era inerte perché il confine chiedeva
312
+ * lo stesso. Una chiave pubblica è pubblica ovunque stia; chiedere per lei insegna a cliccare
313
+ * sì, e la volta che conta (`id_rsa`, senza `.pub`) la persona clicca sì per abitudine.
314
+ * ⛔ Solo per il FILE, mai per la cartella: `ls ~/.ssh` elenca anche ciò che pubblico non è,
315
+ * e infatti resta un segreto per via della classe, che corre prima di questo controllo.
316
+ */
317
+ const ultimo = segmenti[segmenti.length - 1] ?? '';
318
+ if (PERCORSI_SEGRETI.esenzioniOvunque.some((e) => ultimo.toLowerCase().endsWith(e))) return null;
319
+ const nascosto = segmenti.find((s) => s.startsWith('.'));
320
+ if (!nascosto) return null;
321
+ return { classe: 'fuori-workspace-nascosto', percorso: String(percorso).trim(), nascosto };
322
+ }
323
+
324
+ /** La copia per la persona: lingua naturale, nessun nome tecnico, e DICE quale file. */
325
+ function frasePerLaPersona(segnalazione, tipo) {
326
+ const azione = tipo === 'leggi'
327
+ ? { soggetto: 'Questa lettura apre', coda: 'vuoi che la faccia?' }
328
+ : { soggetto: 'Il comando tocca', coda: 'vuoi che lo esegua?' };
329
+ if (segnalazione.classe === 'portachiavi') {
330
+ const apre = tipo === 'leggi' ? 'Questa lettura apre' : 'Il comando apre';
331
+ return `${apre} il portachiavi del sistema, dove sono custodite le password: ${azione.coda}`;
332
+ }
333
+ const cosa = segnalazione.classe === 'segreto'
334
+ ? 'un file che può contenere chiavi o password'
335
+ : 'una cartella nascosta fuori dalla cartella di lavoro';
336
+ return `${azione.soggetto} ${cosa} (${segnalazione.percorso}): ${azione.coda}`;
337
+ }
338
+
339
+ /**
340
+ * ⛔⛔⛔⛔ F15 — LA DOMANDA UNICA, quella che il kernel chiama: questo comando (o questa
341
+ * lettura) va sottoposto alla persona ANCHE se l'attrezzo è impostato a «sempre»?
342
+ *
343
+ * ⛔ È una funzione PURA e l'unica porta: il kernel non deve rifare nessuna di queste
344
+ * decisioni, e chiunque voglia provarle non ha bisogno di avviare una sessione.
345
+ * ⛔ Torna `null` quando non c'è niente da chiedere — e `null` significa «il comportamento di
346
+ * oggi, bit per bit»: questo modulo non nega mai niente e non consente mai niente, decide
347
+ * soltanto se vale la pena disturbare la persona.
348
+ *
349
+ * @returns {null | {classe: string, percorso: string, frase: string}}
350
+ */
351
+ export function motivoDaChiedere({ tipo, comando, percorso, cartella, home = homeDiSistema() } = {}) {
352
+ const testo = tipo === 'leggi' ? percorso : comando;
353
+ if (typeof testo !== 'string' || testo.trim() === '') return null;
354
+ const nominato = nominaUnSegreto(testo, { home });
355
+ if (nominato) return { ...nominato, frase: frasePerLaPersona(nominato, tipo) };
356
+ for (const pezzo of pezziDelComando(testo)) {
357
+ const fuori = esceDalWorkspaceVersoUnNascosto(pezzo, { cartella, home });
358
+ if (fuori) return { ...fuori, frase: frasePerLaPersona(fuori, tipo) };
359
+ }
360
+ return null;
361
+ }
362
+
363
+ export class PathPolicyError extends Error {
364
+ constructor(message, code = 'PATH_NOT_ALLOWED') {
365
+ super(message);
366
+ this.name = 'PathPolicyError';
367
+ this.code = code;
368
+ }
369
+ }
370
+
371
+ export function isPathInside(rootRealPath, candidateRealPath) {
372
+ const difference = relative(resolve(rootRealPath), resolve(candidateRealPath));
373
+ return difference === '' || (
374
+ difference !== '..'
375
+ && !difference.startsWith(`..${sep}`)
376
+ && !isAbsolute(difference)
377
+ );
378
+ }
379
+
380
+ function validateRoot(root) {
381
+ if (typeof root !== 'string' || root.trim() === '' || root.includes('\0') || !isAbsolute(root)) {
382
+ throw new PathPolicyError('Radice non valida', 'PATH_NOT_ALLOWED');
383
+ }
384
+ return resolve(root);
385
+ }
386
+
387
+ function validateCandidate(candidate) {
388
+ if (typeof candidate !== 'string' || candidate.trim() === '' || candidate.includes('\0') || isAbsolute(candidate)) {
389
+ throw new PathPolicyError('Percorso non valido', 'PATH_NOT_ALLOWED');
390
+ }
391
+ // Windows device paths, UNC e alternate data stream non sono percorsi di
392
+ // workspace: non devono poter cambiare semantica fra API diverse.
393
+ if (/^(?:\\\\\?\\|\\\\\.\\|\\\\)/u.test(candidate) || /(?:^|[\\/])[^\\/]+:[^\\/]*$/u.test(candidate)) {
394
+ throw new PathPolicyError('Percorso speciale non consentito', 'PATH_NOT_ALLOWED');
395
+ }
396
+ return candidate;
397
+ }
398
+
399
+ /**
400
+ * Risolve un percorso attraverso il filesystem e verifica il containment sul
401
+ * percorso reale, quindi anche junction/symlink che puntano fuori. Per i
402
+ * nuovi file si può usare `allowMissing`: viene risolto il genitore reale e
403
+ * il nome finale resta dentro la radice.
404
+ */
405
+ export async function resolveContainedRealPath(root, candidate, options = {}) {
406
+ const rootAbsolute = validateRoot(root);
407
+ const relativeCandidate = validateCandidate(candidate);
408
+ const realpathFn = options.realpathFn ?? realpath;
409
+ let rootReal;
410
+ try { rootReal = await realpathFn(rootAbsolute); } catch { throw new PathPolicyError('Radice non leggibile', 'PATH_ROOT_UNREADABLE'); }
411
+ const lexical = resolve(rootReal, relativeCandidate);
412
+ if (!isPathInside(rootReal, lexical)) throw new PathPolicyError('Percorso fuori dall’area autorizzata', 'PATH_NOT_ALLOWED');
413
+ let realCandidate;
414
+ try {
415
+ realCandidate = await realpathFn(lexical);
416
+ } catch (error) {
417
+ if (!options.allowMissing) throw new PathPolicyError('Percorso non trovato', 'PATH_NOT_FOUND');
418
+ const parent = dirname(lexical);
419
+ let parentReal;
420
+ try { parentReal = await realpathFn(parent); } catch { throw new PathPolicyError('Cartella genitore non trovata', 'PATH_NOT_FOUND'); }
421
+ realCandidate = join(parentReal, lexical.slice(parent.length + 1));
422
+ }
423
+ if (!isPathInside(rootReal, realCandidate)) throw new PathPolicyError('Percorso fuori dall’area autorizzata', 'PATH_NOT_ALLOWED');
424
+ return realCandidate;
425
+ }
426
+
427
+ /** Apre un handle soltanto dopo il controllo realpath; il chiamante è tenuto a chiuderlo. */
428
+ export async function openContainedFile(root, candidate, flags = 'r', options = {}) {
429
+ const realCandidate = await resolveContainedRealPath(root, candidate, options);
430
+ const openFn = options.openFn ?? open;
431
+ let handle;
432
+ try { handle = await openFn(realCandidate, flags); } catch { throw new PathPolicyError('File non leggibile', 'PATH_OPEN_FAILED'); }
433
+ try {
434
+ const info = await handle.stat();
435
+ if (!info.isFile()) throw new PathPolicyError('Il percorso non è un file', 'PATH_NOT_FILE');
436
+ return handle;
437
+ } catch (error) {
438
+ await handle.close().catch(() => {});
439
+ if (error instanceof PathPolicyError) throw error;
440
+ throw new PathPolicyError('File non leggibile', 'PATH_OPEN_FAILED');
441
+ }
442
+ }