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,214 @@
1
+ import { en, t } from "../../i18n/index.js";
2
+ import { displayWidth, truncateDisplay } from "../text-width.js";
3
+ import { sanitizeStatus } from "./footer.js";
4
+ function validTerminalDimension(value, fallback) { const measured = Number(value); return Number.isFinite(measured) && measured > 0 ? Math.max(1, Math.floor(measured)) : Math.max(1, Math.floor(fallback)); }
5
+ export function terminalSizeSnapshot(stream, fallback) {
6
+ return { rows: validTerminalDimension(stream?.rows, fallback.rows), columns: validTerminalDimension(stream?.columns, fallback.columns) };
7
+ }
8
+ export function createTerminalSizeStore(stream, fallback = { rows: 30, columns: 100 }, onError) {
9
+ let snapshot = terminalSizeSnapshot(stream, fallback), disposed = false;
10
+ const listeners = new Set();
11
+ const onResize = () => { if (disposed)
12
+ return; try {
13
+ const next = terminalSizeSnapshot(stream, snapshot);
14
+ if (next.rows === snapshot.rows && next.columns === snapshot.columns)
15
+ return;
16
+ snapshot = next;
17
+ for (const listener of [...listeners])
18
+ listener(snapshot);
19
+ }
20
+ catch (error) {
21
+ try {
22
+ onError?.(error);
23
+ }
24
+ catch { /* resize diagnostics cannot break rendering */ }
25
+ } };
26
+ try {
27
+ stream?.on?.('resize', onResize);
28
+ }
29
+ catch (error) {
30
+ try {
31
+ onError?.(error);
32
+ }
33
+ catch { /* best effort */ }
34
+ }
35
+ return {
36
+ getSnapshot: () => snapshot,
37
+ subscribe(listener) { if (disposed)
38
+ return () => { }; listeners.add(listener); return () => listeners.delete(listener); },
39
+ dispose() { if (disposed)
40
+ return; disposed = true; listeners.clear(); try {
41
+ if (typeof stream?.off === 'function')
42
+ stream.off('resize', onResize);
43
+ else
44
+ stream?.removeListener?.('resize', onResize);
45
+ }
46
+ catch (error) {
47
+ try {
48
+ onError?.(error);
49
+ }
50
+ catch { /* best effort */ }
51
+ } },
52
+ };
53
+ }
54
+ const TERMINAL_UNSAFE = /[\x00-\x1f\x7f-\x9f\u2028\u2029\p{Bidi_Control}\p{Default_Ignorable_Code_Point}]/gu;
55
+ export function shellSafeText(value) {
56
+ return String(value ?? '')
57
+ .replace(TERMINAL_UNSAFE, character => {
58
+ if (character === '\n' || character === '\r' || character === '\t')
59
+ return ' ';
60
+ const code = character.codePointAt(0);
61
+ return code > 0xffff ? `\\u{${code.toString(16)}}` : `\\u${code.toString(16).padStart(4, '0')}`;
62
+ })
63
+ .replace(/\s{2,}/gu, ' ')
64
+ .trim();
65
+ }
66
+ function fitStrip(fields, width) {
67
+ const limit = Math.max(0, Math.floor(width));
68
+ if (limit === 0)
69
+ return '';
70
+ const kept = fields.map(field => ({ ...field, text: shellSafeText(field.text) })).filter(field => field.text);
71
+ const render = () => kept.map(field => field.text).join(' · ');
72
+ while (kept.length && displayWidth(render()) > limit) {
73
+ let lowest = 0;
74
+ for (let index = 1; index < kept.length; index++)
75
+ if (kept[index].priority < kept[lowest].priority)
76
+ lowest = index;
77
+ kept.splice(lowest, 1);
78
+ }
79
+ return truncateDisplay(render(), limit);
80
+ }
81
+ /* R4 Q-R4-12: the permission mode as the person reads it (`manual`, `accept edits`, `plan`, `auto`, `don't ask`,
82
+ `Full access`); the id stays what the runtime and the config use. A mode this table does not know is shown as it came. */
83
+ export function permissionModeLabel(mode) {
84
+ const id = `screen.mode.${mode}`;
85
+ return Object.hasOwn(en, id) ? t(id) : shellSafeText(mode);
86
+ }
87
+ /*
88
+ * R5a S5 (owner decision Q-R5-7; gate E8, finding 8): while a turn runs and the busy-input mode is known, the strip leads
89
+ * with what Enter does in that mode ("Enter queues · Alt+S steers · Esc interrupts") — one field, never split — then the
90
+ * mode in its short form, so both fit at 60 columns. Without a mode (callers before R5a) the strip is as it was.
91
+ */
92
+ export function terminalShortcutStrip({ mode, running, queueCount, width, busyInput }) {
93
+ if (running && busyInput)
94
+ return fitStrip([
95
+ { text: t(`screen.busyLine.${busyInput}`), priority: 100 },
96
+ { text: t('screen.shortcut.modeShort', { mode: permissionModeLabel(mode) }), priority: 95 },
97
+ ...(queueCount > 0 ? [{ text: t('screen.shortcut.queue', { count: queueCount }), priority: 90 }] : []),
98
+ { text: t('screen.shortcut.transcript'), priority: 70 },
99
+ ], width);
100
+ return fitStrip([
101
+ { text: t('screen.shortcut.mode', { mode: permissionModeLabel(mode) }), priority: 100 },
102
+ ...(running ? [{ text: t('screen.shortcut.interrupt'), priority: 95 }] : []),
103
+ ...(queueCount > 0 ? [{ text: t('screen.shortcut.queue', { count: queueCount }), priority: 90 }] : []),
104
+ { text: t('screen.shortcut.transcript'), priority: 70 },
105
+ { text: t('screen.shortcut.help'), priority: 60 },
106
+ ], width);
107
+ }
108
+ function busyLabel(kind) {
109
+ if (kind === 'idle')
110
+ return t('screen.status.ready');
111
+ if (kind === 'error')
112
+ return t('screen.status.error');
113
+ return t(`screen.busy.${kind}`);
114
+ }
115
+ /* R2: the outcome words of a roster row; an outcome this table does not know is shown as it came (it is an English id). */
116
+ function outcomeLabel(outcome) {
117
+ const id = `screen.outcome.${outcome}`;
118
+ return Object.hasOwn(en, id) ? t(id) : outcome;
119
+ }
120
+ /* R5a S9 (review D-14): the model now leads the status row (Q-R5-2), so the main roster row may leave it out (`model` null). */
121
+ export function mainRosterLine({ kind, model, width, marker = '●' }) {
122
+ const line = model === null ? t('screen.roster.mainStatus', { marker, status: busyLabel(kind) }) : t('screen.roster.main', { marker, status: busyLabel(kind), model });
123
+ return truncateDisplay(shellSafeText(line), Math.max(0, Math.floor(width)));
124
+ }
125
+ export function queueRosterLine({ count, preview, width, marker = '○' }) {
126
+ if (count <= 0)
127
+ return '';
128
+ const suffix = preview ? ` · ${shellSafeText(preview)}` : '';
129
+ return truncateDisplay(shellSafeText(t('screen.roster.queue', { marker, count, preview: suffix })), Math.max(0, Math.floor(width)));
130
+ }
131
+ function parsedMs(value) {
132
+ if (!value)
133
+ return null;
134
+ const measured = Date.parse(value);
135
+ return Number.isFinite(measured) ? measured : null;
136
+ }
137
+ export function agentDurationLabel(row, nowMs) {
138
+ const start = parsedMs(row.startedAt);
139
+ if (start === null)
140
+ return null;
141
+ const explicitEnd = parsedMs(row.endedAt);
142
+ const end = explicitEnd ?? (row.outcome === 'running' && Number.isFinite(nowMs) ? nowMs : null);
143
+ if (end === null || end < start)
144
+ return null;
145
+ const seconds = Math.floor((end - start) / 1000);
146
+ if (seconds < 60)
147
+ return `${seconds}s`;
148
+ const minutes = Math.floor(seconds / 60), rest = seconds % 60;
149
+ if (minutes < 60)
150
+ return `${minutes}m ${rest}s`;
151
+ const hours = Math.floor(minutes / 60), minuteRest = minutes % 60;
152
+ return `${hours}h ${minuteRest}m`;
153
+ }
154
+ /* R3 Q-R3-7 (gate E7): `status` = the CLI's own activity word for the main row while a turn is open; the kernel list says
155
+ `unknown` or `running` then (measured, gate §0.3). Without it the kernel's outcome word stays. */
156
+ export function agentRosterLine({ row, width, nowMs, marker, status: activity, hideModel = false }) {
157
+ const indent = ' '.repeat(Math.max(0, Math.min(8, row.level)));
158
+ const pointer = marker ?? (row.focused ? '›' : '·');
159
+ const identity = row.role === 'main' ? t('screen.roster.mainName') : row.name ? t('screen.roster.agentNamed', { name: shellSafeText(row.name) }) : t('screen.roster.agent');
160
+ const status = activity ?? outcomeLabel(row.interrupted ? 'interrupted' : row.outcome);
161
+ const duration = agentDurationLabel(row, nowMs);
162
+ const task = row.task ? ` · ${truncateDisplay(shellSafeText(row.task), 32)}` : '';
163
+ const elapsed = duration ? ` · ${duration}` : '';
164
+ const model = row.model && !hideModel ? ` · ${t('screen.roster.model', { model: shellSafeText(row.model) })}` : '';
165
+ const collision = row.hasCollision ? ` · ${t('screen.roster.collision', { count: row.collisionCount })}` : '';
166
+ const body = shellSafeText(`${pointer} ${identity} · ${status}${collision}${elapsed}${model}${task}`);
167
+ return truncateDisplay(`${indent}${body}`, Math.max(0, Math.floor(width)));
168
+ }
169
+ export function edgeRosterLine({ left, right, width }) {
170
+ const limit = Math.max(0, Math.floor(width));
171
+ if (limit === 0)
172
+ return '';
173
+ const safeLeft = shellSafeText(left);
174
+ const safeRight = shellSafeText(right ?? '');
175
+ if (!safeRight)
176
+ return truncateDisplay(safeLeft, limit);
177
+ const leftWidth = displayWidth(safeLeft), rightWidth = displayWidth(safeRight);
178
+ if (leftWidth + 1 + rightWidth > limit)
179
+ return truncateDisplay(safeLeft, limit);
180
+ return `${safeLeft}${' '.repeat(Math.max(1, limit - leftWidth - rightWidth))}${safeRight}`;
181
+ }
182
+ export function residualFooterFields(input) {
183
+ return [
184
+ ...(input.unseen ? [{ id: 'unseen', text: t('screen.footer.unseen', { count: input.unseen }), priority: 95 }] : []),
185
+ ...(input.sessionId ? [{ id: 'session', text: t('screen.footer.session', { id: sanitizeStatus(input.sessionId) }), priority: 70 }] : []),
186
+ { id: 'workspace', text: sanitizeStatus(input.workspace), priority: 50 },
187
+ ];
188
+ }
189
+ export const WIN32_FRAME_HEIGHT = 'rows-1';
190
+ export function frameRowsFor(rows, platform = process.platform, policy = WIN32_FRAME_HEIGHT) {
191
+ const measured = Math.max(1, Math.floor(Number(rows) || 1));
192
+ return platform === 'win32' && policy === 'rows-1' ? Math.max(1, measured - 1) : measured;
193
+ }
194
+ /*
195
+ * P11 (owner, 2026-09-25, "changing theme crashes the process"): Ink's Box wraps its children in a background Provider
196
+ * ONLY when it has a colour (ink/build/components/Box.js:27). `mono` has no background, so crossing it in /theme changed
197
+ * the tree's shape, React remounted the whole app, and its unmount closed the session controller (app.ts, the
198
+ * `controller.close()` cleanup): the conversation was lost. When the session paints the background the frame therefore
199
+ * ALWAYS has a background colour; for a theme without one it is a name chalk does not know, which Ink leaves unpainted
200
+ * (ink/build/colorize.js returns the text unchanged), so mono stays colourless and the shape never changes.
201
+ */
202
+ export const UNPAINTED_BACKGROUND = 'transparent';
203
+ export function terminalFrameProps({ session, background, rows, columns, platform = process.platform, }) {
204
+ if (session.mode === 'plain')
205
+ return { flexDirection: 'column' };
206
+ const width = Math.max(1, Math.floor(columns));
207
+ const height = frameRowsFor(rows, platform);
208
+ return {
209
+ flexDirection: 'column',
210
+ width,
211
+ height,
212
+ ...(session.paintBackground ? { backgroundColor: background || UNPAINTED_BACKGROUND } : {}),
213
+ };
214
+ }
@@ -0,0 +1,19 @@
1
+ import { t } from "../../i18n/index.js";
2
+ import { formatDuration } from "../tools/shared.js";
3
+ export function thinkingRowText(text, visible) { return visible && text ? text : null; }
4
+ /*
5
+ * R5a S8 (owner decision Q-R5-9; gate §4, finding 9): reasoning is collapsed by default, and a collapsed block leaves ONE
6
+ * dim line, "Thought for 2.8 s · Ctrl+T shows" (Claude Code `thought for Ns`, bytes 2.1.281, 2026-09-23; Pi keeps the
7
+ * choice, `docs/settings.md:17`, 2026-09-22). While the block streams nothing is added: the status row already says
8
+ * "thinking". A block with no live timing (a replayed session) says "Thought" without a number (E1).
9
+ */
10
+ export function thoughtLine(item) {
11
+ const { startedAt, endedAt } = item;
12
+ if (typeof startedAt !== 'number' || typeof endedAt !== 'number' || endedAt < startedAt)
13
+ return t('screen.thought.noDuration');
14
+ return t('screen.thought.duration', { duration: formatDuration(endedAt - startedAt) });
15
+ }
16
+ /** What the transcript draws: no `run` rows (Q-R3-9); with reasoning collapsed, a block only once it has ended. */
17
+ export function visibleTranscriptItems(items, reasoningVisible) {
18
+ return items.filter(item => item.kind !== 'run' && (reasoningVisible || item.kind !== 'reasoning' || !item.streaming));
19
+ }
@@ -0,0 +1,97 @@
1
+ import { t, tn } from "../../i18n/index.js";
2
+ import { looksItalian } from "../../i18n/kernel-map.js";
3
+ import { rendererKindFor } from "../tool-display.js";
4
+ import { renderTool } from "../tools/registry.js";
5
+ import { lineCount, sanitizeDisplayText } from "../tools/shared.js";
6
+ import { displayWidth, truncateMiddle } from "../text-width.js";
7
+ const SEPARATOR = ' · ';
8
+ /* A target cut narrower than this names nothing (row-format.ts keeps 12 for the same reason). */
9
+ const TARGET_MIN = 12;
10
+ const number = (value) => new Intl.NumberFormat('en-US').format(Math.round(value));
11
+ const clean = (value) => sanitizeDisplayText(String(value ?? '')).replace(/\s*\n\s*/gu, ' ').trim();
12
+ /*
13
+ * R5a S10 (owner decision Q-R5-17; gate finding 15): why a tool did NOT run, from lane R's `facts.denial`. A rule or the
14
+ * policy deciding alone names itself (`denied by rule …`, `denied by policy: …`); "you did not approve it" is written only
15
+ * when the person was asked and said no — `denial.by === 'person'`, or, before lane R's facts arrive, a row whose
16
+ * approval dialog this transcript opened. A reason that is not English is not shown (E14); the kernel's Italian `via` and
17
+ * reason are taken off the evidence of a denied row, so its expanded view has no Italian either.
18
+ */
19
+ export function denialSentence(denial) {
20
+ if (!denial)
21
+ return null;
22
+ const reason = denial.reason && !looksItalian(denial.reason) ? clean(denial.reason).replace(/\.$/u, '') : '';
23
+ if (denial.by === 'person')
24
+ return t('tool.denied.person');
25
+ if (denial.by === 'rule') {
26
+ const rule = clean(denial.rule);
27
+ if (!rule)
28
+ return reason ? t('tool.denied.policy', { reason }) : t('tool.denied.policyNoReason');
29
+ const scope = clean(denial.scope);
30
+ return scope ? t('tool.denied.ruleScoped', { rule, scope }) : t('tool.denied.rule', { rule });
31
+ }
32
+ if (denial.by === 'mode')
33
+ return reason ? t('tool.denied.mode', { reason }) : t('tool.denied.modeNoReason');
34
+ return reason ? t('tool.denied.policy', { reason }) : t('tool.denied.policyNoReason');
35
+ }
36
+ function deniedRow(row) {
37
+ if (row.status !== 'denied')
38
+ return row;
39
+ const denial = row.facts?.denial ?? null;
40
+ let sentence = denialSentence(denial);
41
+ /* No decider named yet: the kernel's "you did not approve it" stands only if the person was asked; otherwise the row says
42
+ only what is known, that the action was refused (a policy deny reaches the kernel as the same "no", gate finding 15). */
43
+ if (!sentence && !row.askedPerson && String(row.finalOutput ?? '').split(/\r?\n/u)[0]?.trim() === t('error.refused.notApproved'))
44
+ sentence = t('error.refused.generic');
45
+ const outcome = row.outcome && (row.outcome.permissionVia !== null || row.outcome.permissionReason !== null) ? { ...row.outcome, permissionVia: null, permissionReason: null } : row.outcome;
46
+ return { ...row, ...(sentence ? { finalOutput: sentence } : {}), ...(outcome !== row.outcome ? { outcome } : {}) };
47
+ }
48
+ /*
49
+ * R5a S7 (owner decision Q-R5-11; gate finding 4): a read the kernel cut says so on its row, `450 of 500 lines · cut at
50
+ * 8,000 characters`, in place of the line count of what came back (which counted a partial last line). The row is fitted
51
+ * again: the target is cut in the middle to make room; when that would leave the target narrower than 12 columns (60
52
+ * columns and a long path), `cut at …` moves to the first detail line instead. The height estimate measures this same row.
53
+ */
54
+ function withReadCut(row, result, width) {
55
+ const cut = row.readCut;
56
+ if (!cut || row.status !== 'completed' || rendererKindFor(row.name) !== 'read')
57
+ return result;
58
+ const lines = cut.totalLines === null ? tn('tool.result.lines', cut.shownLines, { count: number(cut.shownLines) }) : t('tool.read.cutLines', { shown: number(cut.shownLines), total: number(cut.totalLines) });
59
+ const cutAt = t('tool.read.cutAt', { limit: number(cut.limitChars) });
60
+ const counted = tn('tool.result.lines', lineCount(row.finalOutput));
61
+ const index = result.facts.indexOf(counted);
62
+ const compose = (added) => {
63
+ const facts = index >= 0 ? [...result.facts.slice(0, index), ...added, ...result.facts.slice(index + 1)] : [...result.facts, ...added];
64
+ const statusLabel = facts.join(SEPARATOR);
65
+ const lineOf = (shown) => result.label + (shown ? ' ' + shown : '') + SEPARATOR + statusLabel;
66
+ const room = Number.isFinite(width) ? width - displayWidth(lineOf('')) - 1 : Number.POSITIVE_INFINITY;
67
+ return { facts, statusLabel, room, target: !result.target || displayWidth(lineOf(result.target)) <= width ? result.target : room >= 1 ? truncateMiddle(result.target, room) : '…' };
68
+ };
69
+ /* Both facts on the row while the target keeps a readable width; otherwise the cut goes to the first detail line. */
70
+ let fitted = compose([lines, cutAt]), detailLines = result.detailLines;
71
+ if (result.target && fitted.target !== result.target && fitted.room < TARGET_MIN) {
72
+ fitted = compose([lines]);
73
+ detailLines = [cutAt, ...detailLines];
74
+ }
75
+ const title = result.label + (fitted.target ? ' ' + fitted.target : '');
76
+ return { ...result, title, statusLabel: fitted.statusLabel, line: title + SEPARATOR + fitted.statusLabel, target: fitted.target, facts: fitted.facts, detailLines };
77
+ }
78
+ /*
79
+ * R5a S6 (owner decision Q-R5-10; gate E6): a command output the executor kept in a private file says how much the row
80
+ * holds and where the whole output is — the first detail line, in both views, so the way back is always under the row.
81
+ */
82
+ function withSavedOutput(row, result) {
83
+ const saved = row.savedOutput;
84
+ if (!saved)
85
+ return result;
86
+ const line = clean(t('tool.output.saved', { kept: number(saved.keptChars), total: number(saved.totalChars), path: saved.path }));
87
+ return { ...result, detailLines: [line, ...result.detailLines] };
88
+ }
89
+ /* R3 gate E1: the ONE tool row (tools/row-format.ts). app.ts draws `title + ' · ' + statusLabel` (= `line`) and the dim
90
+ `detailLines`; the height estimate, the search/copy text and the exit transcript read the same result. `now` (optional,
91
+ default the clock) is the time a running row's elapsed is measured at, so a caller that re-renders once a second can
92
+ pass its own tick and a test can pin it. R5a: the denial, the read cut and the kept output are added here, once, for
93
+ every one of those readers. */
94
+ export function toolRowView(row, width, expanded, now) {
95
+ const shown = deniedRow(row);
96
+ return withSavedOutput(shown, withReadCut(shown, renderTool(shown, width, expanded, now), width));
97
+ }
@@ -0,0 +1,165 @@
1
+ import { t, tn } from "../../i18n/index.js";
2
+ import { truncateDisplay, wrapDisplay } from "../text-width.js";
3
+ import { toolRowView } from "./tool-row.js";
4
+ import { thoughtLine } from "./thinking-row.js";
5
+ import { transcriptItemRaw } from "../transcript-model.js";
6
+ const MAX_TRANSCRIPT_MEASUREMENTS = 4096;
7
+ export function transcriptMeasurementKey(itemId, width, variant) {
8
+ const normalizedWidth = Math.max(1, Math.floor(Number.isFinite(width) ? width : 1));
9
+ return JSON.stringify([String(itemId), normalizedWidth, String(variant)]);
10
+ }
11
+ export function createTranscriptMeasurementStore() {
12
+ let snapshot = { version: 0 };
13
+ const heights = new Map();
14
+ const listeners = new Set();
15
+ const publish = () => { snapshot = { version: snapshot.version + 1 }; for (const listener of [...listeners])
16
+ listener(); };
17
+ return {
18
+ getSnapshot: () => snapshot,
19
+ subscribe(listener) { listeners.add(listener); return () => listeners.delete(listener); },
20
+ height(key) { return heights.get(String(key)) ?? null; },
21
+ measure(key, height) {
22
+ const id = String(key);
23
+ if (!id || !Number.isFinite(height) || height <= 0)
24
+ return false;
25
+ const normalized = Math.max(1, Math.ceil(height));
26
+ if (heights.get(id) === normalized)
27
+ return false;
28
+ if (!heights.has(id) && heights.size >= MAX_TRANSCRIPT_MEASUREMENTS) {
29
+ const oldest = heights.keys().next().value;
30
+ if (typeof oldest === 'string')
31
+ heights.delete(oldest);
32
+ }
33
+ heights.set(id, normalized);
34
+ publish();
35
+ return true;
36
+ },
37
+ };
38
+ }
39
+ export function planTranscriptVirtualWindow(input) {
40
+ if (input.scrollRows !== undefined) {
41
+ const rows = planTranscriptRows({ items: input.items, rowBudget: input.rowBudget, scrollRows: input.scrollRows, heightOf: input.heightOf, ...(input.revealIndex !== undefined ? { revealIndex: input.revealIndex } : {}) });
42
+ return { ...rows, estimatedRows: rows.totalRows };
43
+ }
44
+ const { items, heightOf } = input;
45
+ const offset = Number.isFinite(input.offset) ? Math.max(0, Math.floor(input.offset)) : 0;
46
+ const end = Math.max(0, Math.min(items.length, items.length - offset));
47
+ if (end === 0)
48
+ return { start: 0, end: 0, rows: [], estimatedRows: 0 };
49
+ const budget = Number.isFinite(input.rowBudget) ? Math.max(1, Math.floor(input.rowBudget)) : 1;
50
+ let start = end;
51
+ let used = 0;
52
+ while (start > 0) {
53
+ const index = start - 1;
54
+ const raw = Number(heightOf(items[index], index));
55
+ const height = Number.isFinite(raw) && raw > 0 ? Math.max(1, Math.ceil(raw)) : 1;
56
+ if (start < end && used + height > budget)
57
+ break;
58
+ start = index;
59
+ used += height;
60
+ if (used >= budget)
61
+ break;
62
+ }
63
+ const revealIndex = Number.isFinite(input.revealIndex) ? Math.floor(input.revealIndex) : -1;
64
+ const legacyPageSize = Number.isFinite(input.legacyPageSize) ? Math.max(1, Math.floor(input.legacyPageSize)) : 0;
65
+ if (revealIndex >= 0 && revealIndex < start && revealIndex < end && legacyPageSize > 0) {
66
+ const legacyStart = Math.max(0, end - legacyPageSize);
67
+ if (revealIndex >= legacyStart) {
68
+ let legacyRows = 0;
69
+ for (let index = legacyStart; index < end; index++) {
70
+ const raw = Number(heightOf(items[index], index));
71
+ legacyRows += Number.isFinite(raw) && raw > 0 ? Math.max(1, Math.ceil(raw)) : 1;
72
+ }
73
+ return { start: legacyStart, end, rows: items.slice(legacyStart, end), estimatedRows: legacyRows };
74
+ }
75
+ }
76
+ return { start, end, rows: items.slice(start, end), estimatedRows: used };
77
+ }
78
+ /* R1 gate E4: the rows a text takes at `width`, counted with the width function Ink lays out with (text-width.ts). It
79
+ replaces the old `ceil(width / (0.75 × columns))` estimate, which under-counted prose and let the planner walk back
80
+ into rows that did not exist (review D-04). Only items never mounted use it: a mounted item reports its measured
81
+ height (`useBoxMetrics`), and a measurement always wins. */
82
+ export function textRows(text, width) { return Math.max(1, wrapDisplay(String(text), Math.max(1, Math.floor(width))).length); }
83
+ /*
84
+ * R1 (Q-R1-2, E9, E15): a ROW-based window. `scrollRows` counts rows scrolled up from the newest row; the region that
85
+ * renders the window is bottom-anchored with its overflow hidden, so the mounted items may be taller than the view and
86
+ * the view still shows exactly `rowBudget` rows: those between `scrollRows` and `scrollRows + rowBudget` from the
87
+ * bottom. Unlike the item-offset planner above, the top of an item taller than the screen is reachable with PageUp.
88
+ */
89
+ export function planTranscriptRows(input) {
90
+ const { items } = input;
91
+ const budget = Number.isFinite(input.rowBudget) ? Math.max(0, Math.floor(input.rowBudget)) : 0;
92
+ const heights = items.map((item, index) => { const raw = Number(input.heightOf(item, index)); return Number.isFinite(raw) && raw > 0 ? Math.max(1, Math.ceil(raw)) : 1; });
93
+ const below = new Array(items.length);
94
+ let acc = 0;
95
+ for (let index = items.length - 1; index >= 0; index--) {
96
+ below[index] = acc;
97
+ acc += heights[index];
98
+ }
99
+ const totalRows = acc, maxScroll = Math.max(0, totalRows - budget);
100
+ let scroll = Number.isFinite(input.scrollRows) ? Math.max(0, Math.min(maxScroll, Math.floor(input.scrollRows))) : 0;
101
+ const reveal = Number.isFinite(input.revealIndex) ? Math.floor(input.revealIndex) : -1;
102
+ if (reveal >= 0 && reveal < items.length && budget > 0) {
103
+ /* A search or jump target is brought into view: its first row at the top of the view when it is taller than it. */
104
+ const top = below[reveal] + heights[reveal], bottom = below[reveal];
105
+ if (top > scroll + budget || bottom < scroll)
106
+ scroll = Math.max(0, Math.min(maxScroll, top - budget));
107
+ }
108
+ const firstRowFromBottom = (index) => below[index] + heights[index] - 1;
109
+ const rowsBelow = (index) => below[index];
110
+ if (items.length === 0 || budget === 0)
111
+ return { start: items.length, end: items.length, rows: [], clipBelow: 0, hiddenAbove: items.length, totalRows, maxScroll, scrollRows: scroll, firstRowFromBottom, rowsBelow };
112
+ let end = items.length;
113
+ while (end > 0 && below[end - 1] + heights[end - 1] <= scroll)
114
+ end--;
115
+ let start = end;
116
+ while (start > 0 && below[start - 1] < scroll + budget)
117
+ start--;
118
+ const clipBelow = end > 0 ? Math.max(0, scroll - below[end - 1]) : 0;
119
+ return { start, end, rows: items.slice(start, end), clipBelow, hiddenAbove: start, totalRows, maxScroll, scrollRows: scroll, firstRowFromBottom, rowsBelow };
120
+ }
121
+ /*
122
+ * R1 E9 / Q-R1-9: the one dim row that keeps the person's message in sight. When the latest user message starts above
123
+ * the view it is named ("You: …"); when whole messages are above the view they are counted. Returns null when neither
124
+ * is true, so the row costs nothing on a short conversation.
125
+ */
126
+ export function transcriptAnnouncement(input) {
127
+ const parts = [];
128
+ if (input.hiddenAbove > 0) {
129
+ const messages = input.hiddenMessagesAbove;
130
+ parts.push(messages > 0 ? tn('screen.transcript.earlierMessages', messages) : tn('screen.transcript.earlierItems', input.hiddenAbove));
131
+ }
132
+ if (input.promptAboveView && input.latestPrompt) {
133
+ const prompt = input.latestPrompt.replace(/\s+/gu, ' ').trim();
134
+ if (prompt)
135
+ parts.push(t('screen.transcript.youSaid', { prompt }));
136
+ }
137
+ if (!parts.length)
138
+ return null;
139
+ const text = parts.length === 1 && !parts[0].startsWith('↑') ? `↑ ${parts[0]}` : parts.join(' · ');
140
+ return truncateDisplay(text, Math.max(1, input.width));
141
+ }
142
+ /* Rows an item takes before it has been measured. Exact for plain text, raw mode, warnings, run rows and tools (the
143
+ same line lists the renderer draws, wrapped at the same width); a Markdown message is counted from its source text
144
+ and corrected by the first measurement (E4). */
145
+ export function estimateTranscriptItemRows(item, width, options = {}) {
146
+ const normalizedWidth = Math.max(1, Math.floor(Number.isFinite(width) ? width : 80));
147
+ if (options.raw)
148
+ return textRows(transcriptItemRaw(item), normalizedWidth);
149
+ if (item.kind === 'message')
150
+ return 1 + textRows(item.text, normalizedWidth);
151
+ /* R5a S8 (Q-R5-9): a collapsed reasoning block is its one dim line (components/thinking-row.ts). */
152
+ if (item.kind === 'reasoning')
153
+ return options.reasoningVisible === false ? textRows(thoughtLine(item), normalizedWidth) : 3 + textRows(item.text || ' ', Math.max(1, normalizedWidth - 2));
154
+ /* R3 gate E1: the tool row's height comes from the ONE row formatter — the same header line app.ts draws (`line` is
155
+ `title · statusLabel`) and the same detail lines — so the estimate and the render cannot disagree. */
156
+ if (item.kind === 'tool') {
157
+ const view = toolRowView(item, normalizedWidth, Boolean(options.expandedTools), options.now);
158
+ return textRows(view.line, normalizedWidth) + view.detailLines.reduce((rows, line) => rows + textRows(line, normalizedWidth), 0);
159
+ }
160
+ /* A warning, and since R3 (Q-R3-9) the one outcome line of a turn that did not end normally (a warning with `outcome`). */
161
+ if (item.kind === 'warning')
162
+ return textRows(item.text, normalizedWidth);
163
+ /* Legacy `run` items are no longer produced (Q-R3-9); counted the way app.ts still draws one. */
164
+ return textRows(t('screen.transcript.run', { status: item.status }) + (item.message ? ' · ' + item.message : ''), normalizedWidth);
165
+ }
@@ -0,0 +1,43 @@
1
+ import { t } from "../../i18n/index.js";
2
+ import { sanitizeTranscriptText } from "../transcript-model.js";
3
+ export function reduceViewport(view, event, total) {
4
+ if (event === 'jump-tail')
5
+ return { ...view, offset: 0, unseen: 0, followingTail: true };
6
+ if (event === 'new-content')
7
+ return view.followingTail ? { ...view, offset: 0 } : { ...view, unseen: view.unseen + 1 };
8
+ if (event === 'page-up') {
9
+ const max = Math.max(0, total - view.pageSize);
10
+ const offset = Math.min(max, view.offset + view.pageSize);
11
+ return { ...view, offset, followingTail: false };
12
+ }
13
+ const offset = Math.max(0, view.offset - view.pageSize);
14
+ return { ...view, offset, followingTail: offset === 0, unseen: offset === 0 ? 0 : view.unseen };
15
+ }
16
+ export function transcriptWindow(rows, view) {
17
+ const end = Math.max(0, rows.length - Math.max(0, view.offset));
18
+ const start = Math.max(0, end - Math.max(1, view.pageSize));
19
+ return rows.slice(start, end);
20
+ }
21
+ /* R5a S3: a compaction notice is a new row too. */
22
+ export function transcriptEventAddsItem(event) { return event.type === 'run.started' || event.type === 'message.started' || event.type === 'reasoning.started' || event.type === 'tool.started' || event.type === 'warning' || event.type === 'context.compacted' || event.type === 'context.compaction.paused'; }
23
+ export const TRANSCRIPT_COPY_MAX_BYTES = 1024 * 1024;
24
+ export class TranscriptCopyError extends Error {
25
+ code;
26
+ constructor(code, message = code) { super(message); this.name = 'TranscriptCopyError'; this.code = code; }
27
+ }
28
+ export function transcriptViewportForIndex(view, total, index) {
29
+ const count = Math.max(0, Math.trunc(total)), pageSize = Math.max(1, Math.trunc(view.pageSize));
30
+ if (count === 0)
31
+ return { ...view, offset: 0, unseen: 0, followingTail: true };
32
+ const target = Math.max(0, Math.min(count - 1, Math.trunc(index)));
33
+ const maxOffset = Math.max(0, count - pageSize);
34
+ const offset = Math.min(maxOffset, Math.max(0, count - 1 - target));
35
+ return { ...view, offset, followingTail: offset === 0, unseen: offset === 0 ? 0 : view.unseen };
36
+ }
37
+ export function osc52CopySequence(text) {
38
+ const safe = sanitizeTranscriptText(String(text));
39
+ const bytes = Buffer.byteLength(safe, 'utf8');
40
+ if (bytes > TRANSCRIPT_COPY_MAX_BYTES)
41
+ throw new TranscriptCopyError('TRANSCRIPT_COPY_LIMIT', t('screen.copy.tooLarge'));
42
+ return '\u001b]52;c;' + Buffer.from(safe, 'utf8').toString('base64') + '\u0007';
43
+ }
@@ -0,0 +1,104 @@
1
+ import { createTuiEventAdapter } from "./event-adapter.js";
2
+ /** The sessions below `rootId`, at every level, from a session list. */
3
+ export function descendantsOf(rows, rootId) {
4
+ const idOf = (row) => typeof row.sessionId === 'string' && row.sessionId ? row.sessionId : typeof row.id === 'string' && row.id ? row.id : null;
5
+ const below = new Set([rootId]), found = [];
6
+ for (let grew = true; grew;) {
7
+ grew = false;
8
+ for (const row of rows) {
9
+ const id = idOf(row);
10
+ if (!id || below.has(id) || typeof row.parentId !== 'string' || !below.has(row.parentId))
11
+ continue;
12
+ below.add(id);
13
+ grew = true;
14
+ found.push({ sessionId: id, task: typeof row.delegatedTask === 'string' && row.delegatedTask.trim() ? row.delegatedTask : null, depth: typeof row.depth === 'number' ? row.depth : 1 });
15
+ }
16
+ }
17
+ return found;
18
+ }
19
+ export function createDescendantApprovals({ runtime, secretValues = [], intervalMs = 500, onRequired, onResolved, onError = () => { } }) {
20
+ let rootId = null, timer = null, syncing = false, generation = 0;
21
+ const watched = new Map();
22
+ function watch(origin) {
23
+ const adapter = createTuiEventAdapter(secretValues);
24
+ adapter.bindSession(origin.sessionId);
25
+ /* The replay is synchronous inside `subscribe`: what it asks and answers is settled first, and only the requests still
26
+ waiting at its end are handed over. */
27
+ let replaying = true;
28
+ const waiting = new Map();
29
+ const handle = (event) => {
30
+ if (event.type === 'approval.required') {
31
+ if (replaying)
32
+ waiting.set(event.requestId, event);
33
+ else
34
+ onRequired(origin, event);
35
+ }
36
+ else if (event.type === 'approval.resolved') {
37
+ if (replaying)
38
+ waiting.delete(event.requestId);
39
+ else
40
+ onResolved(origin, event.requestId);
41
+ }
42
+ else if (replaying && (event.type === 'run.completed' || event.type === 'run.failed' || event.type === 'run.cancelled'))
43
+ waiting.clear();
44
+ };
45
+ let off = () => { };
46
+ try {
47
+ off = runtime.subscribe(origin.sessionId, raw => { for (const event of adapter.translateAll(raw))
48
+ handle(event); });
49
+ }
50
+ finally {
51
+ replaying = false;
52
+ }
53
+ watched.set(origin.sessionId, off);
54
+ for (const event of waiting.values())
55
+ onRequired(origin, event);
56
+ }
57
+ async function sync() {
58
+ if (!rootId || syncing)
59
+ return;
60
+ syncing = true;
61
+ const mine = generation;
62
+ try {
63
+ const rows = await runtime.listSessions();
64
+ if (mine !== generation || !rootId)
65
+ return;
66
+ for (const origin of descendantsOf(rows, rootId))
67
+ if (!watched.has(origin.sessionId))
68
+ watch(origin);
69
+ }
70
+ catch (error) {
71
+ onError(error);
72
+ }
73
+ finally {
74
+ syncing = false;
75
+ }
76
+ }
77
+ function stop() {
78
+ generation++;
79
+ rootId = null;
80
+ if (timer) {
81
+ clearInterval(timer);
82
+ timer = null;
83
+ }
84
+ for (const off of watched.values()) {
85
+ try {
86
+ off();
87
+ }
88
+ catch { /* an unsubscribe that fails leaves nothing to undo */ }
89
+ }
90
+ watched.clear();
91
+ }
92
+ /** Watches the descendants of `sessionId` until `stop`; a second call for the same session changes nothing. */
93
+ function start(sessionId) {
94
+ if (rootId === sessionId && timer)
95
+ return;
96
+ stop();
97
+ rootId = sessionId;
98
+ timer = setInterval(() => void sync(), Math.max(50, intervalMs));
99
+ timer.unref?.();
100
+ void sync();
101
+ }
102
+ /** `sync` asks the session list now (tests, and a delegation that has just started). */
103
+ return { start, stop, sync, watching: () => [...watched.keys()] };
104
+ }