talos-code 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +111 -3
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/dist/archive/zip.js +73 -0
  5. package/dist/args.js +97 -0
  6. package/dist/automations/history-store.js +34 -0
  7. package/dist/automations/policy-store.js +43 -0
  8. package/dist/automations/runner.js +107 -0
  9. package/dist/automations/schedule.js +85 -0
  10. package/dist/automations/windows-task.js +53 -0
  11. package/dist/commands/advanced-cli.js +233 -0
  12. package/dist/commands/checkpoint-cli.js +45 -0
  13. package/dist/commands/config-cli.js +107 -0
  14. package/dist/commands/context.js +17 -0
  15. package/dist/commands/extensions-cli.js +207 -0
  16. package/dist/commands/project-cli.js +74 -0
  17. package/dist/commands/project-commands.js +143 -0
  18. package/dist/commands/provider-cli.js +185 -0
  19. package/dist/commands/services-cli.js +248 -0
  20. package/dist/commands/session-cli.js +171 -0
  21. package/dist/commands/system-cli.js +405 -0
  22. package/dist/config/commands.js +60 -0
  23. package/dist/config/load.js +382 -0
  24. package/dist/config/migrations.js +48 -0
  25. package/dist/config/types.js +11 -0
  26. package/dist/diagnostics/development-log.js +154 -0
  27. package/dist/diagnostics/doctor.js +104 -0
  28. package/dist/diagnostics/redact.js +80 -0
  29. package/dist/diagnostics/zip.js +52 -0
  30. package/dist/errors.js +156 -0
  31. package/dist/events/bridge.js +130 -0
  32. package/dist/extensions/installer.js +178 -0
  33. package/dist/extensions/package-schema.js +24 -0
  34. package/dist/headless/result.js +84 -0
  35. package/dist/headless/run.js +230 -0
  36. package/dist/i18n/en/approval.js +43 -0
  37. package/dist/i18n/en/common.js +9 -0
  38. package/dist/i18n/en/credentials.js +82 -0
  39. package/dist/i18n/en/errors.js +246 -0
  40. package/dist/i18n/en/firstrun.js +89 -0
  41. package/dist/i18n/en/providers.js +37 -0
  42. package/dist/i18n/en/screen.js +736 -0
  43. package/dist/i18n/en/tools.js +127 -0
  44. package/dist/i18n/en.js +14 -0
  45. package/dist/i18n/error-view.js +307 -0
  46. package/dist/i18n/index.js +19 -0
  47. package/dist/i18n/kernel-map.js +139 -0
  48. package/dist/io.js +45 -0
  49. package/dist/main.js +330 -0
  50. package/dist/paths.js +41 -0
  51. package/dist/protocol/v2/codec.js +9 -0
  52. package/dist/protocol/v2/events.js +555 -0
  53. package/dist/protocol/v2/index.js +4 -0
  54. package/dist/protocol/v2/replay.js +28 -0
  55. package/dist/protocol/v2/types.js +1 -0
  56. package/dist/provider/control-plane.js +135 -0
  57. package/dist/provider/environment-keys.js +275 -0
  58. package/dist/provider/health.js +110 -0
  59. package/dist/provider/missing-key.js +48 -0
  60. package/dist/provider/model-catalog.js +168 -0
  61. package/dist/provider/provider-text.js +13 -0
  62. package/dist/provider/store.js +95 -0
  63. package/dist/provider/system-keyring.js +214 -0
  64. package/dist/runtime/active-run.js +63 -0
  65. package/dist/runtime/agent-tree.js +74 -0
  66. package/dist/runtime/attachments.js +84 -0
  67. package/dist/runtime/brokered-executor.js +395 -0
  68. package/dist/runtime/context-status.js +76 -0
  69. package/dist/runtime/create-runtime.js +111 -0
  70. package/dist/runtime/model-profile.js +361 -0
  71. package/dist/runtime/output-store.js +137 -0
  72. package/dist/runtime/provider-attempts.js +185 -0
  73. package/dist/runtime/replay-buffer.js +153 -0
  74. package/dist/runtime/repo.js +95 -0
  75. package/dist/runtime/session-facade.js +135 -0
  76. package/dist/runtime/session-summary.js +125 -0
  77. package/dist/runtime/supervisor.js +262 -0
  78. package/dist/runtime/talos-composition.js +1006 -0
  79. package/dist/runtime/types.js +5 -0
  80. package/dist/runtime/usage-snapshot.js +82 -0
  81. package/dist/security/auto-classifier.js +38 -0
  82. package/dist/security/credential-free-environment.js +54 -0
  83. package/dist/security/evaluate.js +243 -0
  84. package/dist/security/execution-backends.js +236 -0
  85. package/dist/security/execution-broker.js +102 -0
  86. package/dist/security/forge-scan.js +74 -0
  87. package/dist/security/from-approval.js +39 -0
  88. package/dist/security/mxc-execution-backend.js +281 -0
  89. package/dist/security/permission-engine.js +138 -0
  90. package/dist/security/permission-explanation.js +54 -0
  91. package/dist/security/persist.js +179 -0
  92. package/dist/security/plugin-guard.js +230 -0
  93. package/dist/security/process-tree-evidence-store.js +74 -0
  94. package/dist/security/process-tree-probe.js +554 -0
  95. package/dist/security/project-resource-inventory.js +186 -0
  96. package/dist/security/project-trust-gate.js +38 -0
  97. package/dist/security/project-trust.js +367 -0
  98. package/dist/security/rule-parser.js +201 -0
  99. package/dist/security/shell-segmentation.js +68 -0
  100. package/dist/security/trust-authority.js +324 -0
  101. package/dist/security/types.js +1 -0
  102. package/dist/security/workspace-identity.js +102 -0
  103. package/dist/services/automation-facade.js +59 -0
  104. package/dist/services/forge-facade.js +77 -0
  105. package/dist/services/hook-facade.js +240 -0
  106. package/dist/services/index.js +16 -0
  107. package/dist/services/library-facade.js +174 -0
  108. package/dist/services/mcp-facade.js +348 -0
  109. package/dist/services/memory-facade.js +126 -0
  110. package/dist/services/notes-facade.js +157 -0
  111. package/dist/services/plugin-facade.js +308 -0
  112. package/dist/services/research-facade.js +110 -0
  113. package/dist/services/task-board-facade.js +236 -0
  114. package/dist/services/workflow-catalog.js +246 -0
  115. package/dist/sessions/export-format.js +98 -0
  116. package/dist/sessions/metadata-store.js +160 -0
  117. package/dist/sessions/share.js +122 -0
  118. package/dist/sessions/transfer.js +16 -0
  119. package/dist/subcommands.js +62 -0
  120. package/dist/tui/agent-roster.js +36 -0
  121. package/dist/tui/app.js +3761 -0
  122. package/dist/tui/approval.js +36 -0
  123. package/dist/tui/boot/boot-sequence.js +76 -0
  124. package/dist/tui/boot/cinematic.js +243 -0
  125. package/dist/tui/boot/desktop-logo.js +82 -0
  126. package/dist/tui/boot.js +3 -0
  127. package/dist/tui/busy-input.js +58 -0
  128. package/dist/tui/catalog-service.js +559 -0
  129. package/dist/tui/components/assistant-stream.js +1 -0
  130. package/dist/tui/components/command-menu.js +68 -0
  131. package/dist/tui/components/composer.js +219 -0
  132. package/dist/tui/components/diff.js +35 -0
  133. package/dist/tui/components/footer.js +48 -0
  134. package/dist/tui/components/header.js +6 -0
  135. package/dist/tui/components/markdown.js +305 -0
  136. package/dist/tui/components/status-indicator.js +32 -0
  137. package/dist/tui/components/terminal-shell.js +205 -0
  138. package/dist/tui/components/thinking-row.js +19 -0
  139. package/dist/tui/components/tool-row.js +97 -0
  140. package/dist/tui/components/transcript-virtualizer.js +165 -0
  141. package/dist/tui/components/transcript.js +43 -0
  142. package/dist/tui/diff-model.js +77 -0
  143. package/dist/tui/editor-history.js +21 -0
  144. package/dist/tui/editor.js +210 -0
  145. package/dist/tui/event-adapter.js +436 -0
  146. package/dist/tui/exit-output.js +58 -0
  147. package/dist/tui/external-editor.js +53 -0
  148. package/dist/tui/file-completion.js +89 -0
  149. package/dist/tui/focus-manager.js +5 -0
  150. package/dist/tui/highlight.js +30 -0
  151. package/dist/tui/input-router.js +18 -0
  152. package/dist/tui/interrupt.js +99 -0
  153. package/dist/tui/keybindings.js +194 -0
  154. package/dist/tui/keymap-resolver.js +91 -0
  155. package/dist/tui/launch-state.js +139 -0
  156. package/dist/tui/line-diff.js +57 -0
  157. package/dist/tui/live-activity.js +45 -0
  158. package/dist/tui/metrics.js +123 -0
  159. package/dist/tui/onboarding.js +36 -0
  160. package/dist/tui/overlays/agent-tree.js +39 -0
  161. package/dist/tui/overlays/approval-dialog.js +640 -0
  162. package/dist/tui/overlays/automation-center.js +51 -0
  163. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  164. package/dist/tui/overlays/context-inspector.js +36 -0
  165. package/dist/tui/overlays/effort-line.js +110 -0
  166. package/dist/tui/overlays/forge-center.js +46 -0
  167. package/dist/tui/overlays/help-dialog.js +16 -0
  168. package/dist/tui/overlays/history-picker.js +37 -0
  169. package/dist/tui/overlays/hook-center.js +70 -0
  170. package/dist/tui/overlays/library-center.js +55 -0
  171. package/dist/tui/overlays/mcp-center.js +60 -0
  172. package/dist/tui/overlays/memory-center.js +63 -0
  173. package/dist/tui/overlays/model-picker.js +72 -0
  174. package/dist/tui/overlays/notes-center.js +53 -0
  175. package/dist/tui/overlays/overlay-host.js +8 -0
  176. package/dist/tui/overlays/plugin-center.js +76 -0
  177. package/dist/tui/overlays/provider-picker.js +145 -0
  178. package/dist/tui/overlays/queue-editor.js +32 -0
  179. package/dist/tui/overlays/research-center.js +59 -0
  180. package/dist/tui/overlays/scroll-window.js +234 -0
  181. package/dist/tui/overlays/session-picker.js +115 -0
  182. package/dist/tui/overlays/tasks-center.js +83 -0
  183. package/dist/tui/overlays/theme-picker.js +21 -0
  184. package/dist/tui/overlays/transcript-search.js +58 -0
  185. package/dist/tui/overlays/trust-center.js +29 -0
  186. package/dist/tui/overlays/workflow-center.js +46 -0
  187. package/dist/tui/project-file-index.js +214 -0
  188. package/dist/tui/project-references.js +85 -0
  189. package/dist/tui/project-trust-prompt.js +287 -0
  190. package/dist/tui/prompt-history-store.js +116 -0
  191. package/dist/tui/queue-store.js +110 -0
  192. package/dist/tui/regions/budget.js +59 -0
  193. package/dist/tui/regions/views.js +96 -0
  194. package/dist/tui/render-coordinator.js +148 -0
  195. package/dist/tui/render-scheduler.js +4 -0
  196. package/dist/tui/run.js +69 -0
  197. package/dist/tui/selection-list.js +29 -0
  198. package/dist/tui/session-controller.js +778 -0
  199. package/dist/tui/session-export.js +54 -0
  200. package/dist/tui/shell-input.js +35 -0
  201. package/dist/tui/shell-layout.js +48 -0
  202. package/dist/tui/shell-model.js +98 -0
  203. package/dist/tui/slash-commands.js +80 -0
  204. package/dist/tui/state.js +151 -0
  205. package/dist/tui/status-bar.js +125 -0
  206. package/dist/tui/status-view.js +40 -0
  207. package/dist/tui/terminal-capabilities.js +9 -0
  208. package/dist/tui/terminal-session.js +11 -0
  209. package/dist/tui/text-width.js +66 -0
  210. package/dist/tui/theme-catalog.js +25 -0
  211. package/dist/tui/theme-store.js +23 -0
  212. package/dist/tui/theme.js +23 -0
  213. package/dist/tui/tool-display.js +135 -0
  214. package/dist/tui/tool-facts.js +1 -0
  215. package/dist/tui/tools/bash-renderer.js +32 -0
  216. package/dist/tui/tools/change.js +61 -0
  217. package/dist/tui/tools/edit-renderer.js +17 -0
  218. package/dist/tui/tools/generic-renderer.js +13 -0
  219. package/dist/tui/tools/list-renderer.js +14 -0
  220. package/dist/tui/tools/read-renderer.js +13 -0
  221. package/dist/tui/tools/registry.js +20 -0
  222. package/dist/tui/tools/row-format.js +187 -0
  223. package/dist/tui/tools/search-renderer.js +38 -0
  224. package/dist/tui/tools/shared.js +97 -0
  225. package/dist/tui/tools/write-renderer.js +15 -0
  226. package/dist/tui/transcript-model.js +441 -0
  227. package/dist/tui/ui-preferences.js +79 -0
  228. package/dist/tui/usage-view.js +77 -0
  229. package/dist/tui/vim-mode.js +99 -0
  230. package/dist/update/check.js +15 -0
  231. package/dist/update/npm.js +51 -0
  232. package/dist/update/run.js +129 -0
  233. package/dist/version.js +2 -0
  234. package/dist/workspace/checkpoint-store.js +210 -0
  235. package/dist/workspace/checkpoint.js +413 -0
  236. package/dist/workspace/restore.js +232 -0
  237. package/package.json +63 -5
  238. package/vendor/context-engine/package.json +11 -0
  239. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  240. package/vendor/context-engine/src/contracts.mjs +48 -0
  241. package/vendor/context-engine/src/engine.mjs +445 -0
  242. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  243. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  244. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  245. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  246. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  247. package/vendor/context-engine/src/profiles.mjs +10 -0
  248. package/vendor/context-engine/src/retrieval.mjs +104 -0
  249. package/vendor/context-engine/src/summary.mjs +97 -0
  250. package/vendor/context-engine/src/usage.mjs +34 -0
  251. package/vendor/harness-ui/package.json +38 -0
  252. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  253. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  254. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  255. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  256. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  257. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  258. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  259. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  260. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  261. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  262. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  263. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  264. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  265. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  266. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  267. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  268. package/vendor/harness-ui/src/config.mjs +697 -0
  269. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  270. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  271. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  272. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  273. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  274. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  275. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  276. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  277. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  278. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  279. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  280. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  281. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  282. package/vendor/harness-ui/src/doctor.mjs +142 -0
  283. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  284. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  285. package/vendor/harness-ui/src/document-report.mjs +331 -0
  286. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  287. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  288. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  289. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  290. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  291. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  292. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  293. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  294. package/vendor/harness-ui/src/git-service.mjs +626 -0
  295. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  296. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  297. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  298. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  299. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  300. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  301. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  302. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  303. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  304. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  305. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  306. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  307. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  308. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  309. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  310. package/vendor/harness-ui/src/library-store.mjs +652 -0
  311. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  312. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  313. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  314. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  315. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  316. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  317. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  318. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  319. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  320. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  321. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  322. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  323. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  324. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  325. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  326. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  327. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  328. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  329. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  330. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  331. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  332. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  333. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  334. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  335. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  336. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  337. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  338. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  339. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  340. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  341. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  342. package/vendor/harness-ui/src/research/card.mjs +235 -0
  343. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  344. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  345. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  346. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  347. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  348. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  349. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  350. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  351. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  352. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  353. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  354. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  355. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  356. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  357. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  358. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  359. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  360. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  361. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  362. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  363. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  364. package/vendor/harness-ui/src/research/report.mjs +203 -0
  365. package/vendor/harness-ui/src/research/run.mjs +527 -0
  366. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  367. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  368. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  369. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  370. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  371. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  372. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  373. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  374. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  375. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  376. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  377. package/vendor/harness-ui/src/session-store.mjs +220 -0
  378. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  379. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  380. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  381. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  382. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  383. package/vendor/harness-ui/src/static-files.mjs +96 -0
  384. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  385. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  386. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  387. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  388. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  389. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  390. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  391. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  392. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  393. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  394. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  395. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  396. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  397. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  398. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  399. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  400. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  401. package/vendor/manifest.json +170 -0
