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,54 @@
1
+ /*
2
+ * R5b D1 (owner decision Q-R5-12): `/export [md|json|jsonl] [file]` in the TUI. Markdown by default (the readable form of
3
+ * sessions/export-format.ts: the person's messages, TALOS's answers, tools in English); JSON and JSONL are the kernel's own
4
+ * rows. With no file, `talos-session-<first 8 of the id>-<YYYYMMDD-HHMM>.<ext>` in the project folder; a relative file is
5
+ * taken from the project folder. Secrets are redacted in every format.
6
+ */
7
+ import { mkdir, writeFile } from 'node:fs/promises';
8
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
9
+ import { createRedactor } from "../diagnostics/redact.js";
10
+ import { exportMarkdown } from "../sessions/export-format.js";
11
+ const FORMATS = ['md', 'json', 'jsonl'];
12
+ /** `/export` arguments: a format word and/or a file, in any order; `format:null` for a word that is not a format or file. */
13
+ export function parseExportArgs(args) {
14
+ let format = null, file = null;
15
+ for (const arg of args) {
16
+ const word = arg.toLowerCase();
17
+ if (FORMATS.includes(word) && format === null) {
18
+ format = word;
19
+ continue;
20
+ }
21
+ if (/[\\/.]/u.test(arg) && file === null) {
22
+ file = arg;
23
+ continue;
24
+ }
25
+ return { format: null, file: null };
26
+ }
27
+ if (format === null && file) {
28
+ const ext = /\.(md|json|jsonl)$/iu.exec(file)?.[1]?.toLowerCase();
29
+ format = ext ?? 'md';
30
+ }
31
+ return { format: format ?? 'md', file };
32
+ }
33
+ const pad = (n) => String(n).padStart(2, '0');
34
+ function defaultName(sessionId, format, now) {
35
+ const stamp = `${now.getUTCFullYear()}${pad(now.getUTCMonth() + 1)}${pad(now.getUTCDate())}-${pad(now.getUTCHours())}${pad(now.getUTCMinutes())}`;
36
+ return `talos-session-${sessionId.slice(0, 8)}-${stamp}.${format}`;
37
+ }
38
+ export async function exportSession({ runtime, sessionId, projectRoot, args, now = new Date(), secrets }) {
39
+ const { format, file } = parseExportArgs(args);
40
+ if (!format)
41
+ throw Object.assign(new Error('EXPORT_USAGE'), { code: 'EXPORT_USAGE' });
42
+ const payload = sessionId ? runtime.export(sessionId) : null;
43
+ if (!sessionId || !payload)
44
+ throw Object.assign(new Error('SESSION_NOT_FOUND'), { code: 'SESSION_NOT_FOUND' });
45
+ const redactor = createRedactor(secrets);
46
+ const events = Array.isArray(payload.eventi) ? payload.eventi : [];
47
+ const body = format === 'md' ? exportMarkdown(payload, { secrets })
48
+ : format === 'json' ? `${JSON.stringify(redactor.value(payload), null, 2)}\n`
49
+ : events.map(event => JSON.stringify(redactor.value(event))).join('\n') + (events.length ? '\n' : '');
50
+ const target = file ? (isAbsolute(file) ? file : resolve(projectRoot, file)) : join(projectRoot, defaultName(sessionId, format, now));
51
+ await mkdir(dirname(target), { recursive: true });
52
+ await writeFile(target, body, 'utf8');
53
+ return { path: target, format };
54
+ }
@@ -0,0 +1,35 @@
1
+ import { routeInput } from "./input-router.js";
2
+ /*
3
+ * R4 Q-R4-4 (owner, 2026-09-24): a key pressed while the boot is on screen.
4
+ * - Space, Enter and Esc only skip: the person asked for the composer, not for a space, a send or an interrupt. Measured before
5
+ * R4: the key that skipped the boot was also typed into the composer (`│> x`, gate §0.5).
6
+ * - A printable key skips AND stays as type-ahead, also before the runtime is ready (Codex keeps startup drafts, openai/codex
7
+ * #46750, 2026-09-20). Once the boot is skipped and TALOS still waits for the runtime, a space is part of what is typed.
8
+ * - Ctrl+C and Ctrl+D always reach the exit rule (interrupt.ts): the way out is never blocked by the boot.
9
+ * - Any other key skips; it is routed only once the runtime is ready, as before R4 (an action before the runtime exists had
10
+ * nothing to act on). Enter while waiting sends nothing.
11
+ */
12
+ const CONTROL = /^[\x00-\x1f\x7f]/u;
13
+ function printable(ch, key) {
14
+ if (typeof ch !== 'string' || ch.length === 0 || CONTROL.test(ch))
15
+ return false;
16
+ return !key.ctrl && !key.meta && !key.return && !key.escape && !key.tab && !key.backspace && !key.delete
17
+ && !key.upArrow && !key.downArrow && !key.leftArrow && !key.rightArrow && !key.pageUp && !key.pageup && !key.pageDown && !key.pagedown && !key.home && !key.end;
18
+ }
19
+ export function decideShellInput(input) {
20
+ const { ch, key, focus, composerText, commandMenuOpen, trustCenterOpen, approvalExpanded, boot, keymap } = input;
21
+ const base = { skipBoot: false, closeTrustCenter: false, approvalScrollDelta: null, routed: null };
22
+ if (trustCenterOpen)
23
+ return { ...base, closeTrustCenter: Boolean(key.escape || key.return || ch.toLowerCase() === 'q') };
24
+ if (focus === 'approval' && approvalExpanded && (key.pageUp || key.pageup || key.pageDown || key.pagedown || key.upArrow || key.downArrow)) {
25
+ const delta = (key.pageDown || key.pagedown) ? 4 : (key.pageUp || key.pageup) ? -4 : key.downArrow ? 1 : -1;
26
+ return { ...base, approvalScrollDelta: delta };
27
+ }
28
+ const lower = typeof ch === 'string' ? ch.toLowerCase() : '';
29
+ const exitKey = Boolean(key.ctrl && (lower === 'c' || lower === 'd'));
30
+ const onlySkips = !boot.skipped && Boolean(key.return || key.escape || ch === ' ');
31
+ const bootActive = boot.phase !== 'ready' && boot.phase !== 'error';
32
+ const skipBoot = bootActive && !boot.skipped;
33
+ const forwarded = !bootActive || exitKey || (!onlySkips && (printable(ch, key) || boot.runtimeReady));
34
+ return { ...base, skipBoot, routed: forwarded ? routeInput({ ch, key, focus, composerText, commandMenuOpen, keymap }) : null };
35
+ }
@@ -0,0 +1,48 @@
1
+ import { assistantStreamView } from "./components/assistant-stream.js";
2
+ import { composerDisplay } from "./components/composer.js";
3
+ import { mainRosterLine, queueRosterLine, terminalShortcutStrip } from "./components/terminal-shell.js";
4
+ import { thinkingRowText } from "./components/thinking-row.js";
5
+ import { transcriptWindow } from "./components/transcript.js";
6
+ import { deriveLiveActivity } from "./live-activity.js";
7
+ import { queuedActionPreview } from "./shell-model.js";
8
+ import { statusBar } from "./status-bar.js";
9
+ export function shellDimensions(terminalRows, terminalColumns) {
10
+ const rows = Math.max(8, Number(terminalRows) - 15);
11
+ const columns = Math.max(20, Number(terminalColumns));
12
+ return { rows, columns, desiredPageSize: Math.max(4, Math.floor(rows / 2)) };
13
+ }
14
+ export function pickerWindow(rows, selected, size = 10) {
15
+ const start = Math.max(0, Math.min(Math.max(0, rows.length - size), selected - Math.floor(size / 2)));
16
+ return { start, rows: rows.slice(start, start + size) };
17
+ }
18
+ /*
19
+ * The chrome of one frame, Ink-free. R5a (lane S):
20
+ * - S1 (Q-R5-2): the last row is the status row — `Full access · No sandbox · model X · ↑in ↓out · ctx … · ~$… · cmp N ·
21
+ * N new` — from lane R's usage snapshot. Before R5a the footer held the workspace path and `session <uuid>` and the
22
+ * roster's right edge a context label that the context status had replaced (gate finding 1, review D-14).
23
+ * - S9 (D-14): the path stays in the header only, the session id is in /status and in the resume line at exit, the main
24
+ * roster row no longer repeats the model, and the activity row is shown only when it has something to say.
25
+ * - S5 (Q-R5-7): while a turn runs, the shortcut strip says what Enter does in the busy-input mode.
26
+ */
27
+ export function deriveShellLayout(input) {
28
+ const { state, appState, viewport, queuedActions, busySince, bullet, branch } = input;
29
+ const { rows, columns, desiredPageSize } = shellDimensions(input.terminalRows, input.terminalColumns);
30
+ const messages = transcriptWindow(state.messages, viewport);
31
+ const live = assistantStreamView(state.streamingAssistant);
32
+ const reasoning = thinkingRowText(state.reasoning?.text ?? '', appState.reasoningVisible);
33
+ const bar = statusBar({ snapshot: input.snapshot ?? null, model: state.status.model, width: columns, safety: input.safety ?? { fullAccess: false, noSandbox: false }, unseen: viewport.unseen });
34
+ const activity = deriveLiveActivity(state, busySince);
35
+ const visual = composerDisplay(appState.editor);
36
+ /* While the approval dialog is open Enter and Esc answer the dialog, so the busy line would be wrong: the R3 strip stays. */
37
+ const busyInput = input.busyInput && !state.waitingForPerson ? input.busyInput : null;
38
+ const shortcutLine = terminalShortcutStrip({ mode: state.status.permissionMode, running: state.running, queueCount: queuedActions.length, width: columns, ...(busyInput ? { busyInput } : {}) });
39
+ const rosterLine = mainRosterLine({ kind: activity.kind, model: null, width: columns, marker: bullet });
40
+ const queueLine = queueRosterLine({ count: queuedActions.length, preview: queuedActions[0] ? queuedActionPreview(queuedActions[0].text) : null, width: columns, marker: branch });
41
+ return {
42
+ rows, columns, desiredPageSize, messages, live, reasoning, statusBar: bar, footerLine: bar.text, activity,
43
+ showActivity: activity.kind !== 'idle' || input.exitHint === true,
44
+ visual, shortcutLine, rosterLine, queueLine,
45
+ toolRowsLimit: Math.max(2, Math.floor(rows / 3)),
46
+ pickerRowsLimit: Math.max(4, Math.min(12, rows - 4)),
47
+ };
48
+ }
@@ -0,0 +1,98 @@
1
+ import { t } from "../i18n/index.js";
2
+ import { formatPermissionSimulation, simulatePermission } from "../security/permission-explanation.js";
3
+ import { createEditorState, replaceRange } from "./editor.js";
4
+ import { permissionModeLabel } from "./components/terminal-shell.js";
5
+ import { closeFocus, createFocusState, focusForOverlay, openFocus } from "./focus-manager.js";
6
+ import { filterItems } from "./selection-list.js";
7
+ import { parseSlashCommand } from "./slash-commands.js";
8
+ import { statusFactLines } from "./status-view.js";
9
+ const QUEUE_TERMINAL_UNSAFE = /[\x00-\x1f\x7f-\x9f\u2028\u2029\p{Bidi_Control}\p{Default_Ignorable_Code_Point}]/gu;
10
+ export const MODEL_ID = /^[A-Za-z0-9._-]+:[A-Za-z0-9][A-Za-z0-9._/:@-]*$/u;
11
+ /* R2: sentinel values compared by identity AND shown on the status row, so they come from the dictionary once. */
12
+ export const NO_PROVIDER_MODEL = t('screen.model.noneProvider');
13
+ export const NO_MODEL = t('screen.model.noneModel');
14
+ export function queuedActionPreview(value) {
15
+ return String(value).replace(QUEUE_TERMINAL_UNSAFE, character => {
16
+ if (character === '\n' || character === '\r' || character === '\t')
17
+ return ' ';
18
+ const code = character.codePointAt(0);
19
+ return code > 0xffff ? `\\u{${code.toString(16)}}` : `\\u${code.toString(16).padStart(4, '0')}`;
20
+ }).replace(/\s{2,}/gu, ' ').trim();
21
+ }
22
+ export function openOverlay(state, overlay) {
23
+ return { ...state, overlay, focus: openFocus(state.focus, focusForOverlay(overlay.kind)) };
24
+ }
25
+ export function createTuiAppState(editor = createEditorState('')) { return { editor, overlay: null, focus: createFocusState('composer'), reasoningVisible: false, expandedTools: false, redrawNonce: 0 }; }
26
+ export function closeOverlay(state) { return { ...state, overlay: null, focus: closeFocus(state.focus) }; }
27
+ export function reduceAppAction(state, action) {
28
+ if (action.type === 'close-overlay')
29
+ return closeOverlay(state);
30
+ if (action.action === 'model-picker')
31
+ return openOverlay(state, { kind: 'model' });
32
+ if (action.action === 'provider-picker')
33
+ return openOverlay(state, { kind: 'provider' });
34
+ if (action.action === 'help')
35
+ return openOverlay(state, { kind: 'help' });
36
+ if (action.action === 'reasoning-toggle')
37
+ return { ...state, reasoningVisible: !state.reasoningVisible };
38
+ if (action.action === 'tool-details')
39
+ return { ...state, expandedTools: !state.expandedTools };
40
+ if (action.action === 'redraw')
41
+ return { ...state, redrawNonce: state.redrawNonce + 1 };
42
+ if (action.action === 'overlay-close' || action.action === 'picker-cancel')
43
+ return closeOverlay(state);
44
+ return state;
45
+ }
46
+ export function slashCommandIntent(text) {
47
+ const parsed = parseSlashCommand(text);
48
+ if (parsed.name === 'model')
49
+ return parsed.args[0] ? { kind: 'select-model', id: parsed.args[0] } : { kind: 'open-overlay', overlay: { kind: 'model' } };
50
+ if (parsed.name === 'help')
51
+ return { kind: 'open-overlay', overlay: { kind: 'help' } };
52
+ if (parsed.name === 'provider')
53
+ return { kind: 'open-overlay', overlay: { kind: 'provider' } };
54
+ if (parsed.name === 'resume' || parsed.name === 'fork')
55
+ return parsed.args[0] ? { kind: 'session-id', action: parsed.name, id: parsed.args[0] } : { kind: 'open-overlay', overlay: { kind: 'session', action: parsed.name } };
56
+ if (parsed.name === 'redraw')
57
+ return { kind: 'key-action', action: 'redraw' };
58
+ return null;
59
+ }
60
+ export function togglePlanMode(current, base) { return current === 'plan' ? (base === 'plan' ? 'default' : base) : 'plan'; }
61
+ export function permissionDryRunSlash(args, rules, projectRoot) {
62
+ if (args[0] !== 'dry-run')
63
+ return null;
64
+ const command = args.slice(1).join(' ').trim();
65
+ if (!command)
66
+ return t('screen.usage.permissionsDryRun');
67
+ return formatPermissionSimulation(simulatePermission({ rules, action: { tool: 'Bash', command }, projectRoot })).trimEnd();
68
+ }
69
+ const SLASH_CLI_FAMILY = { mcp: 'mcp', hooks: 'hook', plugins: 'plugin', doctor: 'doctor', memory: 'memory', notes: 'notes', tasks: 'tasks', library: 'library', research: 'research', automations: 'automation', forge: 'forge' };
70
+ export function slashCommandToCliArgs(name, args) { const family = SLASH_CLI_FAMILY[name]; if (!family)
71
+ return null; const out = [family, ...args]; if (args.length === 0 && name !== 'doctor')
72
+ out.push('list'); return out; }
73
+ export function pickerMove(action) {
74
+ if (action === 'picker-up')
75
+ return 'up';
76
+ if (action === 'picker-down')
77
+ return 'down';
78
+ if (action === 'picker-page-up')
79
+ return 'page-up';
80
+ if (action === 'picker-page-down')
81
+ return 'page-down';
82
+ if (action === 'picker-home')
83
+ return 'home';
84
+ if (action === 'picker-end')
85
+ return 'end';
86
+ return null;
87
+ }
88
+ export function modelItems(rows, query) { return filterItems(rows.map(row => ({ id: row.id, label: row.name, searchText: `${row.id} ${row.name} ${row.provider}`, group: row.provider, value: row })), query).map(item => item.value); }
89
+ export function providerItems(rows, query) { return filterItems(rows.map(row => ({ id: row.id, label: row.label, searchText: `${row.id} ${row.label} ${row.description}`, group: row.local ? 'local' : 'cloud', value: row })), query).map(item => item.value); }
90
+ export function lastProjectToken(text) { const match = /(?:^|\s)(@[^\s]*)$/u.exec(text); return match?.[1] ?? null; }
91
+ /* R4: the token is replaced in place, so a collapsed paste earlier in the draft stays collapsed and Ctrl+Z undoes it. */
92
+ export function replaceLastProjectToken(state, token, value) { const start = state.text.lastIndexOf(token); return start < 0 ? state : replaceRange(state, start, start + token.length, value); }
93
+ /* R4 Q-R4-13: /status as a small titled block, the permission mode in words (Q-R4-12), an honest line with no session. */
94
+ /* R5b D7 (Q-R5-17): with `facts`, the R4 block is followed by the sandbox, rules, work and usage lines (status-view.ts). */
95
+ export function sessionStatusText({ session, running, model, mode, tools, facts }) {
96
+ const block = t('screen.status.block', { session: session ?? t('screen.status.noSession'), running: t(running ? 'screen.word.yes' : 'screen.word.no'), model, mode: t('screen.status.mode', { mode: permissionModeLabel(mode) }), tools });
97
+ return facts ? [block, ...statusFactLines(facts)].join('\n') : block;
98
+ }
@@ -0,0 +1,80 @@
1
+ import { t } from "../i18n/index.js";
2
+ const SLASH_COMMAND_META = [
3
+ { name: 'help', usage: '' },
4
+ { name: 'provider', usage: '' },
5
+ { name: 'model', usage: '[provider:model]' },
6
+ /* R5a (owner 2026-09-24): the reasoning effort of the current model, as a line of nodes. */
7
+ { name: 'effort', usage: '[level]' },
8
+ { name: 'queue', usage: '[show|clear|run]' },
9
+ /* R5a (Q-R5-7, Q-R5-19): what Enter does while TALOS works. */
10
+ { name: 'busy', usage: '[queue|steer|interrupt]' },
11
+ { name: 'permissions', usage: t('screen.slash.permissionsUsage') },
12
+ { name: 'plan', usage: '' },
13
+ { name: 'diff', usage: '' },
14
+ { name: 'status', usage: '' },
15
+ { name: 'context', usage: '' },
16
+ /* R5a (Q-R5-2, Q-R5-8, Q-R5-19): tokens, window, cost and compactions; a view, it runs at once during a turn. */
17
+ { name: 'usage', usage: '' },
18
+ { name: 'compact', usage: '' },
19
+ { name: 'clear', usage: '' },
20
+ { name: 'redraw', usage: '' },
21
+ { name: 'resume', usage: '[session-id]' },
22
+ { name: 'fork', usage: '[session-id]' },
23
+ /* R5b D1 (Q-R5-12): this conversation to a file. */
24
+ { name: 'export', usage: '[md|json|jsonl] [file]' },
25
+ { name: 'rename', usage: '<name>' },
26
+ /* R5b D4 (Q-R5-14): the ten product themes and mono, previewed live. */
27
+ { name: 'theme', usage: '[name]' },
28
+ /* R5b D3 (Q-R5-13): this conversation's checkpoints, with their files; /rewind and /checkpoint are the same command. */
29
+ { name: 'undo', usage: '' },
30
+ /* R5b (Q-R5-19): send the last message again; copy an answer (Hermes /retry, Claude Code and Hermes /copy). */
31
+ { name: 'retry', usage: '' },
32
+ { name: 'copy', usage: '[n]' },
33
+ /* R5b (owner 2026-09-25): the messages you sent in this project, to put one back in the composer. */
34
+ { name: 'history', usage: '' },
35
+ { name: 'mcp', usage: '[list|test|trust|untrust] [server-id]' },
36
+ { name: 'hooks', usage: '[list|dry-run|history|trust|untrust|quarantine|release] [hook-id] [args...]' },
37
+ { name: 'plugins', usage: '[list|install|remove|trust|untrust|quarantine|release] [plugin-id] [args...]' },
38
+ { name: 'doctor', usage: '[args...]' },
39
+ { name: 'memory', usage: '[args...]' },
40
+ { name: 'notes', usage: '[args...]' },
41
+ { name: 'tasks', usage: '[args...]' },
42
+ { name: 'library', usage: '[args...]' },
43
+ { name: 'research', usage: '[args...]' },
44
+ { name: 'automations', usage: '[args...]' },
45
+ { name: 'forge', usage: '[args...]' },
46
+ { name: 'exit', usage: '' },
47
+ ];
48
+ /* R2 (Q-R2-1, gate E1): the descriptions are dictionary words; a later locale overlays the dictionary, not this file. */
49
+ export function slashCommandDescription(name) { return t(`screen.slash.${name}`); }
50
+ export const SLASH_COMMANDS = SLASH_COMMAND_META.map(row => ({
51
+ name: row.name,
52
+ usage: row.usage,
53
+ description: slashCommandDescription(row.name),
54
+ }));
55
+ const SLASH_GROUPS = ['session', 'turn', 'view', 'work', 'extensions'];
56
+ const GROUP_OF = {
57
+ resume: 'session', fork: 'session', rename: 'session', export: 'session', undo: 'session', history: 'session', clear: 'session', compact: 'session', exit: 'session',
58
+ provider: 'turn', model: 'turn', effort: 'turn', busy: 'turn', queue: 'turn', retry: 'turn', plan: 'turn', permissions: 'turn',
59
+ help: 'view', status: 'view', context: 'view', usage: 'view', diff: 'view', copy: 'view', theme: 'view', redraw: 'view',
60
+ memory: 'work', notes: 'work', tasks: 'work', library: 'work', research: 'work', automations: 'work', forge: 'work',
61
+ mcp: 'extensions', hooks: 'extensions', plugins: 'extensions', doctor: 'extensions',
62
+ };
63
+ export function slashCommandGroup(name) { return GROUP_OF[name]; }
64
+ export function slashCommandHelp() {
65
+ return SLASH_GROUPS.map(group => t(`screen.help.group.${group}`) + '\n' + SLASH_COMMANDS.filter(command => GROUP_OF[command.name] === group).map(command => {
66
+ const usage = command.usage ? ' ' + command.usage : '';
67
+ return ' /' + command.name + usage + ' — ' + slashCommandDescription(command.name);
68
+ }).join('\n')).join('\n\n');
69
+ }
70
+ function distance(a, b) { const d = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0)); for (let i = 0; i <= a.length; i++)
71
+ d[i][0] = i; for (let j = 0; j <= b.length; j++)
72
+ d[0][j] = j; for (let i = 1; i <= a.length; i++)
73
+ for (let j = 1; j <= b.length; j++)
74
+ d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)); return d[a.length][b.length]; }
75
+ /* R5b D3 (Q-R5-13): other names for a command (Claude Code's /rewind aliases); the parsed name is the command's own. */
76
+ const SLASH_ALIASES = Object.freeze({ rewind: 'undo', checkpoint: 'undo' });
77
+ export function parseSlashCommand(text) { const parts = text.trim().replace(/^\//u, '').split(/\s+/u).filter(Boolean); const typed = parts.shift() ?? ''; const name = SLASH_ALIASES[typed] ?? typed; const known = SLASH_COMMANDS.some(x => x.name === name); const suggestions = known ? [] : SLASH_COMMANDS.map(x => ({ n: x.name, d: distance(name, x.name) })).filter(x => x.d <= Math.max(2, Math.floor(name.length / 3))).sort((a, b) => a.d - b.d || a.n.localeCompare(b.n)).slice(0, 3).map(x => x.n); return { name, args: parts, suggestions }; }
78
+ export function completeSlashCommand(text) { if (!text.startsWith('/'))
79
+ return []; const raw = text.slice(1); if (/\s/u.test(raw))
80
+ return []; return SLASH_COMMANDS.map(x => '/' + x.name).filter(x => x.startsWith('/' + raw)).sort((a, b) => a.localeCompare(b)); }
@@ -0,0 +1,151 @@
1
+ import { t, tn } from "../i18n/index.js";
2
+ import { toolDisplay } from "./tool-display.js";
3
+ import { createTranscriptModel, reduceTranscriptEvent } from "./transcript-model.js";
4
+ const MAX_RENDER_MESSAGES = 512;
5
+ const MAX_RENDER_TOOLS = 256;
6
+ const MAX_RENDER_WARNINGS = 128;
7
+ const MAX_MESSAGE_CHARS = 256 * 1024;
8
+ const MAX_TOOL_SUMMARY_CHARS = 8 * 1024;
9
+ const MAX_WARNING_CHARS = 2048;
10
+ function tail(value, limit) { if (value.length <= limit)
11
+ return value; return `${t('screen.state.truncated')}\n${value.slice(-(limit - 22))}`; }
12
+ export function initialTuiState(x) { return { sessionId: null, running: false, sentAt: null, waitingForPerson: false, lastTurn: null, turnStartedAt: null, retry: null, compactions: 0, messages: [], streamingAssistant: null, reasoning: null, tools: [], approval: null, usage: null, warnings: [], transcript: createTranscriptModel(), status: { model: x.model, permissionMode: x.permissionMode, branch: null, contextLabel: null } }; }
13
+ function finalizeStreaming(state, messageId) {
14
+ const live = state.streamingAssistant;
15
+ if (!live || live.id !== messageId)
16
+ return state;
17
+ const base = state.messages.length >= MAX_RENDER_MESSAGES ? state.messages.slice(-(MAX_RENDER_MESSAGES - 1)) : state.messages;
18
+ return { ...state, streamingAssistant: null, messages: [...base, { id: live.id, role: 'assistant', text: tail(live.text, MAX_MESSAGE_CHARS) }] };
19
+ }
20
+ function endTurn(state, outcome) {
21
+ return { sentAt: null, waitingForPerson: false, retry: null, lastTurn: { outcome, durationMs: state.turnStartedAt === null ? (state.lastTurn?.durationMs ?? null) : Math.max(0, Date.now() - state.turnStartedAt) }, turnStartedAt: null };
22
+ }
23
+ function activeToolStatus(status) { return status === 'queued' || status === 'approval' || status === 'running' || status === 'streaming'; }
24
+ function closeRunningTools(state, status = 'cancelled') { return state.tools.map(row => activeToolStatus(row.status) ? { ...row, status, endedAt: row.endedAt ?? Date.now() } : row); }
25
+ const whole = (value, fallback = null) => typeof value === 'number' && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
26
+ /* The model answered again: a retry, if any, is over (message, reasoning or a tool call). */
27
+ const PROGRESS = new Set(['message.started', 'message.delta', 'reasoning.started', 'reasoning.delta', 'tool.started']);
28
+ export function reduceTuiEvent(state, event) {
29
+ state = { ...state, transcript: reduceTranscriptEvent(state.transcript, event) };
30
+ if (event.type === 'provider.attempt') {
31
+ const attempt = whole(event.attempt, 1), maxAttempts = Math.max(attempt, whole(event.maxAttempts, attempt));
32
+ return { ...state, retry: { attempt, maxAttempts, status: whole(event.status), retryAfterMs: whole(event.retryAfterMs), waitMs: whole(event.waitMs), at: Date.now() } };
33
+ }
34
+ if (state.retry && PROGRESS.has(event.type))
35
+ state = { ...state, retry: null };
36
+ if (event.type === 'context.compacted')
37
+ return { ...state, compactions: (state.compactions ?? 0) + 1 };
38
+ if (event.type === 'run.started')
39
+ return { ...state, sessionId: event.sessionId || state.sessionId, running: true, sentAt: null, lastTurn: null, turnStartedAt: Date.now(), retry: null };
40
+ if (event.type === 'message.started') {
41
+ if (event.role !== 'assistant')
42
+ return state;
43
+ const live = state.streamingAssistant, next = live && live.id !== event.messageId ? finalizeStreaming(state, live.id) : state;
44
+ return next.streamingAssistant?.id === event.messageId ? next : { ...next, streamingAssistant: { id: event.messageId, text: '' } };
45
+ }
46
+ if (event.type === 'message.delta') {
47
+ const live = state.streamingAssistant;
48
+ if (live?.id === event.messageId)
49
+ return { ...state, streamingAssistant: { ...live, text: tail(live.text + event.delta, MAX_MESSAGE_CHARS) } };
50
+ const next = live ? finalizeStreaming(state, live.id) : state;
51
+ return { ...next, streamingAssistant: { id: event.messageId, text: tail(event.delta, MAX_MESSAGE_CHARS) } };
52
+ }
53
+ if (event.type === 'message.end')
54
+ return finalizeStreaming(state, event.messageId);
55
+ if (event.type === 'reasoning.started')
56
+ return state.reasoning?.id === event.messageId ? state : { ...state, reasoning: { id: event.messageId, text: '' } };
57
+ if (event.type === 'reasoning.delta') {
58
+ const current = state.reasoning;
59
+ return { ...state, reasoning: current?.id === event.messageId ? { ...current, text: tail(current.text + event.delta, MAX_MESSAGE_CHARS) } : { id: event.messageId, text: tail(event.delta, MAX_MESSAGE_CHARS) } };
60
+ }
61
+ if (event.type === 'reasoning.end')
62
+ return state.reasoning?.id === event.messageId ? { ...state, reasoning: null } : state;
63
+ if (event.type === 'tool.started') {
64
+ const row = { id: event.toolCallId, name: event.toolName, status: 'queued', argsText: '', liveOutput: '', finalOutput: '', startedAt: Date.now(), endedAt: null, outcome: null };
65
+ const existing = state.tools.findIndex(x => x.id === event.toolCallId);
66
+ if (existing >= 0) {
67
+ const tools = state.tools.slice();
68
+ tools[existing] = row;
69
+ return { ...state, tools };
70
+ }
71
+ return { ...state, tools: [...state.tools.slice(-(MAX_RENDER_TOOLS - 1)), row] };
72
+ }
73
+ if (event.type === 'tool.args')
74
+ return { ...state, tools: state.tools.map(row => row.id === event.toolCallId ? { ...row, argsText: tail(row.argsText + event.delta, MAX_TOOL_SUMMARY_CHARS) } : row) };
75
+ if (event.type === 'tool.output')
76
+ return { ...state, tools: state.tools.map(row => row.id === event.toolCallId && activeToolStatus(row.status) ? { ...row, status: 'streaming', liveOutput: tail(row.liveOutput + event.delta, MAX_TOOL_SUMMARY_CHARS) } : row) };
77
+ if (event.type === 'tool.completed')
78
+ return { ...state, tools: state.tools.map(row => row.id === event.toolCallId ? { ...row, status: event.outcome?.status ?? 'completed', finalOutput: tail(event.content, MAX_TOOL_SUMMARY_CHARS), endedAt: Date.now(), outcome: event.outcome ?? null } : row) };
79
+ if (event.type === 'approval.required') {
80
+ /* R2 (gate E3): the tool is named by lane T's display table, never by its kernel name (`scrivi`, `shell`). */
81
+ const p = event.payload;
82
+ const tool = p.tool ?? p.tipo;
83
+ const label = tool ? toolDisplay(String(tool)).label : t('screen.approval.label');
84
+ const detail = String(p.command ?? p.comando ?? p.resource ?? p.percorso ?? p.path ?? '');
85
+ const toolCallId = event.toolCallId ?? null;
86
+ return { ...state, tools: toolCallId ? state.tools.map(row => row.id === toolCallId && activeToolStatus(row.status) ? { ...row, status: 'approval' } : row) : state.tools, approval: { requestId: event.requestId, label, detail, payload: p, toolCallId } };
87
+ }
88
+ if (event.type === 'approval.resolved') {
89
+ const approval = state.approval;
90
+ if (!approval || approval.requestId !== event.requestId)
91
+ return state;
92
+ const tools = approval.toolCallId ? state.tools.map(row => {
93
+ if (row.id !== approval.toolCallId || !activeToolStatus(row.status))
94
+ return row;
95
+ if (event.approved === true)
96
+ return { ...row, status: 'running' };
97
+ if (event.approved === false)
98
+ return { ...row, status: 'failed', endedAt: row.endedAt ?? Date.now() };
99
+ return row;
100
+ }) : state.tools;
101
+ return { ...state, tools, approval: null };
102
+ }
103
+ if (event.type === 'usage.updated')
104
+ return { ...state, usage: event.value, status: { ...state.status, contextLabel: usageLabel(event.value) } };
105
+ if (event.type === 'warning')
106
+ return { ...state, warnings: [...state.warnings.slice(-(MAX_RENDER_WARNINGS - 1)), tail(event.message, MAX_WARNING_CHARS)] };
107
+ if (event.type === 'run.cancelled')
108
+ return { ...state, ...endTurn(state, 'interrupted'), running: false, approval: null, streamingAssistant: null, reasoning: null, tools: closeRunningTools(state, 'cancelled') };
109
+ if (event.type === 'run.failed')
110
+ return { ...state, ...endTurn(state, 'failed'), running: false, approval: null, streamingAssistant: null, reasoning: null, tools: closeRunningTools(state, 'failed'), warnings: [...state.warnings.slice(-(MAX_RENDER_WARNINGS - 1)), tail(event.message, MAX_WARNING_CHARS)] };
111
+ if (event.type === 'run.completed') {
112
+ const finalized = state.streamingAssistant ? finalizeStreaming(state, state.streamingAssistant.id) : state;
113
+ return { ...finalized, ...endTurn(state, 'done'), running: false, approval: null, reasoning: null };
114
+ }
115
+ return state;
116
+ }
117
+ function usageLabel(value) { if (!value || typeof value !== 'object')
118
+ return null; const row = value; const prompt = numberValue(row.prompt_tokens ?? row.promptTokens ?? row.input_tokens ?? row.inputTokens); const completion = numberValue(row.completion_tokens ?? row.completionTokens ?? row.output_tokens ?? row.outputTokens); if (prompt === null && completion === null)
119
+ return null; return `${prompt ?? 0}+${completion ?? 0}`; }
120
+ function numberValue(value) { return typeof value === 'number' && Number.isFinite(value) ? value : null; }
121
+ function objectValue(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : null; }
122
+ function firstMeasuredNumber(...values) { for (const value of values) {
123
+ const measured = numberValue(value);
124
+ if (measured !== null)
125
+ return measured;
126
+ } return null; }
127
+ export function reasoningUsageEvidence(value) {
128
+ const row = objectValue(value);
129
+ if (!row)
130
+ return { reasoningTokens: null, reasoningCostUsd: null };
131
+ const completion = objectValue(row.completion_tokens_details ?? row.completionTokensDetails);
132
+ const output = objectValue(row.output_tokens_details ?? row.outputTokensDetails);
133
+ const reasoning = objectValue(row.reasoning);
134
+ return {
135
+ reasoningTokens: firstMeasuredNumber(row.reasoning_tokens, row.reasoningTokens, completion?.reasoning_tokens, completion?.reasoningTokens, output?.reasoning_tokens, output?.reasoningTokens, reasoning?.tokens),
136
+ reasoningCostUsd: firstMeasuredNumber(row.reasoning_cost_usd, row.reasoningCostUsd, reasoning?.cost_usd, reasoning?.costUsd),
137
+ };
138
+ }
139
+ export function reasoningUsageLabel(value) {
140
+ const evidence = reasoningUsageEvidence(value);
141
+ if (evidence.reasoningTokens === null && evidence.reasoningCostUsd === null)
142
+ return t('screen.reasoning.notReported');
143
+ const parts = [];
144
+ parts.push(evidence.reasoningTokens === null ? t('screen.reasoning.tokensNotReported') : tn('screen.reasoning.tokens', evidence.reasoningTokens));
145
+ if (evidence.reasoningCostUsd !== null)
146
+ parts.push(t('screen.reasoning.cost', { cost: evidence.reasoningCostUsd }));
147
+ return parts.join(' · ');
148
+ }
149
+ // Temporary compatibility for the old composition root while Task 14 migrates it.
150
+ export const reduceCliEvent = reduceTuiEvent;
151
+ export function selectVisibleTranscript(s, { rows }) { return s.transcript.items.slice(-Math.max(1, rows * 3)); }
@@ -0,0 +1,125 @@
1
+ /*
2
+ * R5a lane S — S1, the ONE status row (owner decisions Q-R5-2 and Q-R5-5; gate E1, E2; R3 Q-R3-13):
3
+ *
4
+ * Full access · No sandbox · model X · ↑1.2k ↓340 · cached 800 · ctx 3.1k/131k 2% · ~$0.004 · cmp 1 · 3 new
5
+ *
6
+ * It is the Hermes form (`☤ model │ 12.4K/200K │ [bar] 6% │ $0.06`, hermes `website/docs/user-guide/cli.md:149-180`,
7
+ * 2026-09-23; TUI `ui-tui/src/components/appChrome.tsx:197-215, 525-547`, 2026-09-19) plus the two R3 safety labels, which
8
+ * lead the row as they led the old footer. Pure: the facts come from lane R's `usageSnapshot` (runtime/usage-snapshot.ts).
9
+ *
10
+ * E1, no invented number: a window nobody knows is `ctx ?` (never a percentage); an estimate carries `~`; a cost is shown
11
+ * only from a named price source (`~$`, list price), `local` for a local engine, `cost ?` otherwise; token counts nobody
12
+ * reported leave their segment out, one side unknown is `?`.
13
+ * E2, one row: when the row is too wide the segments go in a fixed order — cost, cache, ↑↓, the compaction count, the
14
+ * "N new" count, then the window figure (`ctx 3.1k/131k 2%` → `ctx 2%`), then the model is cut in the middle and last
15
+ * dropped. The context figure (`ctx N%` or `ctx ?`), `Full access` and `No sandbox` are never dropped (Q-R3-13).
16
+ * The percentage is toned as Hermes colours it (50 / 80 / 95, appChrome.tsx:319-334); the number is always there, so a
17
+ * monochrome screen loses nothing.
18
+ */
19
+ import { t } from "../i18n/index.js";
20
+ import { shellSafeText } from "./components/terminal-shell.js";
21
+ import { displayWidth, truncateDisplay, truncateMiddle } from "./text-width.js";
22
+ const SEPARATOR = ' · ';
23
+ /* A model id cut shorter than this says nothing; it is dropped instead. */
24
+ const MODEL_MIN = 8;
25
+ /* The drop order of E2 (first dropped first). `context-window` shortens the context segment; `model` cuts, then drops. */
26
+ const DROP_ORDER = ['cost', 'cached', 'tokens', 'compactions', 'unseen', 'context-window', 'model'];
27
+ const count = (value) => typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null;
28
+ /** 340 → `340`, 1,200 → `1.2k`, 131,072 → `131k`, 1,500,000 → `1.5m`. */
29
+ export function compactTokens(value) {
30
+ const n = Math.max(0, Math.round(value));
31
+ if (n < 1000)
32
+ return String(n);
33
+ if (n < 99_950)
34
+ return `${(n / 1000).toFixed(1).replace(/\.0$/u, '')}k`;
35
+ if (n < 999_500)
36
+ return `${Math.round(n / 1000)}k`;
37
+ return `${(n / 1_000_000).toFixed(1).replace(/\.0$/u, '')}m`;
38
+ }
39
+ /** A whole percentage; a used window under one percent reads `<1`, never `0` (that would say "empty"). */
40
+ export function percentText(percent) {
41
+ if (percent > 0 && percent < 1)
42
+ return '<1';
43
+ return String(Math.min(999, Math.round(percent)));
44
+ }
45
+ /** US dollars at list price: three decimals under a dollar (`0.004`), two above (`1.25`), `<0.001` for dust. */
46
+ export function costText(usd) {
47
+ if (usd > 0 && usd < 0.001)
48
+ return '<0.001';
49
+ return usd < 1 ? usd.toFixed(3) : usd.toFixed(2);
50
+ }
51
+ /** Hermes's thresholds for the context percentage (50 / 80 / 95). */
52
+ export function contextTone(percent) {
53
+ if (percent === null)
54
+ return 'normal';
55
+ return percent >= 95 ? 'critical' : percent >= 80 ? 'high' : percent >= 50 ? 'warn' : 'normal';
56
+ }
57
+ function contextSegment(snapshot) {
58
+ /* R5a wiring: `inputTokens` is the run's total; the context in use is lane R's `contextTokens` (the last request). */
59
+ const window = count(snapshot?.windowTokens), used = count(snapshot?.contextTokens), percent = count(snapshot?.percent);
60
+ if (window === null || window === 0 || percent === null)
61
+ return { id: 'context', text: t('screen.statusbar.contextUnknown'), tone: 'normal' };
62
+ const tilde = snapshot?.estimated ? '~' : '';
63
+ const short = t('screen.statusbar.contextShort', { percent: `${tilde}${percentText(percent)}` });
64
+ const text = used === null ? short : t('screen.statusbar.context', { used: `${tilde}${compactTokens(used)}`, window: compactTokens(window), percent: percentText(percent) });
65
+ return { id: 'context', text, short, tone: contextTone(percent) };
66
+ }
67
+ function tokensSegment(snapshot) {
68
+ const input = count(snapshot?.inputTokens), output = count(snapshot?.outputTokens);
69
+ if (input === null && output === null)
70
+ return null;
71
+ const tilde = snapshot?.estimated ? '~' : '', unknown = t('screen.statusbar.unknown');
72
+ return { id: 'tokens', text: t('screen.statusbar.tokens', { input: input === null ? unknown : `${tilde}${compactTokens(input)}`, output: output === null ? unknown : `${tilde}${compactTokens(output)}` }), tone: 'normal' };
73
+ }
74
+ function costSegment(snapshot) {
75
+ if (snapshot?.local)
76
+ return { id: 'cost', text: t('screen.statusbar.local'), tone: 'normal' };
77
+ const usd = count(snapshot?.costUsd), source = typeof snapshot?.costSource === 'string' && snapshot.costSource.trim() ? snapshot.costSource : null;
78
+ if (usd === null || source === null)
79
+ return { id: 'cost', text: t('screen.statusbar.costUnknown'), tone: 'normal' };
80
+ /* R5a wiring (Q-R5-5, E1): a cost the provider declared is reported, not estimated: no `~`. */
81
+ return { id: 'cost', text: t(source === 'provider-reported' ? 'screen.statusbar.costReported' : 'screen.statusbar.cost', { cost: costText(usd) }), tone: 'normal' };
82
+ }
83
+ function segmentsOf(input) {
84
+ const { snapshot } = input, model = shellSafeText(input.model);
85
+ const cached = count(snapshot?.cachedTokens), compactions = count(snapshot?.compactions), unseen = count(input.unseen);
86
+ const tokens = tokensSegment(snapshot);
87
+ return [
88
+ ...(input.safety.fullAccess ? [{ id: 'fullAccess', text: t('screen.footer.fullAccess'), tone: 'normal' }] : []),
89
+ ...(input.safety.noSandbox ? [{ id: 'noSandbox', text: t('screen.footer.noSandbox'), tone: 'normal' }] : []),
90
+ ...(model ? [{ id: 'model', text: t('screen.statusbar.model', { model }), tone: 'normal', model }] : []),
91
+ ...(tokens ? [tokens] : []),
92
+ ...(cached ? [{ id: 'cached', text: t('screen.statusbar.cached', { count: compactTokens(cached) }), tone: 'normal' }] : []),
93
+ contextSegment(snapshot),
94
+ costSegment(snapshot),
95
+ ...(compactions ? [{ id: 'compactions', text: t('screen.statusbar.compactions', { count: compactions }), tone: 'normal' }] : []),
96
+ ...(unseen ? [{ id: 'unseen', text: t('screen.footer.unseen', { count: unseen }), tone: 'normal' }] : []),
97
+ ];
98
+ }
99
+ const joined = (segments) => segments.map(segment => segment.text).join(SEPARATOR);
100
+ /** The status row at `width` columns: never wider, never more than one row, the protected segments always present. */
101
+ export function statusBar(input) {
102
+ const width = Math.max(0, Math.floor(Number(input.width) || 0));
103
+ let current = segmentsOf(input);
104
+ const fits = () => displayWidth(joined(current)) <= width;
105
+ for (const step of DROP_ORDER) {
106
+ if (fits())
107
+ break;
108
+ if (step === 'context-window') {
109
+ current = current.map(segment => segment.id === 'context' && segment.short ? { ...segment, text: segment.short } : segment);
110
+ continue;
111
+ }
112
+ if (step === 'model') {
113
+ const model = current.find(segment => segment.id === 'model');
114
+ if (!model?.model)
115
+ continue;
116
+ const others = current.filter(segment => segment !== model);
117
+ const room = width - displayWidth(joined(others)) - (others.length ? SEPARATOR.length : 0) - displayWidth(t('screen.statusbar.model', { model: '' }));
118
+ current = room >= MODEL_MIN ? current.map(segment => segment === model ? { ...segment, text: t('screen.statusbar.model', { model: truncateMiddle(model.model, room) }) } : segment) : others;
119
+ continue;
120
+ }
121
+ current = current.filter(segment => segment.id !== step);
122
+ }
123
+ const segments = current.map(({ id, text, tone }) => ({ id, text, tone }));
124
+ return { text: truncateDisplay(joined(segments), width), segments };
125
+ }