talos-code 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +661 -0
  3. package/README.md +116 -3
  4. package/THIRD_PARTY_NOTICES.md +40 -0
  5. package/dist/archive/zip.js +73 -0
  6. package/dist/args.js +100 -0
  7. package/dist/automations/history-store.js +34 -0
  8. package/dist/automations/policy-store.js +43 -0
  9. package/dist/automations/runner.js +107 -0
  10. package/dist/automations/schedule.js +85 -0
  11. package/dist/automations/windows-task.js +53 -0
  12. package/dist/commands/advanced-cli.js +233 -0
  13. package/dist/commands/checkpoint-cli.js +45 -0
  14. package/dist/commands/config-cli.js +107 -0
  15. package/dist/commands/context.js +17 -0
  16. package/dist/commands/extensions-cli.js +207 -0
  17. package/dist/commands/project-cli.js +74 -0
  18. package/dist/commands/project-commands.js +143 -0
  19. package/dist/commands/provider-cli.js +244 -0
  20. package/dist/commands/services-cli.js +248 -0
  21. package/dist/commands/session-cli.js +171 -0
  22. package/dist/commands/system-cli.js +405 -0
  23. package/dist/config/commands.js +60 -0
  24. package/dist/config/load.js +382 -0
  25. package/dist/config/migrations.js +48 -0
  26. package/dist/config/types.js +11 -0
  27. package/dist/diagnostics/development-log.js +154 -0
  28. package/dist/diagnostics/doctor.js +104 -0
  29. package/dist/diagnostics/redact.js +80 -0
  30. package/dist/diagnostics/zip.js +52 -0
  31. package/dist/errors.js +156 -0
  32. package/dist/events/bridge.js +130 -0
  33. package/dist/extensions/installer.js +178 -0
  34. package/dist/extensions/package-schema.js +24 -0
  35. package/dist/headless/result.js +84 -0
  36. package/dist/headless/run.js +230 -0
  37. package/dist/i18n/en/approval.js +46 -0
  38. package/dist/i18n/en/common.js +9 -0
  39. package/dist/i18n/en/credentials.js +92 -0
  40. package/dist/i18n/en/errors.js +267 -0
  41. package/dist/i18n/en/firstrun.js +101 -0
  42. package/dist/i18n/en/providers.js +37 -0
  43. package/dist/i18n/en/screen.js +774 -0
  44. package/dist/i18n/en/tools.js +127 -0
  45. package/dist/i18n/en.js +14 -0
  46. package/dist/i18n/error-view.js +307 -0
  47. package/dist/i18n/index.js +19 -0
  48. package/dist/i18n/kernel-map.js +139 -0
  49. package/dist/io.js +45 -0
  50. package/dist/main.js +331 -0
  51. package/dist/paths.js +41 -0
  52. package/dist/protocol/v2/codec.js +9 -0
  53. package/dist/protocol/v2/events.js +555 -0
  54. package/dist/protocol/v2/index.js +4 -0
  55. package/dist/protocol/v2/replay.js +28 -0
  56. package/dist/protocol/v2/types.js +1 -0
  57. package/dist/provider/control-plane.js +135 -0
  58. package/dist/provider/environment-keys.js +275 -0
  59. package/dist/provider/health.js +110 -0
  60. package/dist/provider/missing-key.js +48 -0
  61. package/dist/provider/model-catalog.js +168 -0
  62. package/dist/provider/openrouter-login.js +172 -0
  63. package/dist/provider/provider-text.js +13 -0
  64. package/dist/provider/store.js +95 -0
  65. package/dist/provider/system-keyring.js +214 -0
  66. package/dist/runtime/active-run.js +63 -0
  67. package/dist/runtime/agent-tree.js +74 -0
  68. package/dist/runtime/attachments.js +84 -0
  69. package/dist/runtime/brokered-executor.js +395 -0
  70. package/dist/runtime/context-archive.js +69 -0
  71. package/dist/runtime/context-status.js +76 -0
  72. package/dist/runtime/create-runtime.js +111 -0
  73. package/dist/runtime/model-profile.js +361 -0
  74. package/dist/runtime/output-store.js +137 -0
  75. package/dist/runtime/provider-attempts.js +185 -0
  76. package/dist/runtime/replay-buffer.js +153 -0
  77. package/dist/runtime/repo.js +95 -0
  78. package/dist/runtime/session-facade.js +135 -0
  79. package/dist/runtime/session-summary.js +125 -0
  80. package/dist/runtime/supervisor.js +265 -0
  81. package/dist/runtime/talos-composition.js +1011 -0
  82. package/dist/runtime/types.js +5 -0
  83. package/dist/runtime/usage-snapshot.js +82 -0
  84. package/dist/security/auto-classifier.js +38 -0
  85. package/dist/security/credential-free-environment.js +54 -0
  86. package/dist/security/evaluate.js +243 -0
  87. package/dist/security/execution-backends.js +236 -0
  88. package/dist/security/execution-broker.js +102 -0
  89. package/dist/security/forge-scan.js +74 -0
  90. package/dist/security/from-approval.js +39 -0
  91. package/dist/security/mxc-execution-backend.js +281 -0
  92. package/dist/security/permission-engine.js +138 -0
  93. package/dist/security/permission-explanation.js +54 -0
  94. package/dist/security/persist.js +179 -0
  95. package/dist/security/plugin-guard.js +230 -0
  96. package/dist/security/process-tree-evidence-store.js +74 -0
  97. package/dist/security/process-tree-probe.js +554 -0
  98. package/dist/security/project-resource-inventory.js +186 -0
  99. package/dist/security/project-trust-gate.js +38 -0
  100. package/dist/security/project-trust.js +367 -0
  101. package/dist/security/rule-parser.js +201 -0
  102. package/dist/security/shell-segmentation.js +68 -0
  103. package/dist/security/trust-authority.js +324 -0
  104. package/dist/security/types.js +1 -0
  105. package/dist/security/workspace-identity.js +102 -0
  106. package/dist/services/automation-facade.js +59 -0
  107. package/dist/services/forge-facade.js +77 -0
  108. package/dist/services/hook-facade.js +240 -0
  109. package/dist/services/index.js +16 -0
  110. package/dist/services/library-facade.js +174 -0
  111. package/dist/services/mcp-facade.js +348 -0
  112. package/dist/services/memory-facade.js +126 -0
  113. package/dist/services/notes-facade.js +157 -0
  114. package/dist/services/plugin-facade.js +308 -0
  115. package/dist/services/research-facade.js +110 -0
  116. package/dist/services/task-board-facade.js +236 -0
  117. package/dist/services/workflow-catalog.js +246 -0
  118. package/dist/sessions/export-format.js +98 -0
  119. package/dist/sessions/metadata-store.js +160 -0
  120. package/dist/sessions/share.js +122 -0
  121. package/dist/sessions/transfer.js +16 -0
  122. package/dist/subcommands.js +62 -0
  123. package/dist/tui/agent-roster.js +36 -0
  124. package/dist/tui/agent-view.js +51 -0
  125. package/dist/tui/app.js +4098 -0
  126. package/dist/tui/approval.js +36 -0
  127. package/dist/tui/boot/boot-sequence.js +76 -0
  128. package/dist/tui/boot/cinematic.js +243 -0
  129. package/dist/tui/boot/desktop-logo.js +82 -0
  130. package/dist/tui/boot.js +3 -0
  131. package/dist/tui/busy-input.js +58 -0
  132. package/dist/tui/catalog-service.js +560 -0
  133. package/dist/tui/components/assistant-stream.js +1 -0
  134. package/dist/tui/components/command-menu.js +68 -0
  135. package/dist/tui/components/composer.js +219 -0
  136. package/dist/tui/components/diff.js +35 -0
  137. package/dist/tui/components/footer.js +48 -0
  138. package/dist/tui/components/header.js +6 -0
  139. package/dist/tui/components/markdown.js +305 -0
  140. package/dist/tui/components/status-indicator.js +38 -0
  141. package/dist/tui/components/terminal-shell.js +214 -0
  142. package/dist/tui/components/thinking-row.js +19 -0
  143. package/dist/tui/components/tool-row.js +97 -0
  144. package/dist/tui/components/transcript-virtualizer.js +165 -0
  145. package/dist/tui/components/transcript.js +43 -0
  146. package/dist/tui/descendant-approvals.js +104 -0
  147. package/dist/tui/diff-model.js +77 -0
  148. package/dist/tui/editor-history.js +21 -0
  149. package/dist/tui/editor.js +210 -0
  150. package/dist/tui/event-adapter.js +436 -0
  151. package/dist/tui/exit-output.js +58 -0
  152. package/dist/tui/external-editor.js +53 -0
  153. package/dist/tui/file-completion.js +89 -0
  154. package/dist/tui/focus-manager.js +5 -0
  155. package/dist/tui/highlight.js +30 -0
  156. package/dist/tui/input-router.js +18 -0
  157. package/dist/tui/interrupt.js +99 -0
  158. package/dist/tui/keybindings.js +194 -0
  159. package/dist/tui/keymap-resolver.js +91 -0
  160. package/dist/tui/launch-state.js +148 -0
  161. package/dist/tui/line-diff.js +57 -0
  162. package/dist/tui/live-activity.js +56 -0
  163. package/dist/tui/metrics.js +123 -0
  164. package/dist/tui/onboarding.js +36 -0
  165. package/dist/tui/overlays/agent-tree.js +43 -0
  166. package/dist/tui/overlays/approval-dialog.js +642 -0
  167. package/dist/tui/overlays/automation-center.js +51 -0
  168. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  169. package/dist/tui/overlays/context-inspector.js +36 -0
  170. package/dist/tui/overlays/effort-line.js +110 -0
  171. package/dist/tui/overlays/forge-center.js +46 -0
  172. package/dist/tui/overlays/help-dialog.js +16 -0
  173. package/dist/tui/overlays/history-picker.js +37 -0
  174. package/dist/tui/overlays/hook-center.js +70 -0
  175. package/dist/tui/overlays/library-center.js +55 -0
  176. package/dist/tui/overlays/mcp-center.js +60 -0
  177. package/dist/tui/overlays/memory-center.js +63 -0
  178. package/dist/tui/overlays/model-picker.js +77 -0
  179. package/dist/tui/overlays/notes-center.js +53 -0
  180. package/dist/tui/overlays/overlay-host.js +8 -0
  181. package/dist/tui/overlays/plugin-center.js +76 -0
  182. package/dist/tui/overlays/provider-picker.js +171 -0
  183. package/dist/tui/overlays/queue-editor.js +32 -0
  184. package/dist/tui/overlays/research-center.js +59 -0
  185. package/dist/tui/overlays/scroll-window.js +234 -0
  186. package/dist/tui/overlays/session-picker.js +115 -0
  187. package/dist/tui/overlays/tasks-center.js +83 -0
  188. package/dist/tui/overlays/theme-picker.js +21 -0
  189. package/dist/tui/overlays/transcript-search.js +58 -0
  190. package/dist/tui/overlays/trust-center.js +29 -0
  191. package/dist/tui/overlays/workflow-center.js +46 -0
  192. package/dist/tui/project-file-index.js +214 -0
  193. package/dist/tui/project-references.js +85 -0
  194. package/dist/tui/project-trust-prompt.js +289 -0
  195. package/dist/tui/prompt-history-store.js +116 -0
  196. package/dist/tui/queue-store.js +110 -0
  197. package/dist/tui/regions/budget.js +59 -0
  198. package/dist/tui/regions/views.js +96 -0
  199. package/dist/tui/render-coordinator.js +148 -0
  200. package/dist/tui/render-scheduler.js +4 -0
  201. package/dist/tui/run.js +69 -0
  202. package/dist/tui/selection-list.js +29 -0
  203. package/dist/tui/session-controller.js +791 -0
  204. package/dist/tui/session-export.js +54 -0
  205. package/dist/tui/setup-wizard.js +46 -0
  206. package/dist/tui/shell-input.js +35 -0
  207. package/dist/tui/shell-layout.js +48 -0
  208. package/dist/tui/shell-model.js +98 -0
  209. package/dist/tui/slash-commands.js +82 -0
  210. package/dist/tui/state.js +151 -0
  211. package/dist/tui/status-bar.js +125 -0
  212. package/dist/tui/status-view.js +40 -0
  213. package/dist/tui/terminal-capabilities.js +9 -0
  214. package/dist/tui/terminal-session.js +11 -0
  215. package/dist/tui/text-width.js +66 -0
  216. package/dist/tui/theme-catalog.js +27 -0
  217. package/dist/tui/theme-store.js +23 -0
  218. package/dist/tui/theme.js +23 -0
  219. package/dist/tui/tool-display.js +135 -0
  220. package/dist/tui/tool-facts.js +1 -0
  221. package/dist/tui/tools/bash-renderer.js +32 -0
  222. package/dist/tui/tools/change.js +61 -0
  223. package/dist/tui/tools/edit-renderer.js +17 -0
  224. package/dist/tui/tools/generic-renderer.js +13 -0
  225. package/dist/tui/tools/list-renderer.js +14 -0
  226. package/dist/tui/tools/read-renderer.js +13 -0
  227. package/dist/tui/tools/registry.js +20 -0
  228. package/dist/tui/tools/row-format.js +187 -0
  229. package/dist/tui/tools/search-renderer.js +38 -0
  230. package/dist/tui/tools/shared.js +97 -0
  231. package/dist/tui/tools/write-renderer.js +15 -0
  232. package/dist/tui/transcript-model.js +441 -0
  233. package/dist/tui/ui-preferences.js +79 -0
  234. package/dist/tui/usage-view.js +77 -0
  235. package/dist/tui/vim-mode.js +99 -0
  236. package/dist/update/check.js +15 -0
  237. package/dist/update/npm.js +51 -0
  238. package/dist/update/run.js +129 -0
  239. package/dist/version.js +2 -0
  240. package/dist/workspace/checkpoint-store.js +210 -0
  241. package/dist/workspace/checkpoint.js +413 -0
  242. package/dist/workspace/restore.js +232 -0
  243. package/package.json +64 -5
  244. package/vendor/context-engine/package.json +11 -0
  245. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  246. package/vendor/context-engine/src/contracts.mjs +48 -0
  247. package/vendor/context-engine/src/engine.mjs +445 -0
  248. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  249. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  250. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  251. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  252. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  253. package/vendor/context-engine/src/profiles.mjs +10 -0
  254. package/vendor/context-engine/src/retrieval.mjs +104 -0
  255. package/vendor/context-engine/src/summary.mjs +97 -0
  256. package/vendor/context-engine/src/usage.mjs +34 -0
  257. package/vendor/harness-ui/package.json +38 -0
  258. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  259. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  260. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  261. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  262. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  263. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  264. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  265. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  266. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  267. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  268. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  269. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  270. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  271. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  272. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  273. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  274. package/vendor/harness-ui/src/config.mjs +697 -0
  275. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  276. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  277. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  278. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  279. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  280. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  281. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  282. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  283. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  284. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  285. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  286. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  287. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  288. package/vendor/harness-ui/src/doctor.mjs +142 -0
  289. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  290. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  291. package/vendor/harness-ui/src/document-report.mjs +331 -0
  292. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  293. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  294. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  295. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  296. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  297. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  298. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  299. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  300. package/vendor/harness-ui/src/git-service.mjs +626 -0
  301. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  302. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  303. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  304. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  305. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  306. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  307. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  308. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  309. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  310. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  311. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  312. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  313. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  314. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  315. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  316. package/vendor/harness-ui/src/library-store.mjs +652 -0
  317. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  318. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  319. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  320. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  321. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  322. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  323. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  324. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  325. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  326. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  327. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  328. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  329. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  330. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  331. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  332. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  333. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  334. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  335. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  336. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  337. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  338. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  339. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  340. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  341. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  342. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  343. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  344. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  345. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  346. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  347. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  348. package/vendor/harness-ui/src/research/card.mjs +235 -0
  349. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  350. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  351. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  352. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  353. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  354. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  355. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  356. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  357. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  358. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  359. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  360. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  361. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  362. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  363. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  364. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  365. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  366. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  367. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  368. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  369. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  370. package/vendor/harness-ui/src/research/report.mjs +203 -0
  371. package/vendor/harness-ui/src/research/run.mjs +527 -0
  372. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  373. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  374. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  375. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  376. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  377. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  378. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  379. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  380. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  381. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  382. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  383. package/vendor/harness-ui/src/session-store.mjs +220 -0
  384. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  385. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  386. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  387. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  388. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  389. package/vendor/harness-ui/src/static-files.mjs +96 -0
  390. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  391. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  392. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  393. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  394. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  395. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  396. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  397. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  398. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  399. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  400. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  401. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  402. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  403. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  404. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  405. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  406. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  407. package/vendor/manifest.json +170 -0