@@ -0,0 +1,214 @@
1
+ /*
2
+ * R5b D6 (owner decision Q-R5-16): the project's files for `@`, from an own bounded walker (no new dependency; Pi calls
3
+ * `fd`, Claude Code and Codex walk with ripgrep's ignore rules).
4
+ * - `.gitignore` files are respected, the root's and every nested one, in git's order: later rules win, `!` re-includes,
5
+ * a trailing `/` is for folders only, a pattern with a slash is anchored to its file's folder, one without matches at any
6
+ * depth, `*` `?` `[...]` `**` as git reads them. A folder that is ignored is not walked (git cannot re-include inside it).
7
+ * Not read: `.git/info/exclude` and the global excludes file.
8
+ * - `.git` and `node_modules` are never walked.
9
+ * - A link or junction to a folder is never followed (the R4 guard, file-completion.ts); a linked file is kept only if its
10
+ * real path is inside the workspace. Names with control, line-separator or bidi characters are never drawn.
11
+ * - At most `maxEntries` rows, breadth first; `capped` says the walk stopped, so the list can say so (plan Part III item 2).
12
+ */
13
+ import { readFile, readdir, stat } from 'node:fs/promises';
14
+ import { join, resolve } from 'node:path';
15
+ import { resolvePathInsideWorkspace, resolveWorkspaceIdentity } from "../security/workspace-identity.js";
16
+ export const PROJECT_INDEX_MAX_ENTRIES = 20_000;
17
+ const MAX_DEPTH = 32;
18
+ const NEVER_WALKED = new Set(['.git', 'node_modules']);
19
+ const UNSAFE_NAME = /[\x00-\x1f\x7f\p{Zl}\p{Zp}\p{Bidi_Control}]/u;
20
+ function escapeRegex(text) { return text.replace(/[.*+?^${}()|[\]\\/]/gu, '\\$&'); }
21
+ function globSource(glob) {
22
+ let out = '';
23
+ for (let i = 0; i < glob.length; i++) {
24
+ const c = glob[i];
25
+ if (c === '*') {
26
+ if (glob[i + 1] === '*') {
27
+ const atStart = i === 0 || glob[i - 1] === '/', slashAfter = glob[i + 2] === '/', atEnd = i + 2 === glob.length;
28
+ if (atStart && slashAfter) {
29
+ out += '(?:.*/)?';
30
+ i += 2;
31
+ continue;
32
+ }
33
+ if (atStart && atEnd) {
34
+ out += '.*';
35
+ i += 1;
36
+ continue;
37
+ }
38
+ out += '.*';
39
+ i += 1;
40
+ continue;
41
+ }
42
+ out += '[^/]*';
43
+ continue;
44
+ }
45
+ if (c === '?') {
46
+ out += '[^/]';
47
+ continue;
48
+ }
49
+ if (c === '[') {
50
+ const end = glob.indexOf(']', i + 2);
51
+ if (end > i) {
52
+ let body = glob.slice(i + 1, end);
53
+ if (body.startsWith('!'))
54
+ body = '^' + body.slice(1);
55
+ out += `[${body.replace(/\\/gu, '\\\\')}]`;
56
+ i = end;
57
+ continue;
58
+ }
59
+ }
60
+ if (c === '\\' && i + 1 < glob.length) {
61
+ out += escapeRegex(glob[i + 1]);
62
+ i += 1;
63
+ continue;
64
+ }
65
+ out += escapeRegex(c);
66
+ }
67
+ return out;
68
+ }
69
+ /** The rules of one `.gitignore`; `base` is its folder relative to the project root ('' for the root). */
70
+ export function gitignoreRules(text, base) {
71
+ const rules = [];
72
+ for (const raw of text.split(/\r?\n/u)) {
73
+ let line = raw.replace(/(?<!\\)\s+$/u, '');
74
+ if (!line || line.startsWith('#'))
75
+ continue;
76
+ let negate = false;
77
+ if (line.startsWith('!')) {
78
+ negate = true;
79
+ line = line.slice(1);
80
+ }
81
+ if (line.startsWith('\\#') || line.startsWith('\\!'))
82
+ line = line.slice(1);
83
+ let directoryOnly = false;
84
+ if (line.endsWith('/')) {
85
+ directoryOnly = true;
86
+ line = line.slice(0, -1);
87
+ }
88
+ if (!line)
89
+ continue;
90
+ const anchored = line.includes('/');
91
+ if (line.startsWith('/'))
92
+ line = line.slice(1);
93
+ const source = globSource(line);
94
+ rules.push({ base, negate, directoryOnly, pattern: new RegExp(anchored ? `^${source}$` : `(?:^|/)${source}$`, 'u') });
95
+ }
96
+ return rules;
97
+ }
98
+ /** Git's answer for one path (relative to the root, `/`-separated): the last rule that matches decides. */
99
+ export function isIgnored(rules, path, directory) {
100
+ let ignored = false;
101
+ for (const rule of rules) {
102
+ if (rule.directoryOnly && !directory)
103
+ continue;
104
+ const relative = rule.base ? (path.startsWith(rule.base + '/') ? path.slice(rule.base.length + 1) : null) : path;
105
+ if (relative === null)
106
+ continue;
107
+ if (rule.pattern.test(relative))
108
+ ignored = !rule.negate;
109
+ }
110
+ return ignored;
111
+ }
112
+ export async function buildProjectFileIndex(projectRoot, { maxEntries = PROJECT_INDEX_MAX_ENTRIES } = {}) {
113
+ const workspace = await resolveWorkspaceIdentity({ projectRoot });
114
+ const root = workspace.requestedRoot;
115
+ const entries = [];
116
+ let capped = false;
117
+ const queue = [{ path: '', depth: 0, rules: [] }];
118
+ while (queue.length && !capped) {
119
+ const folder = queue.shift();
120
+ const absolute = folder.path ? join(root, ...folder.path.split('/')) : root;
121
+ let rules = folder.rules;
122
+ try {
123
+ rules = [...rules, ...gitignoreRules(await readFile(join(absolute, '.gitignore'), 'utf8'), folder.path)];
124
+ }
125
+ catch { /* no .gitignore here */ }
126
+ let dirents;
127
+ try {
128
+ dirents = await readdir(absolute, { withFileTypes: true });
129
+ }
130
+ catch {
131
+ continue;
132
+ }
133
+ dirents.sort((a, b) => a.name.localeCompare(b.name));
134
+ for (const dirent of dirents) {
135
+ if (UNSAFE_NAME.test(dirent.name) || NEVER_WALKED.has(dirent.name))
136
+ continue;
137
+ const path = folder.path ? `${folder.path}/${dirent.name}` : dirent.name;
138
+ let directory = dirent.isDirectory();
139
+ if (dirent.isSymbolicLink()) {
140
+ /* A link or junction: a folder is never followed; a file is kept only when its real path stays inside. */
141
+ const real = await resolvePathInsideWorkspace(workspace, resolve(absolute, dirent.name)).catch(() => null);
142
+ if (!real)
143
+ continue;
144
+ let info;
145
+ try {
146
+ info = await stat(real);
147
+ }
148
+ catch {
149
+ continue;
150
+ }
151
+ if (!info.isFile())
152
+ continue;
153
+ directory = false;
154
+ }
155
+ else if (!directory && !dirent.isFile())
156
+ continue;
157
+ if (isIgnored(rules, path, directory))
158
+ continue;
159
+ if (entries.length >= maxEntries) {
160
+ capped = true;
161
+ break;
162
+ }
163
+ entries.push({ path, directory });
164
+ if (directory && folder.depth + 1 < MAX_DEPTH)
165
+ queue.push({ path, depth: folder.depth + 1, rules });
166
+ }
167
+ }
168
+ return { root, entries, capped, max: maxEntries, builtAt: Date.now() };
169
+ }
170
+ /*
171
+ * Ranking, best first (Claude Code 2.1.280: "a file whose name contains the query above folder-only matches"):
172
+ * 0 the name starts with the query · 1 the name contains it · 2 the path contains it · 3 the query's letters appear in
173
+ * order in the path (3 letters or more); inside a tier, files before folders, then shallower, then shorter, then by name.
174
+ */
175
+ export function rankProjectFiles(index, query) {
176
+ const q = query.toLowerCase().replace(/\\/gu, '/');
177
+ if (!q)
178
+ return [];
179
+ const scored = [];
180
+ for (const entry of index.entries) {
181
+ const path = entry.path.toLowerCase(), name = path.slice(path.lastIndexOf('/') + 1);
182
+ let tier = -1;
183
+ if (name.startsWith(q))
184
+ tier = 0;
185
+ else if (name.includes(q))
186
+ tier = 1;
187
+ else if (path.includes(q))
188
+ tier = 2;
189
+ else if (q.length >= 3) {
190
+ let at = 0;
191
+ for (const ch of path) {
192
+ if (ch === q[at])
193
+ at += 1;
194
+ if (at === q.length)
195
+ break;
196
+ }
197
+ if (at === q.length)
198
+ tier = 3;
199
+ }
200
+ if (tier >= 0)
201
+ scored.push({ entry, tier });
202
+ }
203
+ const depth = (path) => path.split('/').length;
204
+ return scored.sort((a, b) => a.tier - b.tier || Number(a.entry.directory) - Number(b.entry.directory) || depth(a.entry.path) - depth(b.entry.path) || a.entry.path.length - b.entry.path.length || a.entry.path.localeCompare(b.entry.path)).map(row => row.entry);
205
+ }
206
+ /** One index per project, rebuilt when older than `ttlMs`, so typing after `@` does not walk the tree at every key. */
207
+ export function createProjectFileIndexCache(projectRoot, { ttlMs = 15_000 } = {}) {
208
+ let pending = null;
209
+ return { get() {
210
+ if (pending)
211
+ return pending.then(index => Date.now() - index.builtAt <= ttlMs ? index : (pending = buildProjectFileIndex(projectRoot)));
212
+ return (pending = buildProjectFileIndex(projectRoot).catch(error => { pending = null; throw error; }));
213
+ } };
214
+ }
@@ -0,0 +1,85 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { IMAGE_ATTACHMENT_MAX_BYTES, TEXT_ATTACHMENT_MAX_BYTES, imageMimeFromBytes, validateCliAttachments } from "../runtime/attachments.js";
4
+ import { isPathInsideWorkspace, resolvePathInsideWorkspace, resolveWorkspaceIdentity } from "../security/workspace-identity.js";
5
+ export class ProjectReferenceError extends Error {
6
+ code;
7
+ constructor(code, message = code) { super(message); this.name = 'ProjectReferenceError'; this.code = code; }
8
+ }
9
+ export function parseProjectReferenceTokens(prompt) {
10
+ const out = [];
11
+ const pattern = /(?:^|\s)@(?:"([^"\r\n]+)"|([^\s"'<>]+))/gu;
12
+ for (const match of prompt.matchAll(pattern)) {
13
+ const value = match[1] ?? match[2];
14
+ if (value)
15
+ out.push(value);
16
+ }
17
+ return out;
18
+ }
19
+ function portableAbsolute(value) { return path.isAbsolute(value) || path.win32.isAbsolute(value); }
20
+ function relativeProjectPath(root, target) { return path.relative(root, target).split(path.sep).join('/'); }
21
+ export async function resolveProjectReferences({ projectRoot, prompt }) {
22
+ const tokens = parseProjectReferenceTokens(prompt);
23
+ if (tokens.length === 0)
24
+ return [];
25
+ const workspace = await resolveWorkspaceIdentity({ projectRoot });
26
+ const seen = new Set();
27
+ const attachments = [];
28
+ for (const raw of tokens) {
29
+ if (portableAbsolute(raw))
30
+ throw new ProjectReferenceError('PROJECT_REFERENCE_OUTSIDE_WORKSPACE', 'Absolute project references are not allowed.');
31
+ const lexical = path.resolve(workspace.requestedRoot, raw);
32
+ if (!isPathInsideWorkspace(workspace, lexical))
33
+ throw new ProjectReferenceError('PROJECT_REFERENCE_OUTSIDE_WORKSPACE', 'Project reference escapes the workspace.');
34
+ let info;
35
+ try {
36
+ info = await stat(lexical);
37
+ }
38
+ catch (error) {
39
+ if (error.code === 'ENOENT')
40
+ throw new ProjectReferenceError('PROJECT_REFERENCE_NOT_FOUND', `Project reference not found: ${raw}`);
41
+ throw error;
42
+ }
43
+ const canonical = await resolvePathInsideWorkspace(workspace, lexical);
44
+ if (!canonical)
45
+ throw new ProjectReferenceError('PROJECT_REFERENCE_OUTSIDE_WORKSPACE', 'Project reference resolves outside the workspace.');
46
+ try {
47
+ info = await stat(canonical);
48
+ }
49
+ catch {
50
+ throw new ProjectReferenceError('PROJECT_REFERENCE_NOT_FOUND', `Project reference not found: ${raw}`);
51
+ }
52
+ if (!info.isFile())
53
+ throw new ProjectReferenceError('PROJECT_REFERENCE_NOT_FILE', `Project reference is not a regular file: ${raw}`);
54
+ if (info.size > IMAGE_ATTACHMENT_MAX_BYTES)
55
+ throw new ProjectReferenceError('ATTACHMENT_ITEM_TOO_LARGE', 'Referenced file is larger than the maximum supported attachment.');
56
+ if (seen.has(canonical))
57
+ continue;
58
+ seen.add(canonical);
59
+ const bytes = await readFile(canonical);
60
+ const canonicalAfter = await resolvePathInsideWorkspace(workspace, canonical);
61
+ if (canonicalAfter !== canonical)
62
+ throw new ProjectReferenceError('PROJECT_REFERENCE_CHANGED', 'Project reference changed while it was being read.');
63
+ const mime = imageMimeFromBytes(bytes);
64
+ const stablePath = relativeProjectPath(workspace.canonicalRoot, canonical);
65
+ if (!stablePath || /[\0\r\n]/u.test(stablePath))
66
+ throw new ProjectReferenceError('ATTACHMENT_INVALID', 'Project reference path is invalid.');
67
+ if (mime) {
68
+ attachments.push({ kind: 'image', path: stablePath, mimeType: mime, bytes: bytes.length, dataBase64: bytes.toString('base64') });
69
+ continue;
70
+ }
71
+ if (bytes.length > TEXT_ATTACHMENT_MAX_BYTES)
72
+ throw new ProjectReferenceError('ATTACHMENT_TEXT_TOO_LARGE', `Text reference is larger than ${TEXT_ATTACHMENT_MAX_BYTES} bytes.`);
73
+ let text;
74
+ try {
75
+ text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
76
+ }
77
+ catch {
78
+ throw new ProjectReferenceError('ATTACHMENT_BINARY_UNSUPPORTED', `Unsupported binary project reference: ${stablePath}`);
79
+ }
80
+ if (text.includes('\0'))
81
+ throw new ProjectReferenceError('ATTACHMENT_BINARY_UNSUPPORTED', `Unsupported binary project reference: ${stablePath}`);
82
+ attachments.push({ kind: 'text', path: stablePath, mimeType: 'text/plain', bytes: bytes.length, text });
83
+ }
84
+ return validateCliAttachments(attachments);
85
+ }
@@ -0,0 +1,287 @@
1
+ import { t, tn } from "../i18n/index.js";
2
+ import { createTerminalSizeStore, frameRowsFor, terminalFrameProps } from "./components/terminal-shell.js";
3
+ import { detectTerminalCapabilities } from "./terminal-capabilities.js";
4
+ import { createTerminalSessionPlan } from "./terminal-session.js";
5
+ import { displayWidth, truncateDisplay, truncateMiddle, wrapDisplay } from "./text-width.js";
6
+ import { liveTheme } from "./theme-store.js";
7
+ export const PROJECT_TRUST_GRACE_MS = 300;
8
+ /* E4: paths are shown as the person types them. Control, C1, line/paragraph separators, bidi controls and
9
+ default-ignorable characters are shown as `\uXXXX`; a backslash is doubled only where it would read as such an escape
10
+ (a folder named `u0041`), so every escape on screen is unambiguous and every other backslash is single. */
11
+ const TERMINAL_UNSAFE = /[\x00-\x1f\x7f-\x9f\u2028\u2029\p{Bidi_Control}\p{Default_Ignorable_Code_Point}]/gu;
12
+ const ESCAPE_LOOKALIKE = /\\(?=u(?:[0-9a-fA-F]{4}|\{[0-9a-fA-F]+\}))/gu;
13
+ const BACKSLASH = String.fromCharCode(92);
14
+ export function inertProjectTrustText(value) {
15
+ return value.replace(ESCAPE_LOOKALIKE, () => BACKSLASH + BACKSLASH).replace(TERMINAL_UNSAFE, character => {
16
+ const code = character.codePointAt(0);
17
+ return code > 0xffff ? `${BACKSLASH}u{${code.toString(16)}}` : `${BACKSLASH}u${code.toString(16).padStart(4, '0')}`;
18
+ });
19
+ }
20
+ function reasonText(assessment) {
21
+ if (assessment.reason === 'PROJECT_UNTRUSTED')
22
+ return t('firstrun.trust.reason.untrusted');
23
+ if (assessment.trusted)
24
+ return t('firstrun.trust.plain');
25
+ if (assessment.reason === 'RESOURCE_FINGERPRINT_CHANGED')
26
+ return t('firstrun.trust.reason.fingerprint');
27
+ if (assessment.reason === 'RESOURCE_SET_CHANGED')
28
+ return t('firstrun.trust.reason.set');
29
+ if (assessment.reason === 'RESOURCE_IDENTITY_MISMATCH')
30
+ return t('firstrun.trust.reason.identity');
31
+ if (assessment.reason === 'NESTED_REPOSITORY_BOUNDARY_CHANGED')
32
+ return t('firstrun.trust.reason.nested');
33
+ return t('firstrun.trust.reason.other', { reason: assessment.reason });
34
+ }
35
+ /* E4: each resource once — `kind · path`, and `kind · id · path` only when the id is not the path (an MCP server or a
36
+ hook named inside a shared config file: the trust center lists them the same way). */
37
+ function resourceLine(resource) {
38
+ const where = inertProjectTrustText(resource.relativePath);
39
+ return resource.id === resource.relativePath ? `${resource.kind} · ${where}` : `${resource.kind} · ${inertProjectTrustText(resource.id)} · ${where}`;
40
+ }
41
+ export function projectTrustPromptModel({ assessment, nestedRepositories }) {
42
+ const counts = new Map();
43
+ for (const resource of assessment.resources)
44
+ counts.set(resource.kind, (counts.get(resource.kind) ?? 0) + 1);
45
+ const detail = [...counts.entries()].sort(([left], [right]) => left.localeCompare(right)).map(([kind, count]) => `${kind} ${count}`).join(', ');
46
+ const folder = assessment.folder;
47
+ const sessionOnly = folder?.sessionOnly === true;
48
+ /* The parent is offered for a folder being decided for the first time, and only when it may be saved. */
49
+ const parent = !sessionOnly && folder && !folder.decided && folder.parent ? inertProjectTrustText(folder.parent) : null;
50
+ const choices = [
51
+ { id: 'cancel', label: t('firstrun.trust.choice.cancel'), shortcut: 'c' },
52
+ { id: 'trust', label: t(sessionOnly ? 'firstrun.trust.choice.trustSession' : 'firstrun.trust.choice.trust'), shortcut: 't' },
53
+ ...(parent ? [{ id: 'trust-parent', label: t('firstrun.trust.choice.parent', { path: parent }), shortcut: 'p' }] : []),
54
+ ];
55
+ const firstTime = assessment.trusted || assessment.reason === 'PROJECT_UNTRUSTED';
56
+ return {
57
+ title: t(firstTime ? 'firstrun.trust.title' : 'firstrun.trust.titleChanged'),
58
+ project: inertProjectTrustText(assessment.workspace.canonicalRoot),
59
+ reason: reasonText(assessment),
60
+ resourceSummary: assessment.resources.length ? tn('firstrun.trust.files', assessment.resources.length, { detail }) : '',
61
+ resources: assessment.resources.map(resourceLine),
62
+ nestedRepositories: nestedRepositories.map(entry => inertProjectTrustText(entry.relativePath)),
63
+ session: sessionOnly ? t('firstrun.trust.session') : null,
64
+ choices,
65
+ parent,
66
+ defaultSelection: 'cancel',
67
+ };
68
+ }
69
+ /** Moves the highlight; Tab wraps around, the arrows stop at the ends. Two choices when `choices` is omitted (pre-R4). */
70
+ export function nextProjectTrustSelection(current, direction, choices = ['cancel', 'trust']) {
71
+ const index = Math.max(0, choices.indexOf(current));
72
+ if (direction === 'toggle')
73
+ return choices[(index + 1) % choices.length];
74
+ return choices[Math.min(choices.length - 1, Math.max(0, index + (direction === 'next' ? 1 : -1)))];
75
+ }
76
+ /* Word wrap by display width; a word longer than the row is broken inside. */
77
+ function wrapWords(text, width) {
78
+ const limit = Math.max(1, width);
79
+ const rows = [];
80
+ let row = '';
81
+ for (const word of text.split(/ +/u).filter(Boolean)) {
82
+ const candidate = row ? `${row} ${word}` : word;
83
+ if (displayWidth(candidate) <= limit) {
84
+ row = candidate;
85
+ continue;
86
+ }
87
+ if (row)
88
+ rows.push(row);
89
+ if (displayWidth(word) <= limit) {
90
+ row = word;
91
+ continue;
92
+ }
93
+ const pieces = wrapDisplay(word, limit);
94
+ row = pieces.pop();
95
+ rows.push(...pieces);
96
+ }
97
+ if (row || !rows.length)
98
+ rows.push(row);
99
+ return rows;
100
+ }
101
+ /* A long value squeezed into one row: cut with an ellipsis. */
102
+ function oneRow(rows, width) { return rows.length <= 1 ? rows : [truncateDisplay(rows.join(' '), width)]; }
103
+ /**
104
+ * The question's rows for a terminal of `columns`×`rows` (inside a round border with one column of padding, in a frame of
105
+ * the R1 height). What does not fit is given up in this order: the blank row, the keys row, nested repositories (counted);
106
+ * then the folder path, the summary and the reason shrink to one row each; then listed files go one by one (counted
107
+ * instead) and the session line shrinks; at last the rest goes. The title and every choice always show, and the files a
108
+ * folder holds are named for as long as there is room (measured at 40×12: the list went first and the one file was lost).
109
+ */
110
+ export function layoutProjectTrustPrompt(model, { columns, rows, selected, platform = process.platform }) {
111
+ const width = Math.max(1, Math.floor(columns) - 4);
112
+ const budget = Math.max(1, frameRowsFor(rows, platform) - 2);
113
+ const choiceRows = model.choices.map(choice => {
114
+ let label = choice.label;
115
+ if (choice.id === 'trust-parent' && model.parent && displayWidth(label) > width - 2) {
116
+ const room = Math.max(4, width - 2 - displayWidth(t('firstrun.trust.choice.parent', { path: '' })));
117
+ label = t('firstrun.trust.choice.parent', { path: truncateMiddle(model.parent, room) });
118
+ }
119
+ return { kind: 'choice', text: truncateDisplay(`${choice.id === selected ? '>' : ' '} ${label}`, width), selected: choice.id === selected };
120
+ });
121
+ const title = truncateDisplay(t('firstrun.trust.heading', { title: model.title }), width);
122
+ let path = wrapDisplay(model.project, width);
123
+ let reason = wrapWords(model.reason, width);
124
+ let summary = model.resourceSummary ? wrapWords(model.resourceSummary, width) : [];
125
+ let session = model.session ? wrapWords(model.session, width) : [];
126
+ let shown = Math.min(model.resources.length, 8);
127
+ let nestedList = model.nestedRepositories.length > 0;
128
+ let nestedCount = false;
129
+ let blank = true;
130
+ let keys = true;
131
+ const more = () => shown < model.resources.length && shown > 0 ? 1 : 0;
132
+ const nestedRows = () => nestedList ? 1 + model.nestedRepositories.length : nestedCount ? 1 : 0;
133
+ const total = () => 1 + path.length + reason.length + summary.length + shown + more() + nestedRows() + session.length + (blank ? 1 : 0) + choiceRows.length + (keys ? 1 : 0);
134
+ /* Each step gives up one thing and says whether it could. */
135
+ const steps = [
136
+ () => { if (!blank)
137
+ return false; blank = false; return true; },
138
+ () => { if (!keys)
139
+ return false; keys = false; return true; },
140
+ () => { if (!nestedList)
141
+ return false; nestedList = false; nestedCount = true; return true; },
142
+ () => { if (path.length <= 1)
143
+ return false; path = [truncateMiddle(model.project, width)]; return true; },
144
+ () => { if (summary.length <= 1)
145
+ return false; summary = oneRow(summary, width); return true; },
146
+ () => { if (reason.length <= 1)
147
+ return false; reason = oneRow(reason, width); return true; },
148
+ () => { if (shown === 0)
149
+ return false; shown -= 1; return true; },
150
+ () => { if (session.length <= 1)
151
+ return false; session = oneRow(session, width); return true; },
152
+ () => { if (!nestedCount)
153
+ return false; nestedCount = false; return true; },
154
+ () => { if (!summary.length)
155
+ return false; summary = []; return true; },
156
+ () => { if (!reason.length)
157
+ return false; reason = []; return true; },
158
+ () => { if (!session.length)
159
+ return false; session = []; return true; },
160
+ () => { if (!path.length)
161
+ return false; path = []; return true; },
162
+ ];
163
+ for (const step of steps) {
164
+ while (total() > budget && step()) { /* keep giving up this part */ }
165
+ if (total() <= budget)
166
+ break;
167
+ }
168
+ const out = [{ kind: 'title', text: title }];
169
+ for (const row of path)
170
+ out.push({ kind: 'path', text: row });
171
+ for (const row of reason)
172
+ out.push({ kind: 'text', text: row });
173
+ for (const row of summary)
174
+ out.push({ kind: 'dim', text: row });
175
+ for (const resource of model.resources.slice(0, shown))
176
+ out.push({ kind: 'dim', text: truncateMiddle(` - ${resource}`, width) });
177
+ if (more())
178
+ out.push({ kind: 'dim', text: truncateDisplay(` - ${t('firstrun.trust.more', { count: model.resources.length - shown })}`, width) });
179
+ if (nestedList) {
180
+ out.push({ kind: 'warning', text: truncateDisplay(t('firstrun.trust.nested'), width) });
181
+ for (const nested of model.nestedRepositories)
182
+ out.push({ kind: 'text', text: truncateMiddle(` - ${nested}`, width) });
183
+ }
184
+ else if (nestedCount)
185
+ out.push({ kind: 'warning', text: truncateDisplay(tn('firstrun.trust.nestedCount', model.nestedRepositories.length), width) });
186
+ for (const row of session)
187
+ out.push({ kind: 'warning', text: row });
188
+ if (blank)
189
+ out.push({ kind: 'blank', text: '' });
190
+ out.push(...choiceRows);
191
+ if (keys)
192
+ out.push({ kind: 'keys', text: truncateDisplay(t('firstrun.trust.keys'), width) });
193
+ return out;
194
+ }
195
+ export async function runProjectTrustPrompt(input, io = {}) {
196
+ const stdin = io.stdin ?? process.stdin;
197
+ const stdout = io.stdout ?? process.stdout;
198
+ const env = io.env ?? process.env;
199
+ const clock = io.clock ?? Date.now;
200
+ if (!stdin.isTTY || !stdout.isTTY)
201
+ return 'cancel';
202
+ let React, Ink;
203
+ try {
204
+ React = await import('react');
205
+ Ink = await import('ink');
206
+ }
207
+ catch {
208
+ throw new Error('TUI_DEPENDENCY_UNAVAILABLE');
209
+ }
210
+ const capabilities = detectTerminalCapabilities({ stdinIsTTY: true, stdoutIsTTY: true, color: input.color !== false, env });
211
+ const session = createTerminalSessionPlan({ capabilities, env });
212
+ const theme = liveTheme(capabilities, input.theme ?? 'calm').theme;
213
+ const model = projectTrustPromptModel(input);
214
+ const ids = model.choices.map(choice => choice.id);
215
+ const sizeStore = createTerminalSizeStore(stdout, { rows: 24, columns: 80 });
216
+ let settled = false;
217
+ let settle = () => { };
218
+ const choicePromise = new Promise(resolve => { settle = choice => { if (settled)
219
+ return; settled = true; resolve(choice); }; });
220
+ /* The grace runs from when the question is drawn: set now, and again when the first frame is committed. */
221
+ const opened = { at: clock() };
222
+ function Prompt() {
223
+ const size = React.useSyncExternalStore(sizeStore.subscribe, sizeStore.getSnapshot, sizeStore.getSnapshot);
224
+ const [selected, setSelected] = React.useState(model.defaultSelection);
225
+ /* Read by the key handler synchronously: Ink's input handler may close over an older render. */
226
+ const selectedRef = React.useRef(model.defaultSelection);
227
+ const { exit } = Ink.useApp();
228
+ React.useEffect(() => { opened.at = clock(); }, []);
229
+ const choose = (choice) => { selectedRef.current = choice; setSelected(choice); };
230
+ const finish = (choice) => { settle(choice); exit(); };
231
+ Ink.useInput((value, key) => {
232
+ if (clock() - opened.at < PROJECT_TRUST_GRACE_MS)
233
+ return;
234
+ const lower = typeof value === 'string' ? value.toLowerCase() : '';
235
+ if (key.escape || (key.ctrl && lower === 'c')) {
236
+ finish('cancel');
237
+ return;
238
+ }
239
+ if (key.return) {
240
+ finish(selectedRef.current);
241
+ return;
242
+ }
243
+ if (key.upArrow || key.leftArrow || (key.tab && key.shift)) {
244
+ choose(nextProjectTrustSelection(selectedRef.current, 'previous', ids));
245
+ return;
246
+ }
247
+ if (key.downArrow || key.rightArrow) {
248
+ choose(nextProjectTrustSelection(selectedRef.current, 'next', ids));
249
+ return;
250
+ }
251
+ if (key.tab) {
252
+ choose(nextProjectTrustSelection(selectedRef.current, 'toggle', ids));
253
+ return;
254
+ }
255
+ if (key.ctrl || key.meta)
256
+ return;
257
+ const shortcut = model.choices.find(choice => choice.shortcut === lower);
258
+ if (shortcut)
259
+ choose(shortcut.id);
260
+ });
261
+ const rows = layoutProjectTrustPrompt(model, { columns: size.columns, rows: size.rows, selected });
262
+ const style = (row) => row.kind === 'title' ? { bold: true, color: theme.colors.warning }
263
+ : row.kind === 'choice' ? (row.selected ? { bold: true, color: theme.colors.accent } : {})
264
+ : row.kind === 'warning' ? { color: theme.colors.warning }
265
+ : row.kind === 'dim' || row.kind === 'keys' ? { dimColor: true } : {};
266
+ const children = rows.map((row, index) => React.createElement(Ink.Text, { key: `row-${index}`, wrap: 'truncate', ...style(row) }, row.text || ' '));
267
+ const box = React.createElement(Ink.Box, { flexDirection: 'column', borderStyle: capabilities.unicode ? 'round' : 'classic', ...(theme.colors.warning ? { borderColor: theme.colors.warning } : {}), paddingX: 1, flexShrink: 0 }, ...children);
268
+ const frame = terminalFrameProps({ session, ...(theme.colors.background ? { background: theme.colors.background } : {}), rows: size.rows, columns: size.columns });
269
+ return React.createElement(Ink.Box, frame, box);
270
+ }
271
+ const rendered = Ink.render(React.createElement(Prompt), {
272
+ stdin, stdout, exitOnCtrlC: false,
273
+ interactive: session.mode === 'plain' ? false : capabilities.interactive,
274
+ alternateScreen: session.alternateScreen,
275
+ });
276
+ const exitPromise = rendered.waitUntilExit().then(() => { if (!settled)
277
+ settle('cancel'); }, () => { if (!settled)
278
+ settle('cancel'); });
279
+ try {
280
+ const choice = await choicePromise;
281
+ await exitPromise;
282
+ return choice;
283
+ }
284
+ finally {
285
+ sizeStore.dispose();
286
+ }
287
+ }