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,201 @@
1
+ import { homedir } from 'node:os';
2
+ import path from 'node:path';
3
+ import { segmentShellCommand } from "./shell-segmentation.js";
4
+ const TOOLS = new Set(['Read', 'Write', 'Edit', 'Bash', 'WebFetch', 'Mcp', 'Hook', 'Plugin', 'TalosService']);
5
+ const RULE_SHAPE = /^([A-Za-z]+)\((.*)\)$/u;
6
+ // B1 slice 21. Why a stored rule cannot be read, in words a person can act on, or null when it can.
7
+ // It accepts exactly what parseRule accepts: the explanation is chosen only AFTER the shape has
8
+ // failed, so a rule that parses today (a trailing line break is trimmed) keeps parsing.
9
+ // `.` in the shape does not match a line break (LF, CR, U+2028, U+2029), which is how "allow always"
10
+ // on a command with a line break used to write a rule that stopped the next start.
11
+ const LINE_BREAK = /[\x0a\x0d\p{Zl}\p{Zp}]/u;
12
+ export function ruleProblem(raw) {
13
+ if (typeof raw !== 'string')
14
+ return 'is not text';
15
+ const m = RULE_SHAPE.exec(raw.trim());
16
+ if (!m)
17
+ return LINE_BREAK.test(raw.trim()) ? 'contains a line break, and a rule is read on one line' : 'is not of the form Tool(pattern)';
18
+ if (!TOOLS.has(m[1]))
19
+ return `names "${m[1]}", which is not one of the tools ${[...TOOLS].join(', ')}`;
20
+ return null;
21
+ }
22
+ export function parseRule(raw, effect) { const m = ruleProblem(raw) === null ? RULE_SHAPE.exec(raw.trim()) : null; if (!m)
23
+ throw new Error(`PERMISSION_RULE_INVALID:${raw}`); return { raw, effect, tool: m[1], pattern: m[2] }; }
24
+ export function tokenizeCommand(command) { if (typeof command !== 'string' || !command.trim() || command.includes('\0'))
25
+ return []; const out = []; let token = '', quote = null, escape = false; for (const c of command.trim()) {
26
+ if (escape) {
27
+ token += c;
28
+ escape = false;
29
+ continue;
30
+ }
31
+ if (c === '\\' && quote) {
32
+ escape = true;
33
+ continue;
34
+ }
35
+ if (c === '"' || c === "'") {
36
+ if (quote === c)
37
+ quote = null;
38
+ else if (!quote)
39
+ quote = c;
40
+ else
41
+ token += c;
42
+ continue;
43
+ }
44
+ if (!quote && /[&|;<>()]/u.test(c))
45
+ return [];
46
+ if (!quote && /\s/u.test(c)) {
47
+ if (token) {
48
+ out.push(token);
49
+ token = '';
50
+ }
51
+ continue;
52
+ }
53
+ token += c;
54
+ } if (quote)
55
+ return []; if (token)
56
+ out.push(token); return out; }
57
+ const READ_ONLY_COMMANDS = new Set(['ls', 'dir', 'cat', 'type', 'head', 'tail', 'grep', 'rg', 'wc', 'diff', 'stat', 'du', 'pwd']);
58
+ const READ_ONLY_GIT = new Set(['status', 'diff', 'log', 'show', 'grep', 'rev-parse', 'ls-files', 'ls-tree']);
59
+ export function isReadOnlyShellCommand(command) {
60
+ let commands;
61
+ try {
62
+ commands = segmentShellCommand(command).map(segment => segment.command);
63
+ }
64
+ catch {
65
+ return false;
66
+ }
67
+ return commands.every(singleCommandReadOnly);
68
+ }
69
+ // B1 slice 11. Windows resolves a command name without case, so `TYPE` and `type` are the same
70
+ // program. Recognizing only the lowercase spelling let the shift key defeat the whole boundary:
71
+ // measured on this host, `Bash(*)` plus `TYPE <private key>` was allowed and the shell really read
72
+ // the key. The verb is folded only where the platform folds it, and only to RECOGNIZE a read-only
73
+ // command. ⛔ Rule matching is deliberately NOT folded — a rule names the text a person wrote, and
74
+ // making that comparison case-blind would change what every existing rule means.
75
+ function readOnlyVerb(token) { return path.sep === '\\' ? token.toLowerCase() : token; }
76
+ function singleCommandReadOnly(command) {
77
+ const tokens = tokenizeCommand(command);
78
+ if (!tokens.length)
79
+ return false;
80
+ const [head, sub] = tokens;
81
+ const verb = readOnlyVerb(head);
82
+ if (READ_ONLY_COMMANDS.has(verb))
83
+ return true;
84
+ if (verb === 'git' && sub && READ_ONLY_GIT.has(sub))
85
+ return true;
86
+ return false;
87
+ }
88
+ // B1 slice 10. A token whose value cannot be decided by reading it. An unexpanded shell variable
89
+ // (`$HOME`, `${HOME}`), a command substitution, a Windows environment reference (`%USERPROFILE%`)
90
+ // and a `~user` form all resolve to something this process cannot compute; a drive-relative path
91
+ // (`C:foo`) means "the current directory of drive C:", which is not the project root even when
92
+ // path.resolve() folds it into one. Measured 2026-09-16 on this host: path.resolve(root,'$HOME/x'),
93
+ // path.resolve(root,'%USERPROFILE%\\x') and path.resolve(root,'C:foo') all land INSIDE the project,
94
+ // so treating them as ordinary relative paths is what makes the boundary bypassable.
95
+ // A trailing `$` cannot expand in any shell, so `grep 'end$'` stays verifiable and does not prompt.
96
+ const WINDOWS_PATHS = path.sep === '\\';
97
+ export function isUnverifiableArgument(token) {
98
+ if (/\$[\s\S]/u.test(token) || token.includes('`'))
99
+ return true;
100
+ if (WINDOWS_PATHS && /%[^%\s]*%/u.test(token))
101
+ return true;
102
+ if (WINDOWS_PATHS && /^[A-Za-z]:(?![\\/])/u.test(token))
103
+ return true;
104
+ return token.startsWith('~') && token !== '~' && !token.startsWith('~/') && !token.startsWith(`~${path.sep}`);
105
+ }
106
+ export function expandHomePrefix(token) {
107
+ if (token === '~')
108
+ return homedir();
109
+ if (token.startsWith('~/') || token.startsWith(`~${path.sep}`))
110
+ return path.join(homedir(), token.slice(2));
111
+ return token;
112
+ }
113
+ // On Windows a single-segment `/x` token is a switch, never a path: no Windows shell resolves
114
+ // `/s` as a file name. More than one segment (`/c/Users/x`, git-bash style) or anything with a
115
+ // separator in it is not switch-shaped and stays an operand, so POSIX is untouched.
116
+ export function isWindowsStyleSwitch(token) { return WINDOWS_PATHS && /^\/[A-Za-z?][-\w:,]*$/u.test(token); }
117
+ function tokenizeForBoundary(command) {
118
+ if (typeof command !== 'string' || !command.trim() || command.includes('\0'))
119
+ return null;
120
+ const chars = [...command.trim()];
121
+ const out = [];
122
+ let value = '', bare = '', started = false, quote = null;
123
+ const push = () => { if (started) {
124
+ out.push({ value, bare });
125
+ value = '';
126
+ bare = '';
127
+ started = false;
128
+ } };
129
+ for (let i = 0; i < chars.length; i += 1) {
130
+ const c = chars[i];
131
+ if (c === '\\' && !WINDOWS_PATHS && quote !== "'" && i + 1 < chars.length) {
132
+ const next = chars[i + 1];
133
+ value += next;
134
+ if (quote === null)
135
+ bare += next;
136
+ started = true;
137
+ i += 1;
138
+ continue;
139
+ }
140
+ if (c === '"' || c === "'") {
141
+ if (quote === c)
142
+ quote = null;
143
+ else if (quote === null)
144
+ quote = c;
145
+ else
146
+ value += c;
147
+ started = true;
148
+ continue;
149
+ }
150
+ if (quote === null && /[&|;<>()]/u.test(c))
151
+ return null;
152
+ if (quote === null && /\s/u.test(c)) {
153
+ push();
154
+ continue;
155
+ }
156
+ value += c;
157
+ if (quote === null)
158
+ bare += c;
159
+ started = true;
160
+ }
161
+ if (quote !== null)
162
+ return null;
163
+ push();
164
+ return out.length ? out : null;
165
+ }
166
+ // Brace expansion makes one token into several, and only its unquoted part expands. A bare
167
+ // quantifier such as `a{2}` has no comma and no range, so a regular expression does not prompt.
168
+ const BRACE_LIST = /\{[^{}]*,[^{}]*\}/u;
169
+ const BRACE_RANGE = /\{[^{}]*\.\.[^{}]*\}/u;
170
+ // The operands of one command segment: everything the verb is pointed at. Option flags are dropped,
171
+ // but their value is not, in either form a path reaches them: `--file=PATH` and the attached short
172
+ // form `-fPATH`. Nothing says where a short cluster ends, so every suffix is offered and the
173
+ // comparison decides; a suffix that is not a path resolves inside the project and costs nothing.
174
+ // Everything else is kept even when it is obviously not a path, for the same reason: a search
175
+ // pattern lands inside the project, so keeping it costs nothing and guessing would cost a hole.
176
+ export function commandOperands(command) {
177
+ const tokens = tokenizeForBoundary(command);
178
+ if (!tokens)
179
+ return null;
180
+ const operands = [];
181
+ for (const token of tokens.slice(1)) {
182
+ if (BRACE_LIST.test(token.bare) || BRACE_RANGE.test(token.bare))
183
+ return null;
184
+ const value = token.value;
185
+ if (value === '--')
186
+ continue;
187
+ if (value.startsWith('-')) {
188
+ const eq = value.indexOf('=');
189
+ if (eq >= 0) {
190
+ operands.push(value.slice(eq + 1));
191
+ continue;
192
+ }
193
+ if (!value.startsWith('--'))
194
+ for (let i = 2; i < value.length; i += 1)
195
+ operands.push(value.slice(i));
196
+ continue;
197
+ }
198
+ operands.push(value);
199
+ }
200
+ return operands;
201
+ }
@@ -0,0 +1,68 @@
1
+ function fail(code) {
2
+ throw Object.assign(new Error(code), { code });
3
+ }
4
+ export function segmentShellCommand(command) {
5
+ if (typeof command !== 'string' || !command.trim() || command.includes('\0'))
6
+ fail('SHELL_COMPOSITION_INVALID');
7
+ const segments = [];
8
+ let buffer = '';
9
+ let quote = null;
10
+ let operatorBefore = null;
11
+ const push = () => {
12
+ const value = buffer.trim();
13
+ if (!value)
14
+ fail('SHELL_COMPOSITION_INVALID');
15
+ segments.push({ command: value, operatorBefore });
16
+ buffer = '';
17
+ };
18
+ for (let index = 0; index < command.length; index += 1) {
19
+ const current = command[index];
20
+ const next = command[index + 1];
21
+ if (current === '\\' && next !== undefined && (quote !== null || /[&|;<>()`'"\\$]/u.test(next))) {
22
+ buffer += current + next;
23
+ index += 1;
24
+ continue;
25
+ }
26
+ if (current === '^' && quote === null && next !== undefined) {
27
+ buffer += current + next;
28
+ index += 1;
29
+ continue;
30
+ }
31
+ if (current === '"' || current === "'") {
32
+ if (quote === current)
33
+ quote = null;
34
+ else if (quote === null)
35
+ quote = current;
36
+ buffer += current;
37
+ continue;
38
+ }
39
+ if (quote !== "'" && (current === '`' || (current === '$' && next === '(')))
40
+ fail('SHELL_COMPOSITION_UNSUPPORTED');
41
+ if (quote === null) {
42
+ if (/[<>()]/u.test(current))
43
+ fail('SHELL_COMPOSITION_UNSUPPORTED');
44
+ let operator = null;
45
+ if ((current === '&' || current === '|') && next === current) {
46
+ operator = `${current}${next}`;
47
+ index += 1;
48
+ }
49
+ else if (current === '&' || current === '|' || current === ';')
50
+ operator = current;
51
+ else if (current === '\r' || current === '\n') {
52
+ operator = 'newline';
53
+ if (current === '\r' && next === '\n')
54
+ index += 1;
55
+ }
56
+ if (operator) {
57
+ push();
58
+ operatorBefore = operator;
59
+ continue;
60
+ }
61
+ }
62
+ buffer += current;
63
+ }
64
+ if (quote !== null)
65
+ fail('SHELL_COMPOSITION_INVALID');
66
+ push();
67
+ return segments;
68
+ }
@@ -0,0 +1,324 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdir, readFile, realpath, rename, rm, writeFile } from 'node:fs/promises';
3
+ import { homedir } from 'node:os';
4
+ import path from 'node:path';
5
+ import { normalizeProjectRoot } from "../paths.js";
6
+ import { createProjectTrustService, projectResourceKey } from "./project-trust.js";
7
+ import { inventoryProjectResources } from "./project-resource-inventory.js";
8
+ import { findNestedRepositories, resolveWorkspaceIdentity } from "./workspace-identity.js";
9
+ function fail(code, message = code, extra = {}) { throw Object.assign(new Error(message), { code, ...extra }); }
10
+ function resourceSignature(resources) { return JSON.stringify([...resources].map(row => ({ key: projectResourceKey(row), path: row.relativePath, fingerprint: row.fingerprint })).sort((a, b) => a.key.localeCompare(b.key))); }
11
+ function nestedSignature(entries) { return JSON.stringify([...entries].map(row => ({ relativePath: row.relativePath, resources: [...row.resources].sort() })).sort((a, b) => a.relativePath.localeCompare(b.relativePath))); }
12
+ function expectationOf(value) {
13
+ if ('workspaceIdentityHash' in value)
14
+ return value;
15
+ return { workspaceIdentityHash: value.workspace.identityHash, resources: value.resources.flatMap(row => row.current ? [row.current] : []), nestedRepositories: value.nestedRepositories };
16
+ }
17
+ function nestedConsentFailure(snapshot) {
18
+ const crossing = snapshot.nestedRepositories.reduce((total, row) => total + row.resources.length, 0);
19
+ const detail = snapshot.nestedRepositories.map(row => `${row.relativePath} (${row.resources.slice(0, 3).join(', ')}${row.resources.length > 3 ? `, and ${row.resources.length - 3} more` : ''})`).join('; ');
20
+ fail('PROJECT_TRUST_NESTED_REPOSITORY_CONSENT_REQUIRED', `PROJECT_TRUST_NESTED_REPOSITORY_CONSENT_REQUIRED: ${crossing} project resources cross nested repository boundaries: ${detail}. Re-run with --include-nested to trust them deliberately.`);
21
+ }
22
+ function scopeRows(snapshot, scope) {
23
+ if (scope.kind === 'plugin') {
24
+ const prefix = `.harness-ui-plugins/${scope.id}/`;
25
+ return snapshot.resources.filter(row => row.kind === 'plugin' && row.relativePath.startsWith(prefix));
26
+ }
27
+ return snapshot.resources.filter(row => row.kind === scope.kind && row.id === scope.id);
28
+ }
29
+ export function compatibilityTrustRoots(trustRoot, workspace) {
30
+ const base = path.join(trustRoot, 'compat', workspace.identityHash);
31
+ return { hooks: path.join(base, 'hooks'), mcp: path.join(base, 'mcp'), plugins: path.join(base, 'plugins') };
32
+ }
33
+ function quarantinePath(trustRoot, workspace, scope) {
34
+ const digest = createHash('sha256').update(scope.kind + '\0' + scope.id).digest('hex');
35
+ return path.join(trustRoot, 'quarantine', workspace.identityHash, digest + '.json');
36
+ }
37
+ function scopeReviewedFingerprint(rows) {
38
+ const fingerprints = rows.flatMap(row => row.currentFingerprint ? [row.currentFingerprint] : []).sort();
39
+ if (!fingerprints.length)
40
+ return null;
41
+ return fingerprints.length === 1 ? fingerprints[0] : createHash('sha256').update(fingerprints.join('\n')).digest('hex');
42
+ }
43
+ function validateQuarantineRecord(value, workspace, scope) {
44
+ if (!value || value.schema !== 'talos.cli.trust-quarantine.v1' || value.active !== true
45
+ || value.workspaceIdentityHash !== workspace.identityHash || value.kind !== scope.kind || value.id !== scope.id
46
+ || typeof value.quarantinedAt !== 'string' || (value.reason !== null && typeof value.reason !== 'string')
47
+ || (value.reviewedFingerprint !== null && typeof value.reviewedFingerprint !== 'string')) {
48
+ fail('TRUST_QUARANTINE_STORE_INVALID');
49
+ }
50
+ return value;
51
+ }
52
+ async function readQuarantineRecord(trustRoot, workspace, scope) {
53
+ try {
54
+ const text = await readFile(quarantinePath(trustRoot, workspace, scope), 'utf8');
55
+ return validateQuarantineRecord(JSON.parse(text), workspace, scope);
56
+ }
57
+ catch (error) {
58
+ if (error.code === 'ENOENT')
59
+ return null;
60
+ if (error instanceof SyntaxError)
61
+ fail('TRUST_QUARANTINE_STORE_INVALID');
62
+ throw error;
63
+ }
64
+ }
65
+ async function writeQuarantineRecord(trustRoot, workspace, scope, record) {
66
+ const target = quarantinePath(trustRoot, workspace, scope);
67
+ await mkdir(path.dirname(target), { recursive: true, mode: 0o700 });
68
+ const temporary = target + '.tmp-' + process.pid + '-' + randomUUID();
69
+ try {
70
+ await writeFile(temporary, JSON.stringify(record, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
71
+ await rename(temporary, target);
72
+ }
73
+ finally {
74
+ await rm(temporary, { force: true }).catch(() => { });
75
+ }
76
+ }
77
+ /* Folder decisions (R4). One small file per folder under `<trustRoot>/folders/`, named by a hash of the normalized
78
+ canonical path (the same normalization as the workspace identity), written through a temporary file and a rename. A
79
+ record that cannot be read or does not name its own folder decides nothing: the folder is asked again (fail closed). */
80
+ const FOLDER_SCHEMA = 'talos.cli.folder-trust.v1';
81
+ function folderKey(folder) { return normalizeProjectRoot(path.resolve(folder)); }
82
+ function folderRecordPath(trustRoot, folder) {
83
+ const digest = createHash('sha256').update('talos-folder-trust-v1\0' + folderKey(folder)).digest('hex');
84
+ return path.join(trustRoot, 'folders', digest + '.json');
85
+ }
86
+ async function hasFolderRecord(trustRoot, folder) {
87
+ try {
88
+ const value = JSON.parse(await readFile(folderRecordPath(trustRoot, folder), 'utf8'));
89
+ return value?.schema === FOLDER_SCHEMA && typeof value.canonicalRoot === 'string' && folderKey(value.canonicalRoot) === folderKey(folder) && typeof value.decidedAt === 'string';
90
+ }
91
+ catch {
92
+ return false;
93
+ }
94
+ }
95
+ async function writeFolderRecord(trustRoot, folder, decidedAt) {
96
+ const target = folderRecordPath(trustRoot, folder);
97
+ await mkdir(path.dirname(target), { recursive: true, mode: 0o700 });
98
+ const temporary = target + '.tmp-' + process.pid + '-' + randomUUID();
99
+ try {
100
+ await writeFile(temporary, JSON.stringify({ schema: FOLDER_SCHEMA, canonicalRoot: folder, decidedAt }, null, 2) + '\n', { encoding: 'utf8', mode: 0o600 });
101
+ await rename(temporary, target);
102
+ }
103
+ finally {
104
+ await rm(temporary, { force: true }).catch(() => { });
105
+ }
106
+ }
107
+ async function canonicalHome(homeDir) { const raw = homeDir ?? homedir(); try {
108
+ return await realpath(raw);
109
+ }
110
+ catch {
111
+ return path.resolve(raw);
112
+ } }
113
+ /** The home folder and a drive root count for the session only (owner Q-R4-1; Claude Code treats the home directory so). */
114
+ export function isSessionOnlyFolder(folder, { home = homedir() } = {}) {
115
+ const resolved = path.resolve(folder);
116
+ const key = folderKey(resolved);
117
+ return key === folderKey(path.parse(resolved).root) || key === folderKey(home);
118
+ }
119
+ export function createTrustAuthority({ projectRoot, trustRoot, clock, homeDir }) {
120
+ const service = createProjectTrustService({ trustRoot, ...(clock ? { clock } : {}) });
121
+ const now = clock ?? (() => new Date().toISOString());
122
+ async function folderDecision(snapshot) {
123
+ const fresh = snapshot ?? await current();
124
+ const folder = fresh.workspace.canonicalRoot;
125
+ const home = await canonicalHome(homeDir);
126
+ if (isSessionOnlyFolder(folder, { home }))
127
+ return { decided: false, source: null, path: null, sessionOnly: true, parent: null };
128
+ const above = path.dirname(folder);
129
+ const parent = above !== folder && !isSessionOnlyFolder(above, { home }) ? above : null;
130
+ if (await hasFolderRecord(trustRoot, folder))
131
+ return { decided: true, source: 'folder', path: folder, sessionOnly: false, parent };
132
+ if (fresh.storeSchema !== null)
133
+ return { decided: true, source: 'project', path: folder, sessionOnly: false, parent };
134
+ for (let cursor = above;;) {
135
+ if (!isSessionOnlyFolder(cursor, { home }) && await hasFolderRecord(trustRoot, cursor))
136
+ return { decided: true, source: 'parent', path: cursor, sessionOnly: false, parent };
137
+ const next = path.dirname(cursor);
138
+ if (next === cursor)
139
+ break;
140
+ cursor = next;
141
+ }
142
+ return { decided: false, source: null, path: null, sessionOnly: false, parent };
143
+ }
144
+ /* Which folder a grant saves, decided before anything is written. Omitted: this folder, or nothing for a session-only one. */
145
+ async function folderTarget(fresh, want) {
146
+ const folder = fresh.workspace.canonicalRoot;
147
+ const home = await canonicalHome(homeDir);
148
+ const sessionOnly = isSessionOnlyFolder(folder, { home });
149
+ const target = want ?? (sessionOnly ? 'none' : 'this');
150
+ if (target === 'none')
151
+ return null;
152
+ if (target === 'this') {
153
+ if (sessionOnly)
154
+ fail('PROJECT_TRUST_REQUIRED', 'The home folder and a drive root are trusted for one session only; TALOS never saves that decision.');
155
+ return folder;
156
+ }
157
+ const above = path.dirname(folder);
158
+ if (sessionOnly || above === folder || isSessionOnlyFolder(above, { home }))
159
+ fail('PROJECT_TRUST_REQUIRED', 'The parent folder is the home folder or a drive root, and TALOS never saves trust for those.');
160
+ return above;
161
+ }
162
+ async function current() {
163
+ const workspace = await resolveWorkspaceIdentity({ projectRoot });
164
+ const resources = await inventoryProjectResources(workspace);
165
+ const nestedRepositories = await findNestedRepositories(workspace, [...new Set(resources.map(resource => resource.relativePath))]);
166
+ const inspection = await service.inspectSnapshot(workspace, resources, nestedRepositories.map(row => row.relativePath));
167
+ const noResources = resources.length === 0 && inspection.storeSchema === null;
168
+ const rollbackAvailable = await service.rollbackAvailable(workspace);
169
+ return {
170
+ trusted: noResources ? true : inspection.state.trusted,
171
+ reason: noResources ? 'NO_PROJECT_RESOURCES' : inspection.state.reason,
172
+ storeSchema: inspection.storeSchema,
173
+ migrationRequired: inspection.migrationRequired,
174
+ rollbackAvailable,
175
+ workspace, resources: inspection.resources, nestedRepositories
176
+ };
177
+ }
178
+ async function exactExpected(expected, fresh) {
179
+ const want = expectationOf(expected);
180
+ const currentResources = fresh.resources.flatMap(row => row.current ? [row.current] : []);
181
+ if (want.workspaceIdentityHash !== fresh.workspace.identityHash || resourceSignature(want.resources) !== resourceSignature(currentResources)) {
182
+ fail('PROJECT_TRUST_INVALIDATED', 'PROJECT_TRUST_INVALIDATED: project resources changed while trust was being reviewed.', { reason: 'TRUST_REVIEW_CHANGED' });
183
+ }
184
+ if (nestedSignature(want.nestedRepositories) !== nestedSignature(fresh.nestedRepositories)) {
185
+ fail('PROJECT_TRUST_INVALIDATED', 'PROJECT_TRUST_INVALIDATED: nested repository boundaries changed while trust was being reviewed.', { reason: 'NESTED_REPOSITORY_BOUNDARY_CHANGED' });
186
+ }
187
+ }
188
+ return {
189
+ inspect: current,
190
+ folderDecision,
191
+ async trustProject(options = {}) {
192
+ const fresh = await current();
193
+ const expected = options.expected ? expectationOf(options.expected) : null;
194
+ const home = await canonicalHome(homeDir);
195
+ /* Q-R4-17: a session-only folder never gets anything on disk, whoever asks; the flag makes the question's answer explicit. */
196
+ const session = options.session === true || isSessionOnlyFolder(fresh.workspace.canonicalRoot, { home });
197
+ const target = session && options.folder === undefined ? null : await folderTarget(fresh, options.folder);
198
+ if (expected && expected.workspaceIdentityHash !== fresh.workspace.identityHash)
199
+ fail('PROJECT_TRUST_INVALIDATED', 'PROJECT_TRUST_INVALIDATED: workspace identity changed while trust was being reviewed.', { reason: 'RESOURCE_IDENTITY_MISMATCH' });
200
+ const reviewedNested = expected?.nestedRepositories ?? fresh.nestedRepositories;
201
+ if (reviewedNested.length && !options.includeNested)
202
+ nestedConsentFailure({ ...fresh, nestedRepositories: [...reviewedNested] });
203
+ const reviewedResources = expected?.resources ?? fresh.resources.flatMap(row => row.current ? [row.current] : []);
204
+ if (reviewedResources.length)
205
+ await service.trust(fresh.workspace, reviewedResources, reviewedNested.map(row => row.relativePath), { session });
206
+ const verified = await current();
207
+ if (expected && resourceSignature(expected.resources) !== resourceSignature(verified.resources.flatMap(row => row.current ? [row.current] : []))) {
208
+ fail('PROJECT_TRUST_INVALIDATED', `PROJECT_TRUST_INVALIDATED: the trusted project snapshot changed (${verified.reason}). TALOS did not load the changed snapshot.`, { reason: verified.reason, projectIdentity: verified.workspace.identityHash });
209
+ }
210
+ if (expected && nestedSignature(expected.nestedRepositories) !== nestedSignature(verified.nestedRepositories)) {
211
+ if (session)
212
+ service.forgetSession(fresh.workspace);
213
+ else
214
+ await service.revoke(fresh.workspace);
215
+ fail('PROJECT_TRUST_INVALIDATED', 'PROJECT_TRUST_INVALIDATED: nested repository boundaries changed while trust was being confirmed. TALOS revoked the new grant.', { reason: 'NESTED_REPOSITORY_BOUNDARY_CHANGED', projectIdentity: verified.workspace.identityHash });
216
+ }
217
+ if (target !== null && !session)
218
+ await writeFolderRecord(trustRoot, target, now());
219
+ return verified;
220
+ },
221
+ async sessionGrant() { return service.hasSession(await resolveWorkspaceIdentity({ projectRoot })); },
222
+ async revokeProject() { const before = await current(); await service.revoke(before.workspace); await rm(folderRecordPath(trustRoot, before.workspace.canonicalRoot), { force: true }); await rm(path.join(trustRoot, 'compat', before.workspace.identityHash), { recursive: true, force: true }); return current(); },
223
+ async trustScope(scope, options = {}) {
224
+ const before = await current();
225
+ if (options.expected)
226
+ await exactExpected(options.expected, before);
227
+ if (await readQuarantineRecord(trustRoot, before.workspace, scope))
228
+ fail('TRUST_SCOPE_QUARANTINED', 'TRUST_SCOPE_QUARANTINED: release quarantine before trusting this executable scope.');
229
+ if (before.storeSchema === null)
230
+ fail('PROJECT_TRUST_REQUIRED');
231
+ if (before.storeSchema === 'v1')
232
+ fail('TRUST_MIGRATION_REQUIRED', 'TRUST_MIGRATION_REQUIRED: migrate the legacy project trust store before changing individual resource trust.');
233
+ if (before.reason === 'NESTED_REPOSITORY_BOUNDARY_CHANGED')
234
+ nestedConsentFailure(before);
235
+ const rows = scopeRows(before, scope);
236
+ if (!rows.length)
237
+ fail('RESOURCE_NOT_FOUND');
238
+ const keys = rows.map(row => row.key);
239
+ const allTrusted = rows.every(row => row.state === 'trusted');
240
+ const resources = before.resources.flatMap(row => row.current ? [row.current] : []);
241
+ const changed = allTrusted ? false : await service.reconcile(before.workspace, resources, keys, before.nestedRepositories.map(row => row.relativePath));
242
+ return { changed, snapshot: await current() };
243
+ },
244
+ async untrustScope(scope) {
245
+ const before = await current();
246
+ if (before.storeSchema === null)
247
+ return { changed: false, snapshot: before };
248
+ if (before.storeSchema === 'v1')
249
+ fail('TRUST_MIGRATION_REQUIRED', 'TRUST_MIGRATION_REQUIRED: migrate the legacy project trust store before changing individual resource trust.');
250
+ const rows = scopeRows(before, scope);
251
+ if (!rows.length)
252
+ return { changed: false, snapshot: before };
253
+ const resources = before.resources.flatMap(row => row.current ? [row.current] : []);
254
+ const changed = await service.untrustResources(before.workspace, resources, rows.map(row => row.key), before.nestedRepositories.map(row => row.relativePath));
255
+ return { changed, snapshot: await current() };
256
+ },
257
+ async verifyScope(scope) {
258
+ const snapshot = await current();
259
+ if (await readQuarantineRecord(trustRoot, snapshot.workspace, scope))
260
+ return false;
261
+ const rows = scopeRows(snapshot, scope).filter(row => row.current);
262
+ return rows.length > 0 && rows.every(row => row.state === 'trusted');
263
+ },
264
+ async quarantineScope(scope, options = {}) {
265
+ const before = await current();
266
+ if (options.expected)
267
+ await exactExpected(options.expected, before);
268
+ const rows = scopeRows(before, scope).filter(row => row.current);
269
+ if (!rows.length)
270
+ fail('RESOURCE_NOT_FOUND');
271
+ const existing = await readQuarantineRecord(trustRoot, before.workspace, scope);
272
+ const record = {
273
+ schema: 'talos.cli.trust-quarantine.v1', active: true, workspaceIdentityHash: before.workspace.identityHash,
274
+ kind: scope.kind, id: scope.id, quarantinedAt: existing?.quarantinedAt ?? now(),
275
+ reason: options.reason ?? existing?.reason ?? null,
276
+ reviewedFingerprint: scopeReviewedFingerprint(rows)
277
+ };
278
+ const markerChanged = !existing || JSON.stringify(existing) !== JSON.stringify(record);
279
+ if (markerChanged)
280
+ await writeQuarantineRecord(trustRoot, before.workspace, scope, record);
281
+ let trustChanged = false;
282
+ if (before.storeSchema === 'v2') {
283
+ const resources = before.resources.flatMap(row => row.current ? [row.current] : []);
284
+ trustChanged = await service.untrustResources(before.workspace, resources, rows.map(row => row.key), before.nestedRepositories.map(row => row.relativePath));
285
+ }
286
+ return { changed: markerChanged || trustChanged, snapshot: await current(), record };
287
+ },
288
+ async releaseQuarantine(scope) {
289
+ const snapshot = await current();
290
+ const existing = await readQuarantineRecord(trustRoot, snapshot.workspace, scope);
291
+ if (!existing)
292
+ return { changed: false };
293
+ const rows = scopeRows(snapshot, scope).filter(row => row.current);
294
+ if (snapshot.storeSchema === 'v1')
295
+ fail('TRUST_MIGRATION_REQUIRED', 'TRUST_MIGRATION_REQUIRED: migrate the legacy project trust store before releasing quarantine.');
296
+ if (snapshot.storeSchema === 'v2' && rows.length) {
297
+ const resources = snapshot.resources.flatMap(row => row.current ? [row.current] : []);
298
+ await service.untrustResources(snapshot.workspace, resources, rows.map(row => row.key), snapshot.nestedRepositories.map(row => row.relativePath));
299
+ }
300
+ await rm(quarantinePath(trustRoot, snapshot.workspace, scope), { force: true });
301
+ return { changed: true };
302
+ },
303
+ async quarantineStatus(scope) {
304
+ const snapshot = await current();
305
+ return readQuarantineRecord(trustRoot, snapshot.workspace, scope);
306
+ },
307
+ async migrateLegacy(options = {}) {
308
+ const before = await current();
309
+ if (before.storeSchema !== 'v1')
310
+ return before;
311
+ const resourcesClean = before.resources.every(row => row.state === 'trusted');
312
+ if (!resourcesClean)
313
+ fail('TRUST_MIGRATION_SNAPSHOT_CHANGED');
314
+ if (before.nestedRepositories.length && !options.includeNested)
315
+ nestedConsentFailure(before);
316
+ const resources = before.resources.flatMap(row => row.current ? [row.current] : []);
317
+ await service.migrate(before.workspace, resources, before.nestedRepositories.map(row => row.relativePath));
318
+ return current();
319
+ },
320
+ async rollbackMigration() { const before = await current(); await service.rollbackMigration(before.workspace); return current(); },
321
+ async migrationBackupPath() { const snapshot = await current(); return service.migrationBackupPath(snapshot.workspace); },
322
+ async compatibilityRoots() { const snapshot = await current(); return compatibilityTrustRoots(trustRoot, snapshot.workspace); }
323
+ };
324
+ }
@@ -0,0 +1 @@
1
+ export {};