@@ -0,0 +1,441 @@
1
+ import { describeError, describeRefusal, errorViewLines } from "../i18n/error-view.js";
2
+ import { t, tn } from "../i18n/index.js";
3
+ import { rendererKindFor, toolDisplay } from "./tool-display.js";
4
+ import { toolRowView } from "./components/tool-row.js";
5
+ import { compactTokens } from "./status-bar.js";
6
+ import { lineCount, sanitizeDisplayText, truncatedMarker, visibleEscape, withoutKernelExitLine } from "./tools/shared.js";
7
+ export const MAX_TRANSCRIPT_ITEMS = 768;
8
+ const MAX_MESSAGE_CHARS = 256 * 1024;
9
+ const MAX_TOOL_SUMMARY_CHARS = 8 * 1024;
10
+ const MAX_WARNING_CHARS = 2048;
11
+ /* A file change is kept whole (for the Ctrl+O diff) up to this size per side; beyond it only its summary is kept. */
12
+ const MAX_CHANGE_CHARS = 512 * 1024;
13
+ const MAX_RUNS = 64;
14
+ function tail(value, limit) { if (value.length <= limit)
15
+ return value; const marker = truncatedMarker() + '\n'; return marker + value.slice(-(limit - marker.length)); }
16
+ export function sanitizeTranscriptText(value) { return sanitizeDisplayText(value); }
17
+ export function transcriptItemText(item) {
18
+ if (item.kind === 'message')
19
+ return sanitizeTranscriptText((item.role === 'user' ? 'You' : 'TALOS') + '\n' + item.text);
20
+ if (item.kind === 'reasoning')
21
+ return sanitizeTranscriptText('Reasoning\n' + item.text);
22
+ if (item.kind === 'tool') {
23
+ /* R3 gate E1: the header is the ONE row formatter's line (tools/row-format.ts), unfitted; the output follows whole for
24
+ search and copy, without the kernel's `exit N [sandbox: …]` first line (gate E3). The raw item stays in
25
+ `transcriptItemRaw`. */
26
+ const output = withoutKernelExitLine(item.finalOutput || item.liveOutput);
27
+ return sanitizeTranscriptText([toolRowView(item, Number.POSITIVE_INFINITY, false).line, output.trim() ? 'Output\n' + output : ''].filter(Boolean).join('\n'));
28
+ }
29
+ if (item.kind === 'warning')
30
+ return sanitizeTranscriptText(item.outcome ? item.text : 'Warning\n' + item.text);
31
+ return sanitizeTranscriptText('Run · ' + item.status + (item.runId ? ' · ' + item.runId : '') + (item.message ? '\n' + item.message : ''));
32
+ }
33
+ export function transcriptItemRaw(item) {
34
+ return JSON.stringify(item, null, 2).replace(/[\x7f-\x9f\u2028\u2029\p{Bidi_Control}]/gu, visibleEscape);
35
+ }
36
+ export function createTranscriptModel() { return { items: [], nextLocalId: 1, pendingApproval: null }; }
37
+ export function clearTranscript(model) { return { items: [], nextLocalId: model.nextLocalId, pendingApproval: null, ...(model.runs ? { runs: model.runs } : {}) }; }
38
+ function activeTool(status) { return status === 'queued' || status === 'approval' || status === 'running' || status === 'streaming'; }
39
+ function active(item) { return (item.kind === 'message' && item.streaming) || (item.kind === 'reasoning' && item.streaming) || (item.kind === 'tool' && activeTool(item.status)) || (item.kind === 'run' && item.status === 'running'); }
40
+ function trim(model) {
41
+ const items = [...model.items];
42
+ while (items.length > MAX_TRANSCRIPT_ITEMS) {
43
+ const index = items.findIndex(item => !active(item));
44
+ if (index < 0)
45
+ break;
46
+ items.splice(index, 1);
47
+ }
48
+ return { ...model, items };
49
+ }
50
+ function put(model, item) {
51
+ const index = model.items.findIndex(row => row.id === item.id);
52
+ if (index >= 0) {
53
+ const items = model.items.slice();
54
+ items[index] = item;
55
+ return trim({ ...model, items });
56
+ }
57
+ return trim({ ...model, items: [...model.items, item] });
58
+ }
59
+ function localId(model, prefix) { return { id: prefix + ':local:' + model.nextLocalId, model: { ...model, nextLocalId: model.nextLocalId + 1 } }; }
60
+ function existing(model, id, kind) {
61
+ const item = model.items.find(row => row.id === id);
62
+ return item?.kind === kind ? item : null;
63
+ }
64
+ function toolId(toolCallId) { return 'tool:' + toolCallId; }
65
+ /* ---- R3 contract events (tui/tool-facts.ts; produced by lane SP) ---- */
66
+ function definedFacts(facts) { const out = {}; for (const [key, value] of Object.entries(facts ?? {}))
67
+ if (value !== undefined)
68
+ out[key] = value; return out; }
69
+ function withFacts(item, pending) {
70
+ const next = { ...item };
71
+ if (pending.origin)
72
+ next.origin = pending.origin;
73
+ if (pending.decision !== undefined)
74
+ next.decision = pending.decision;
75
+ if (pending.facts)
76
+ next.facts = { ...(item.facts ?? {}), ...definedFacts(pending.facts) };
77
+ if (pending.savedOutput)
78
+ next.savedOutput = pending.savedOutput;
79
+ if (pending.readCut)
80
+ next.readCut = pending.readCut;
81
+ return next;
82
+ }
83
+ const count = (value) => typeof value === 'number' && Number.isFinite(value) && value >= 0 ? Math.floor(value) : null;
84
+ /* R5a S6/S7: the facts of lane R's cut events, kept only when they are whole numbers and a path (a malformed event is
85
+ dropped, never drawn as a zero). */
86
+ function savedOutputOf(event) {
87
+ const kept = count(event.keptChars), total = count(event.totalChars);
88
+ return typeof event.path === 'string' && event.path.trim() && kept !== null && total !== null ? { path: event.path, keptChars: kept, totalChars: total } : null;
89
+ }
90
+ function readCutOf(event) {
91
+ const shown = count(event.shownLines), limit = count(event.limitChars);
92
+ return shown !== null && limit !== null && limit > 0 ? { shownLines: shown, totalLines: count(event.totalLines), limitChars: limit } : null;
93
+ }
94
+ /*
95
+ * R5a S3 (owner decision Q-R5-4, gate E9): a compaction is named on the transcript when it happens — which engine, what
96
+ * was summarized, the tokens before and after. The Context Engine compacts at half of a KNOWN window (owner, 2026-09-16:
97
+ * "the threshold is stated to the user"), so its line states the 50 %; the kernel's own rule (every 8 model calls) is the
98
+ * one that runs when no window is known. An unknown count is said to be unknown, never zero (E1).
99
+ */
100
+ export function compactionNoticeText(event) {
101
+ const messages = count(event.messagesSummarized), before = count(event.tokensBefore), after = count(event.tokensAfter);
102
+ const parts = [messages === null ? t('screen.compaction.messagesUnknown') : tn('screen.compaction.messages', messages)];
103
+ if (before !== null && after !== null)
104
+ parts.push(t('screen.compaction.tokens', { before: compactTokens(before), after: compactTokens(after) }));
105
+ else if (before !== null)
106
+ parts.push(t('screen.compaction.tokensBefore', { before: compactTokens(before) }));
107
+ else if (after !== null)
108
+ parts.push(t('screen.compaction.tokensAfter', { after: compactTokens(after) }));
109
+ else
110
+ parts.push(t('screen.compaction.tokensUnknown'));
111
+ return t(event.engine === 'context-engine' ? 'screen.compaction.contextEngine' : 'screen.compaction.kernel', { summary: parts.join(' · ') });
112
+ }
113
+ /*
114
+ * Desktop CE fix (aligned 2026-09-25): the pause after a refused summary is said once per failed job, with the local time of
115
+ * the next automatic try (absolute, so a replayed line does not lie) and that /compact does not wait.
116
+ */
117
+ export function compactionPausedText(event) {
118
+ const at = new Date(event.retryAfter);
119
+ const time = `${String(at.getHours()).padStart(2, '0')}:${String(at.getMinutes()).padStart(2, '0')}`;
120
+ return tn('screen.compaction.paused', event.attempts && event.attempts > 1 ? event.attempts : 1, { code: event.code, time });
121
+ }
122
+ /** A new tool item, with whatever facts arrived before it (and those facts consumed). */
123
+ function newTool(model, toolCallId, row) {
124
+ const pending = model.pendingTools?.[toolCallId];
125
+ let item = { kind: 'tool', id: toolId(toolCallId), ...row };
126
+ let base = model;
127
+ if (pending) {
128
+ item = withFacts(item, pending);
129
+ const rest = { ...model.pendingTools };
130
+ delete rest[toolCallId];
131
+ base = { ...model, pendingTools: rest };
132
+ }
133
+ return put(base, item);
134
+ }
135
+ function toolFacts(model, toolCallId, pending) {
136
+ const current = existing(model, toolId(toolCallId), 'tool');
137
+ if (current)
138
+ return put(model, withFacts(current, pending));
139
+ const all = { ...(model.pendingTools ?? {}) };
140
+ all[toolCallId] = { ...all[toolCallId], ...pending, ...(pending.facts ? { facts: { ...(all[toolCallId]?.facts ?? {}), ...definedFacts(pending.facts) } } : {}) };
141
+ const keys = Object.keys(all);
142
+ if (keys.length > MAX_RUNS)
143
+ delete all[keys[0]];
144
+ return { ...model, pendingTools: all };
145
+ }
146
+ /* The path a write/edit row names, for matching a `file.changed` that carries no tool call id. */
147
+ const normalPath = (value) => String(value ?? '').trim().replaceAll('\\', '/').replace(/^\.\//u, '');
148
+ function changedPathOf(item) { try {
149
+ const args = JSON.parse(item.argsText || '{}');
150
+ return normalPath(args?.percorso ?? args?.path ?? args?.file ?? args?.filename);
151
+ }
152
+ catch {
153
+ return '';
154
+ } }
155
+ function fileChanged(model, change) {
156
+ let target = change.toolCallId ? existing(model, toolId(change.toolCallId), 'tool') : null;
157
+ if (!target) {
158
+ const writers = model.items.filter((item) => item.kind === 'tool' && (rendererKindFor(item.name) === 'write' || rendererKindFor(item.name) === 'edit'));
159
+ const path = normalPath(change.path);
160
+ target = writers.findLast(item => changedPathOf(item) === path) ?? writers.findLast(item => activeTool(item.status)) ?? null;
161
+ }
162
+ if (!target)
163
+ return model;
164
+ const small = (change.before ?? '').length <= MAX_CHANGE_CHARS && change.after.length <= MAX_CHANGE_CHARS;
165
+ return put(model, small ? { ...target, change, changeSummary: null } : { ...target, change: null, changeSummary: { newFile: change.before === null, lines: lineCount(change.after), added: null, removed: null } });
166
+ }
167
+ /* R2 lane E (gate E2, E4): a refused tool's `content` is what the MODEL reads ("REFUSED. <Italian motivo> …") and is never
168
+ rewritten; the row shows the English reason of the receipt's `via`, and the content stays in `outcome.content`.
169
+ R3 (gate E9): a refusal is `denied`, not `failed`. */
170
+ function refused(outcome) { return Boolean(outcome && (outcome.allowed === false || outcome.receiptStatus === 'denied')); }
171
+ function terminalTool(model, toolCallId, rawContent, outcome) {
172
+ const id = toolId(toolCallId), current = existing(model, id, 'tool');
173
+ const status = refused(outcome) ? 'denied' : outcome?.status ?? (current?.status === 'denied' ? 'denied' : 'completed');
174
+ const content = refused(outcome) ? describeRefusal({ via: outcome.permissionVia, allowed: outcome.allowed }) : status === 'denied' && current ? current.finalOutput || rawContent : rawContent;
175
+ const next = current
176
+ ? put(model, { ...current, status, finalOutput: tail(content, MAX_TOOL_SUMMARY_CHARS), endedAt: Date.now(), outcome })
177
+ : newTool(model, toolCallId, { name: 'tool', status, argsText: '', liveOutput: '', finalOutput: tail(content, MAX_TOOL_SUMMARY_CHARS), startedAt: null, endedAt: Date.now(), outcome });
178
+ return next.pendingApproval?.toolCallId === toolCallId ? { ...next, pendingApproval: null } : next;
179
+ }
180
+ /* ---- Runs (R1 lane R; R3 Q-R3-8/9) ---- */
181
+ function runsOf(model) { return model.runs ?? []; }
182
+ function setRun(model, run) {
183
+ const runs = runsOf(model);
184
+ return { ...model, runs: runs.some(row => row.key === run.key) ? runs.map(row => row.key === run.key ? run : row) : [...runs, run].slice(-MAX_RUNS) };
185
+ }
186
+ function findRun(model, event) {
187
+ const runs = runsOf(model);
188
+ if (event.runId) {
189
+ const run = runs.find(row => row.runId === event.runId || row.key === event.runId);
190
+ if (run)
191
+ return run;
192
+ }
193
+ for (let i = runs.length - 1; i >= 0; i--)
194
+ if (runs[i].status === 'running')
195
+ return runs[i];
196
+ return null;
197
+ }
198
+ function anchorIndex(model, run) { return run?.anchor ? model.items.findIndex(item => item.id === run.anchor) : -1; }
199
+ /*
200
+ * R1 lane R (D-04, gate E9): the person's message is rebuilt from its run's RunStarted and keyed by the run
201
+ * (`message:user:<runId>`), so a replay from 0 is idempotent. At send time app.ts has already shown a local echo
202
+ * (`message:local:<n>`): the RunStarted adopts the newest unclaimed echo of the same text sent since the last run, so the
203
+ * message is never shown twice. When the RunStarted comes first (queued prompt, command-line prompt) the message is created
204
+ * here and the late echo is absorbed once (`lateEcho`). A replayed history RunStarted (`history`, set by the controller) never
205
+ * absorbs an echo: a new send with the same text as an old one must stay visible at once.
206
+ * R3 (Q-R3-9): "since the last run" is "after the last run's anchor" — there is no Run row to stop at any more.
207
+ */
208
+ function userMessageId(key) { return 'message:user:' + key; }
209
+ function isLocalEcho(item) { return item.kind === 'message' && item.role === 'user' && item.id.startsWith('message:local:'); }
210
+ function adoptOrAddPrompt(model, key, text, history) {
211
+ const id = userMessageId(key);
212
+ if (existing(model, id, 'message'))
213
+ return model;
214
+ const stop = anchorIndex(model, runsOf(model).at(-1));
215
+ for (let i = model.items.length - 1; i > stop; i--) {
216
+ const row = model.items[i];
217
+ if (row.kind === 'run' || (row.kind === 'message' && row.role === 'user' && !isLocalEcho(row)))
218
+ break;
219
+ if (isLocalEcho(row) && row.kind === 'message' && row.text === text) {
220
+ const items = model.items.slice();
221
+ items[i] = { ...row, id, streaming: false };
222
+ return { ...model, items, lateEcho: null };
223
+ }
224
+ }
225
+ const next = put(model, { kind: 'message', id, role: 'user', text: tail(text, MAX_MESSAGE_CHARS), streaming: false });
226
+ return { ...next, lateEcho: history ? null : { id, text } };
227
+ }
228
+ /* R5a S8: the clock of a reasoning block — now for a live run, none while a history is replayed. */
229
+ function liveClock(model) { const run = runsOf(model).at(-1); return run?.history && run.status === 'running' ? null : Date.now(); }
230
+ function endReasoning(item) {
231
+ return { ...item, streaming: false, ...(typeof item.startedAt === 'number' && typeof item.endedAt !== 'number' ? { endedAt: Date.now() } : {}) };
232
+ }
233
+ /* A turn that ends closes the reasoning it left open (the kernel may end a run without a reasoning end event). */
234
+ function closeReasoning(model) {
235
+ if (!model.items.some(item => item.kind === 'reasoning' && item.streaming))
236
+ return model;
237
+ return { ...model, items: model.items.map(item => item.kind === 'reasoning' && item.streaming ? endReasoning(item) : item) };
238
+ }
239
+ function closeInterrupted(model) {
240
+ const now = Date.now();
241
+ const items = model.items.map(item => {
242
+ if (item.kind === 'reasoning' && item.streaming)
243
+ return endReasoning(item);
244
+ if (item.kind === 'message' && item.streaming)
245
+ return { ...item, streaming: false };
246
+ /* Q-R3-8: a tool the stop cut is `interrupted`; one that was still waiting for approval never ran (no duration). */
247
+ if (item.kind === 'tool' && activeTool(item.status))
248
+ return { ...item, status: 'interrupted', endedAt: item.endedAt ?? (item.status === 'approval' ? item.startedAt : now) };
249
+ return item;
250
+ });
251
+ return { ...model, items, pendingApproval: null };
252
+ }
253
+ export function appendTranscriptMessage(model, input) {
254
+ const late = model.lateEcho;
255
+ if (late && !input.id && input.role === 'user' && late.text === input.text && existing(model, late.id, 'message'))
256
+ return { ...model, lateEcho: null };
257
+ let base = model, id = input.id ? 'message:' + input.id : '';
258
+ if (!id) {
259
+ const next = localId(model, 'message');
260
+ id = next.id;
261
+ base = next.model;
262
+ }
263
+ const current = existing(base, id, 'message');
264
+ return put(base, current ? { ...current, role: input.role, text: tail(input.text, MAX_MESSAGE_CHARS), streaming: false } : { kind: 'message', id, role: input.role, text: tail(input.text, MAX_MESSAGE_CHARS), streaming: false });
265
+ }
266
+ export function appendTranscriptWarning(model, text, id, detail) {
267
+ let base = model, itemId = id ? 'warning:' + id : '';
268
+ if (!itemId) {
269
+ const next = localId(model, 'warning');
270
+ itemId = next.id;
271
+ base = next.model;
272
+ }
273
+ return put(base, { kind: 'warning', id: itemId, text: tail(text, MAX_WARNING_CHARS), ...(detail ? { detail: tail(detail, MAX_WARNING_CHARS) } : {}) });
274
+ }
275
+ /*
276
+ * R2 lane E (gate E15): where an interrupted run stopped, read from THIS transcript (the kernel's stop point is Italian free
277
+ * text). The last row of the run decides: a tool waiting for approval, a tool still running, or the model's answer.
278
+ */
279
+ function stopPoint(model, run) {
280
+ const start = anchorIndex(model, run);
281
+ for (let i = model.items.length - 1; i > start; i--) {
282
+ const item = model.items[i];
283
+ if (item.kind === 'tool') {
284
+ const tool = toolDisplay(item.name).label;
285
+ if (item.status === 'approval' || model.pendingApproval?.toolCallId === item.id.slice(5))
286
+ return { kind: 'approval', tool };
287
+ if (activeTool(item.status))
288
+ return { kind: 'tool', tool };
289
+ return null;
290
+ }
291
+ if ((item.kind === 'message' && item.role === 'assistant') || item.kind === 'reasoning')
292
+ return { kind: 'answering' };
293
+ if (item.kind === 'run' || (item.kind === 'warning' && item.outcome))
294
+ return null;
295
+ }
296
+ return null;
297
+ }
298
+ function stopPhrase(point) {
299
+ if (point.kind === 'answering')
300
+ return t('error.run.while.answering');
301
+ return point.kind === 'approval' ? t('error.run.while.approval', { tool: point.tool }) : t('error.run.while.tool', { tool: point.tool });
302
+ }
303
+ export function reduceTranscriptEvent(model, event) {
304
+ if (event.type === 'message.started') {
305
+ const id = 'message:' + event.messageId, current = existing(model, id, 'message');
306
+ return put(model, current ? { ...current, role: event.role } : { kind: 'message', id, role: event.role, text: '', streaming: true });
307
+ }
308
+ if (event.type === 'message.delta') {
309
+ const id = 'message:' + event.messageId, current = existing(model, id, 'message');
310
+ return put(model, current ? { ...current, text: tail(current.text + event.delta, MAX_MESSAGE_CHARS), streaming: true } : { kind: 'message', id, role: 'assistant', text: tail(event.delta, MAX_MESSAGE_CHARS), streaming: true });
311
+ }
312
+ if (event.type === 'message.end') {
313
+ const id = 'message:' + event.messageId, current = existing(model, id, 'message');
314
+ return current ? put(model, { ...current, streaming: false }) : model;
315
+ }
316
+ if (event.type === 'reasoning.started') {
317
+ const id = 'reasoning:' + event.messageId, current = existing(model, id, 'reasoning');
318
+ return current ? model : put(model, { kind: 'reasoning', id, text: '', streaming: true, startedAt: liveClock(model) });
319
+ }
320
+ if (event.type === 'reasoning.delta') {
321
+ const id = 'reasoning:' + event.messageId, current = existing(model, id, 'reasoning');
322
+ return put(model, current ? { ...current, text: tail(current.text + event.delta, MAX_MESSAGE_CHARS), streaming: true } : { kind: 'reasoning', id, text: tail(event.delta, MAX_MESSAGE_CHARS), streaming: true, startedAt: liveClock(model) });
323
+ }
324
+ if (event.type === 'reasoning.end') {
325
+ const id = 'reasoning:' + event.messageId, current = existing(model, id, 'reasoning');
326
+ return current ? put(model, endReasoning(current)) : model;
327
+ }
328
+ if (event.type === 'context.summary.requested') {
329
+ const live = runsOf(model).filter(run => run.status === 'running').at(-1);
330
+ return live ? setRun(model, { ...live, summaryRequests: (live.summaryRequests ?? 0) + 1 }) : model;
331
+ }
332
+ if (event.type === 'context.compaction.paused')
333
+ return appendTranscriptWarning(model, compactionPausedText(event), event.noticeId ? `paused:${event.noticeId}` : event.eventId ? `paused:${event.eventId}` : undefined);
334
+ if (event.type === 'context.compacted') {
335
+ const live = event.engine === 'context-engine' ? runsOf(model).filter(run => run.status === 'running').at(-1) : undefined;
336
+ const base = live ? setRun(model, { ...live, summaryAccepted: true }) : model;
337
+ return appendTranscriptWarning(base, compactionNoticeText(event), event.eventId ? `compaction:${event.eventId}` : undefined);
338
+ }
339
+ if (event.type === 'tool.output.saved') {
340
+ const saved = savedOutputOf(event);
341
+ return saved ? toolFacts(model, event.toolCallId, { savedOutput: saved }) : model;
342
+ }
343
+ if (event.type === 'tool.read.cut') {
344
+ const cut = readCutOf(event);
345
+ return cut ? toolFacts(model, event.toolCallId, { readCut: cut }) : model;
346
+ }
347
+ if (event.type === 'tool.started') {
348
+ const current = existing(model, toolId(event.toolCallId), 'tool');
349
+ /* A row made by an early `tool.args`/`tool.output` carries the placeholder name until its start names the tool. */
350
+ if (current)
351
+ return current.name === 'tool' && event.toolName !== 'tool' ? put(model, { ...current, name: event.toolName }) : model;
352
+ return newTool(model, event.toolCallId, { name: event.toolName, status: 'queued', argsText: '', liveOutput: '', finalOutput: '', startedAt: Date.now(), endedAt: null, outcome: null });
353
+ }
354
+ if (event.type === 'tool.args') {
355
+ const current = existing(model, toolId(event.toolCallId), 'tool');
356
+ return current ? put(model, { ...current, argsText: tail(current.argsText + event.delta, MAX_TOOL_SUMMARY_CHARS) }) : newTool(model, event.toolCallId, { name: 'tool', status: 'queued', argsText: tail(event.delta, MAX_TOOL_SUMMARY_CHARS), liveOutput: '', finalOutput: '', startedAt: Date.now(), endedAt: null, outcome: null });
357
+ }
358
+ if (event.type === 'tool.output') {
359
+ const current = existing(model, toolId(event.toolCallId), 'tool');
360
+ return current ? put(model, { ...current, status: 'streaming', liveOutput: tail(current.liveOutput + event.delta, MAX_TOOL_SUMMARY_CHARS) }) : newTool(model, event.toolCallId, { name: 'tool', status: 'streaming', argsText: '', liveOutput: tail(event.delta, MAX_TOOL_SUMMARY_CHARS), finalOutput: '', startedAt: Date.now(), endedAt: null, outcome: null });
361
+ }
362
+ if (event.type === 'tool.completed')
363
+ return terminalTool(model, event.toolCallId, event.content, event.outcome ?? null);
364
+ if (event.type === 'tool.origin')
365
+ return toolFacts(model, event.toolCallId, { origin: event.origin });
366
+ if (event.type === 'tool.decision')
367
+ return toolFacts(model, event.toolCallId, { decision: event.decision });
368
+ if (event.type === 'tool.facts')
369
+ return toolFacts(model, event.toolCallId, { facts: event.facts });
370
+ if (event.type === 'file.changed')
371
+ return 'change' in event && event.change ? fileChanged(model, event.change) : model;
372
+ if (event.type === 'approval.required') {
373
+ if (!event.toolCallId)
374
+ return model;
375
+ const id = toolId(event.toolCallId), current = existing(model, id, 'tool');
376
+ const next = current ? put(model, { ...current, status: 'approval', askedPerson: true }) : model;
377
+ return { ...next, pendingApproval: { requestId: event.requestId, toolCallId: event.toolCallId } };
378
+ }
379
+ if (event.type === 'approval.resolved') {
380
+ const pending = model.pendingApproval;
381
+ if (!pending || pending.requestId !== event.requestId)
382
+ return model;
383
+ const id = toolId(pending.toolCallId), current = existing(model, id, 'tool');
384
+ let next = model;
385
+ if (current) {
386
+ /* R3: an approved tool starts running now — its running clock does not count the time the person took to answer. */
387
+ if (event.approved === true)
388
+ next = put(model, { ...current, status: 'running', startedAt: Date.now() });
389
+ /* R3 gate E9: the person's "no" is `denied` with its English reason until the receipt names the exact one. */
390
+ else if (event.approved === false)
391
+ next = put(model, { ...current, status: 'denied', endedAt: current.endedAt ?? Date.now(), finalOutput: current.finalOutput || t('error.refused.notApproved') });
392
+ }
393
+ return { ...next, pendingApproval: null };
394
+ }
395
+ if (event.type === 'warning')
396
+ return appendTranscriptWarning(model, event.message, event.eventId, event.raw);
397
+ if (event.type === 'run.started') {
398
+ let base = model, key = event.runId ?? event.eventId ?? '';
399
+ if (!key) {
400
+ const next = localId(model, 'run');
401
+ key = next.id.slice(4);
402
+ base = next.model;
403
+ }
404
+ if (event.prompt)
405
+ base = adoptOrAddPrompt(base, key, event.prompt, event.history === true);
406
+ const known = runsOf(base).find(run => run.key === key);
407
+ return setRun(base, known ? { ...known, status: 'running' } : { key, runId: event.runId ?? null, status: 'running', history: event.history === true, anchor: base.items.at(-1)?.id ?? null });
408
+ }
409
+ if (event.type === 'run.completed' || event.type === 'run.failed' || event.type === 'run.cancelled') {
410
+ const run = findRun(model, event);
411
+ let base = model, key = run?.key ?? event.runId ?? event.eventId ?? '';
412
+ if (!key) {
413
+ const next = localId(model, 'run');
414
+ key = next.id.slice(4);
415
+ base = next.model;
416
+ }
417
+ const noticeId = 'run:' + key;
418
+ /* R5b: before the run's own ending line, the summaries it asked for and none accepted (each one billed). */
419
+ const current = run ? runsOf(model).find(entry => entry.key === run.key) ?? run : null;
420
+ if (current?.summaryRequests && !current.summaryAccepted)
421
+ base = appendTranscriptWarning(base, tn('screen.compaction.summaryNotAccepted', current.summaryRequests), `summary:${key}`);
422
+ const settle = (next, status) => run ? setRun(next, { ...(current ?? run), status }) : next;
423
+ /* Q-R3-9: a turn that ended normally leaves no line; one that a stop had marked but then completed drops the mark. */
424
+ if (event.type === 'run.completed')
425
+ return settle(closeReasoning({ ...base, items: base.items.filter(item => !(item.id === noticeId && item.kind === 'warning')) }), 'completed');
426
+ if (event.type === 'run.failed') {
427
+ base = closeReasoning(base);
428
+ /* R2 lane E: a failure is worded from its CODE with the stop point of this transcript; the kernel text is the detail. */
429
+ const view = event.error ? describeError(event.error, { stopPoint: stopPoint(base, run) }) : null;
430
+ const text = tail(view ? errorViewLines(view).join('\n') : event.message, MAX_WARNING_CHARS);
431
+ const detail = view?.raw ?? null;
432
+ return settle(put(base, { kind: 'warning', id: noticeId, text, outcome: 'failed', ...(detail ? { detail: tail(detail, MAX_WARNING_CHARS) } : {}) }), 'failed');
433
+ }
434
+ /* Q-R3-8: an interrupt is one neutral line naming where it stopped. A replayed run the kernel reports cut off (TALOS
435
+ closed mid-answer, R1 lane R D-08) says so; the stop closes whatever the run left streaming. */
436
+ const point = stopPoint(base, run);
437
+ const text = event.interrupted === true && run?.history ? t('tool.turn.closed') : point ? t('tool.turn.interruptedWhile', { while: stopPhrase(point) }) : t('tool.turn.interrupted');
438
+ return settle(put(closeInterrupted(base), { kind: 'warning', id: noticeId, text, outcome: 'interrupted' }), 'interrupted');
439
+ }
440
+ return model;
441
+ }
@@ -0,0 +1,79 @@
1
+ import { loadEffectiveConfig, writeConfigScope } from "../config/load.js";
2
+ import { DEFAULT_BUSY_INPUT, parseBusyInput } from "./busy-input.js";
3
+ export const DEFAULT_UI_PREFERENCES = Object.freeze({ busyInput: DEFAULT_BUSY_INPUT, reasoning: 'collapsed' });
4
+ export function parseReasoning(value) { return value === 'collapsed' || value === 'visible' ? value : null; }
5
+ /** The effective choices; a configuration that cannot be read gives the defaults (a screen choice never blocks the TUI). */
6
+ export async function loadUiPreferences({ paths, projectRoot }) {
7
+ try {
8
+ const ui = (await loadEffectiveConfig({ paths, projectRoot })).value.ui;
9
+ return { busyInput: parseBusyInput(ui?.busyInput) ?? DEFAULT_UI_PREFERENCES.busyInput, reasoning: parseReasoning(ui?.reasoning) ?? DEFAULT_UI_PREFERENCES.reasoning };
10
+ }
11
+ catch {
12
+ return { ...DEFAULT_UI_PREFERENCES };
13
+ }
14
+ }
15
+ /* R5a (owner 2026-09-24, as Hermes's `onboarding.seen.busy_input_prompt`): the busy tip is shown once per install. */
16
+ export async function loadBusyTipSeen({ paths, projectRoot }) {
17
+ try {
18
+ return (await loadEffectiveConfig({ paths, projectRoot })).value.ui?.busyTipSeen === true;
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
24
+ /** Remember one choice in the user configuration. Throws what the writer throws; the caller says it was not saved. */
25
+ export async function saveUiPreference({ paths, projectRoot, patch }) {
26
+ const ui = {};
27
+ if (patch.theme)
28
+ ui.theme = patch.theme;
29
+ if (patch.busyInput)
30
+ ui.busyInput = patch.busyInput;
31
+ if (patch.reasoning)
32
+ ui.reasoning = patch.reasoning;
33
+ if (patch.busyTipSeen === true)
34
+ ui.busyTipSeen = true;
35
+ await writeConfigScope({ paths, projectRoot, scope: 'user', patch: { ui } });
36
+ }
37
+ /*
38
+ * The app's side (E-05: app.ts only wires): the busy-input mode as React state with a ref for the key handlers, the one
39
+ * read of the remembered choices when main.ts did not pass them (a choice the person makes meanwhile wins), the writes,
40
+ * and the one-time busy tip. `applyReasoning` shows or hides the reasoning (app state); `onSaveError` reports a write that
41
+ * failed (the choice still holds for this session).
42
+ */
43
+ export function useUiPreferences(React, { paths, projectRoot, given, applyReasoning, onSaveError }) {
44
+ const [busyInput, setBusyInputState] = React.useState(given.busyInput ?? DEFAULT_UI_PREFERENCES.busyInput);
45
+ const ref = React.useRef({ busyInput, touchedBusy: false, touchedReasoning: false, tipShown: false, onSaveError });
46
+ ref.current.busyInput = busyInput;
47
+ ref.current.onSaveError = onSaveError;
48
+ React.useEffect(() => {
49
+ if (given.busyInput && given.reasoning)
50
+ return;
51
+ let alive = true;
52
+ void loadUiPreferences({ paths, projectRoot }).then(prefs => {
53
+ if (!alive)
54
+ return;
55
+ if (!given.busyInput && !ref.current.touchedBusy) {
56
+ ref.current.busyInput = prefs.busyInput;
57
+ setBusyInputState(prefs.busyInput);
58
+ }
59
+ if (!given.reasoning && !ref.current.touchedReasoning)
60
+ applyReasoning(prefs.reasoning === 'visible');
61
+ });
62
+ return () => { alive = false; };
63
+ }, []);
64
+ /* R5a: a tip seen in an earlier launch is not shown again. */
65
+ React.useEffect(() => { let alive = true; void loadBusyTipSeen({ paths, projectRoot }).then(seen => { if (alive && seen)
66
+ ref.current.tipShown = true; }); return () => { alive = false; }; }, []);
67
+ const remember = (patch) => { void saveUiPreference({ paths, projectRoot, patch }).catch(error => ref.current.onSaveError(error)); };
68
+ return {
69
+ busyInput,
70
+ /** The mode now, for handlers that run outside a render (a key, a queued command). */
71
+ currentBusyInput: () => ref.current.busyInput,
72
+ setBusyInput(mode) { ref.current.touchedBusy = true; ref.current.busyInput = mode; setBusyInputState(mode); remember({ busyInput: mode }); },
73
+ /** Ctrl+T was pressed: `visible` is the state it leads to. */
74
+ rememberReasoning(visible) { ref.current.touchedReasoning = true; remember({ reasoning: visible ? 'visible' : 'collapsed' }); },
75
+ /** True the first time only, per install (the one-time busy tip, Q-R5-7; owner 2026-09-24). */
76
+ firstBusyEnter() { if (ref.current.tipShown)
77
+ return false; ref.current.tipShown = true; remember({ busyTipSeen: true }); return true; },
78
+ };
79
+ }
@@ -0,0 +1,77 @@
1
+ /*
2
+ * R5a lane S — S1 data wiring and the `/usage` view (owner decisions Q-R5-2, Q-R5-5, Q-R5-8; gate E1).
3
+ *
4
+ * `usageFacts` asks lane R's two frozen contracts — `modelProfile({provider,model})` and
5
+ * `usageSnapshot({state,contextStatus,profile})` — for what the status row and `/usage` show. Before R5a the layout replaced
6
+ * the turn's usage with the context status (`layoutState={...state,usage:contextStatus??state.usage}`, app.ts:1564 at
7
+ * 4676d2f59), so a reported `1200 in / 340 out` never reached the screen (gate finding 1); both now go to the snapshot.
8
+ * A contract body that throws is a figure nobody knows, never a crash of the screen (E1: unknown stays unknown).
9
+ * `/usage` is a view (Q-R5-8: it runs at once during a turn): tokens, the window with its source and date, the cost with
10
+ * its source or `cost unknown`, and the compaction count with the rule that produced it (E9: the 50 % threshold is stated
11
+ * where the window is known).
12
+ */
13
+ import { t } from "../i18n/index.js";
14
+ import { CONTEXT_ENGINE_MIN_WINDOW, modelProfile } from "../runtime/model-profile.js";
15
+ import { usageSnapshot } from "../runtime/usage-snapshot.js";
16
+ import { shellSafeText } from "./components/terminal-shell.js";
17
+ import { compactTokens, costText, percentText } from "./status-bar.js";
18
+ /** `ollama:qwen3` → {provider:'ollama',model:'qwen3'}; the part after the first colon is the provider's own model id (the
19
+ * shape runtime/talos-composition.ts `profileForSelection` matches). A sentinel (`none · /provider`) has no provider. */
20
+ export function splitModelId(id) {
21
+ const text = String(id ?? ''), at = text.indexOf(':');
22
+ if (at <= 0 || at === text.length - 1 || /\s/u.test(text))
23
+ return null;
24
+ return { provider: text.slice(0, at), model: text.slice(at + 1) };
25
+ }
26
+ export function usageFacts({ model, usage, contextStatus, compactions = 0 }) {
27
+ let profile = null;
28
+ const selected = splitModelId(model);
29
+ try {
30
+ profile = selected ? modelProfile(selected) : null;
31
+ }
32
+ catch {
33
+ profile = null;
34
+ }
35
+ let snapshot = null;
36
+ try {
37
+ snapshot = usageSnapshot({ state: { usage, compactions }, contextStatus, profile, model });
38
+ }
39
+ catch {
40
+ snapshot = null;
41
+ }
42
+ return { snapshot, profile };
43
+ }
44
+ const number = (value) => typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null;
45
+ const full = (value) => new Intl.NumberFormat('en-US').format(Math.round(value));
46
+ function sourceWords(profile) {
47
+ if (!profile)
48
+ return t('screen.usageView.sourceUnnamed');
49
+ const id = `screen.usageView.source.${profile.source}`;
50
+ const named = ['registry', 'provider', 'ollama-show', 'override', 'models.dev'].includes(profile.source) ? t(id) : t('screen.usageView.sourceUnnamed');
51
+ return profile.date ? t('screen.usageView.sourceDated', { source: named, date: shellSafeText(profile.date) }) : named;
52
+ }
53
+ /** The `/usage` block. Every figure nobody reported says so; nothing is computed here that lane R did not provide. */
54
+ export function usageViewText({ model, facts }) {
55
+ const { snapshot, profile } = facts;
56
+ const estimated = (text) => snapshot?.estimated ? t('screen.usageView.estimated', { text }) : text;
57
+ const input = number(snapshot?.inputTokens), output = number(snapshot?.outputTokens), cached = number(snapshot?.cachedTokens);
58
+ const unknown = t('screen.statusbar.unknown');
59
+ const tokens = input === null && output === null ? t('screen.usageView.tokensUnknown') : estimated(cached
60
+ ? t('screen.usageView.tokensCached', { input: input === null ? unknown : full(input), output: output === null ? unknown : full(output), cached: full(cached) })
61
+ : t('screen.usageView.tokens', { input: input === null ? unknown : full(input), output: output === null ? unknown : full(output) }));
62
+ const window = number(snapshot?.windowTokens), percent = number(snapshot?.percent), inUse = number(snapshot?.contextTokens);
63
+ const context = window === null || window === 0 ? t('screen.usageView.contextUnknown')
64
+ : inUse === null || percent === null ? t('screen.usageView.contextUsedUnknown', { window: compactTokens(window), source: sourceWords(profile) })
65
+ : t('screen.usageView.context', { used: estimated(full(inUse)), window: full(window), percent: percentText(percent), source: sourceWords(profile) });
66
+ const usd = number(snapshot?.costUsd), costSource = typeof snapshot?.costSource === 'string' && snapshot.costSource.trim() ? shellSafeText(snapshot.costSource) : null;
67
+ const cost = snapshot?.local ? t('screen.usageView.local') : usd !== null && costSource ? t('screen.usageView.cost', { cost: costText(usd), source: costSource }) : t('screen.usageView.costUnknown');
68
+ const compactions = number(snapshot?.compactions) ?? 0;
69
+ const rule = window && window >= CONTEXT_ENGINE_MIN_WINDOW ? t('screen.usageView.compactionsEarly', { count: compactions }) : window ? t('screen.usageView.compactionsSmallWindow', { count: compactions }) : t('screen.usageView.compactionsKernel', { count: compactions });
70
+ return t('screen.usageView.block', { model: shellSafeText(model), tokens, context, cost, compactions: rule });
71
+ }
72
+ /** R5a (owner 2026-09-24): said once per model when its known window is below the Context Engine's 64k; null otherwise. */
73
+ export function smallWindowNotice(profile, model) {
74
+ if (!profile || profile.windowTokens >= CONTEXT_ENGINE_MIN_WINDOW)
75
+ return null;
76
+ return t('screen.context.smallWindow', { model: shellSafeText(model), window: compactTokens(profile.windowTokens), min: compactTokens(CONTEXT_ENGINE_MIN_WINDOW) });
77
+ }