talos-code 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +111 -3
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/dist/archive/zip.js +73 -0
  5. package/dist/args.js +97 -0
  6. package/dist/automations/history-store.js +34 -0
  7. package/dist/automations/policy-store.js +43 -0
  8. package/dist/automations/runner.js +107 -0
  9. package/dist/automations/schedule.js +85 -0
  10. package/dist/automations/windows-task.js +53 -0
  11. package/dist/commands/advanced-cli.js +233 -0
  12. package/dist/commands/checkpoint-cli.js +45 -0
  13. package/dist/commands/config-cli.js +107 -0
  14. package/dist/commands/context.js +17 -0
  15. package/dist/commands/extensions-cli.js +207 -0
  16. package/dist/commands/project-cli.js +74 -0
  17. package/dist/commands/project-commands.js +143 -0
  18. package/dist/commands/provider-cli.js +185 -0
  19. package/dist/commands/services-cli.js +248 -0
  20. package/dist/commands/session-cli.js +171 -0
  21. package/dist/commands/system-cli.js +405 -0
  22. package/dist/config/commands.js +60 -0
  23. package/dist/config/load.js +382 -0
  24. package/dist/config/migrations.js +48 -0
  25. package/dist/config/types.js +11 -0
  26. package/dist/diagnostics/development-log.js +154 -0
  27. package/dist/diagnostics/doctor.js +104 -0
  28. package/dist/diagnostics/redact.js +80 -0
  29. package/dist/diagnostics/zip.js +52 -0
  30. package/dist/errors.js +156 -0
  31. package/dist/events/bridge.js +130 -0
  32. package/dist/extensions/installer.js +178 -0
  33. package/dist/extensions/package-schema.js +24 -0
  34. package/dist/headless/result.js +84 -0
  35. package/dist/headless/run.js +230 -0
  36. package/dist/i18n/en/approval.js +43 -0
  37. package/dist/i18n/en/common.js +9 -0
  38. package/dist/i18n/en/credentials.js +82 -0
  39. package/dist/i18n/en/errors.js +246 -0
  40. package/dist/i18n/en/firstrun.js +89 -0
  41. package/dist/i18n/en/providers.js +37 -0
  42. package/dist/i18n/en/screen.js +736 -0
  43. package/dist/i18n/en/tools.js +127 -0
  44. package/dist/i18n/en.js +14 -0
  45. package/dist/i18n/error-view.js +307 -0
  46. package/dist/i18n/index.js +19 -0
  47. package/dist/i18n/kernel-map.js +139 -0
  48. package/dist/io.js +45 -0
  49. package/dist/main.js +330 -0
  50. package/dist/paths.js +41 -0
  51. package/dist/protocol/v2/codec.js +9 -0
  52. package/dist/protocol/v2/events.js +555 -0
  53. package/dist/protocol/v2/index.js +4 -0
  54. package/dist/protocol/v2/replay.js +28 -0
  55. package/dist/protocol/v2/types.js +1 -0
  56. package/dist/provider/control-plane.js +135 -0
  57. package/dist/provider/environment-keys.js +275 -0
  58. package/dist/provider/health.js +110 -0
  59. package/dist/provider/missing-key.js +48 -0
  60. package/dist/provider/model-catalog.js +168 -0
  61. package/dist/provider/provider-text.js +13 -0
  62. package/dist/provider/store.js +95 -0
  63. package/dist/provider/system-keyring.js +214 -0
  64. package/dist/runtime/active-run.js +63 -0
  65. package/dist/runtime/agent-tree.js +74 -0
  66. package/dist/runtime/attachments.js +84 -0
  67. package/dist/runtime/brokered-executor.js +395 -0
  68. package/dist/runtime/context-status.js +76 -0
  69. package/dist/runtime/create-runtime.js +111 -0
  70. package/dist/runtime/model-profile.js +361 -0
  71. package/dist/runtime/output-store.js +137 -0
  72. package/dist/runtime/provider-attempts.js +185 -0
  73. package/dist/runtime/replay-buffer.js +153 -0
  74. package/dist/runtime/repo.js +95 -0
  75. package/dist/runtime/session-facade.js +135 -0
  76. package/dist/runtime/session-summary.js +125 -0
  77. package/dist/runtime/supervisor.js +262 -0
  78. package/dist/runtime/talos-composition.js +1006 -0
  79. package/dist/runtime/types.js +5 -0
  80. package/dist/runtime/usage-snapshot.js +82 -0
  81. package/dist/security/auto-classifier.js +38 -0
  82. package/dist/security/credential-free-environment.js +54 -0
  83. package/dist/security/evaluate.js +243 -0
  84. package/dist/security/execution-backends.js +236 -0
  85. package/dist/security/execution-broker.js +102 -0
  86. package/dist/security/forge-scan.js +74 -0
  87. package/dist/security/from-approval.js +39 -0
  88. package/dist/security/mxc-execution-backend.js +281 -0
  89. package/dist/security/permission-engine.js +138 -0
  90. package/dist/security/permission-explanation.js +54 -0
  91. package/dist/security/persist.js +179 -0
  92. package/dist/security/plugin-guard.js +230 -0
  93. package/dist/security/process-tree-evidence-store.js +74 -0
  94. package/dist/security/process-tree-probe.js +554 -0
  95. package/dist/security/project-resource-inventory.js +186 -0
  96. package/dist/security/project-trust-gate.js +38 -0
  97. package/dist/security/project-trust.js +367 -0
  98. package/dist/security/rule-parser.js +201 -0
  99. package/dist/security/shell-segmentation.js +68 -0
  100. package/dist/security/trust-authority.js +324 -0
  101. package/dist/security/types.js +1 -0
  102. package/dist/security/workspace-identity.js +102 -0
  103. package/dist/services/automation-facade.js +59 -0
  104. package/dist/services/forge-facade.js +77 -0
  105. package/dist/services/hook-facade.js +240 -0
  106. package/dist/services/index.js +16 -0
  107. package/dist/services/library-facade.js +174 -0
  108. package/dist/services/mcp-facade.js +348 -0
  109. package/dist/services/memory-facade.js +126 -0
  110. package/dist/services/notes-facade.js +157 -0
  111. package/dist/services/plugin-facade.js +308 -0
  112. package/dist/services/research-facade.js +110 -0
  113. package/dist/services/task-board-facade.js +236 -0
  114. package/dist/services/workflow-catalog.js +246 -0
  115. package/dist/sessions/export-format.js +98 -0
  116. package/dist/sessions/metadata-store.js +160 -0
  117. package/dist/sessions/share.js +122 -0
  118. package/dist/sessions/transfer.js +16 -0
  119. package/dist/subcommands.js +62 -0
  120. package/dist/tui/agent-roster.js +36 -0
  121. package/dist/tui/app.js +3761 -0
  122. package/dist/tui/approval.js +36 -0
  123. package/dist/tui/boot/boot-sequence.js +76 -0
  124. package/dist/tui/boot/cinematic.js +243 -0
  125. package/dist/tui/boot/desktop-logo.js +82 -0
  126. package/dist/tui/boot.js +3 -0
  127. package/dist/tui/busy-input.js +58 -0
  128. package/dist/tui/catalog-service.js +559 -0
  129. package/dist/tui/components/assistant-stream.js +1 -0
  130. package/dist/tui/components/command-menu.js +68 -0
  131. package/dist/tui/components/composer.js +219 -0
  132. package/dist/tui/components/diff.js +35 -0
  133. package/dist/tui/components/footer.js +48 -0
  134. package/dist/tui/components/header.js +6 -0
  135. package/dist/tui/components/markdown.js +305 -0
  136. package/dist/tui/components/status-indicator.js +32 -0
  137. package/dist/tui/components/terminal-shell.js +205 -0
  138. package/dist/tui/components/thinking-row.js +19 -0
  139. package/dist/tui/components/tool-row.js +97 -0
  140. package/dist/tui/components/transcript-virtualizer.js +165 -0
  141. package/dist/tui/components/transcript.js +43 -0
  142. package/dist/tui/diff-model.js +77 -0
  143. package/dist/tui/editor-history.js +21 -0
  144. package/dist/tui/editor.js +210 -0
  145. package/dist/tui/event-adapter.js +436 -0
  146. package/dist/tui/exit-output.js +58 -0
  147. package/dist/tui/external-editor.js +53 -0
  148. package/dist/tui/file-completion.js +89 -0
  149. package/dist/tui/focus-manager.js +5 -0
  150. package/dist/tui/highlight.js +30 -0
  151. package/dist/tui/input-router.js +18 -0
  152. package/dist/tui/interrupt.js +99 -0
  153. package/dist/tui/keybindings.js +194 -0
  154. package/dist/tui/keymap-resolver.js +91 -0
  155. package/dist/tui/launch-state.js +139 -0
  156. package/dist/tui/line-diff.js +57 -0
  157. package/dist/tui/live-activity.js +45 -0
  158. package/dist/tui/metrics.js +123 -0
  159. package/dist/tui/onboarding.js +36 -0
  160. package/dist/tui/overlays/agent-tree.js +39 -0
  161. package/dist/tui/overlays/approval-dialog.js +640 -0
  162. package/dist/tui/overlays/automation-center.js +51 -0
  163. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  164. package/dist/tui/overlays/context-inspector.js +36 -0
  165. package/dist/tui/overlays/effort-line.js +110 -0
  166. package/dist/tui/overlays/forge-center.js +46 -0
  167. package/dist/tui/overlays/help-dialog.js +16 -0
  168. package/dist/tui/overlays/history-picker.js +37 -0
  169. package/dist/tui/overlays/hook-center.js +70 -0
  170. package/dist/tui/overlays/library-center.js +55 -0
  171. package/dist/tui/overlays/mcp-center.js +60 -0
  172. package/dist/tui/overlays/memory-center.js +63 -0
  173. package/dist/tui/overlays/model-picker.js +72 -0
  174. package/dist/tui/overlays/notes-center.js +53 -0
  175. package/dist/tui/overlays/overlay-host.js +8 -0
  176. package/dist/tui/overlays/plugin-center.js +76 -0
  177. package/dist/tui/overlays/provider-picker.js +145 -0
  178. package/dist/tui/overlays/queue-editor.js +32 -0
  179. package/dist/tui/overlays/research-center.js +59 -0
  180. package/dist/tui/overlays/scroll-window.js +234 -0
  181. package/dist/tui/overlays/session-picker.js +115 -0
  182. package/dist/tui/overlays/tasks-center.js +83 -0
  183. package/dist/tui/overlays/theme-picker.js +21 -0
  184. package/dist/tui/overlays/transcript-search.js +58 -0
  185. package/dist/tui/overlays/trust-center.js +29 -0
  186. package/dist/tui/overlays/workflow-center.js +46 -0
  187. package/dist/tui/project-file-index.js +214 -0
  188. package/dist/tui/project-references.js +85 -0
  189. package/dist/tui/project-trust-prompt.js +287 -0
  190. package/dist/tui/prompt-history-store.js +116 -0
  191. package/dist/tui/queue-store.js +110 -0
  192. package/dist/tui/regions/budget.js +59 -0
  193. package/dist/tui/regions/views.js +96 -0
  194. package/dist/tui/render-coordinator.js +148 -0
  195. package/dist/tui/render-scheduler.js +4 -0
  196. package/dist/tui/run.js +69 -0
  197. package/dist/tui/selection-list.js +29 -0
  198. package/dist/tui/session-controller.js +778 -0
  199. package/dist/tui/session-export.js +54 -0
  200. package/dist/tui/shell-input.js +35 -0
  201. package/dist/tui/shell-layout.js +48 -0
  202. package/dist/tui/shell-model.js +98 -0
  203. package/dist/tui/slash-commands.js +80 -0
  204. package/dist/tui/state.js +151 -0
  205. package/dist/tui/status-bar.js +125 -0
  206. package/dist/tui/status-view.js +40 -0
  207. package/dist/tui/terminal-capabilities.js +9 -0
  208. package/dist/tui/terminal-session.js +11 -0
  209. package/dist/tui/text-width.js +66 -0
  210. package/dist/tui/theme-catalog.js +25 -0
  211. package/dist/tui/theme-store.js +23 -0
  212. package/dist/tui/theme.js +23 -0
  213. package/dist/tui/tool-display.js +135 -0
  214. package/dist/tui/tool-facts.js +1 -0
  215. package/dist/tui/tools/bash-renderer.js +32 -0
  216. package/dist/tui/tools/change.js +61 -0
  217. package/dist/tui/tools/edit-renderer.js +17 -0
  218. package/dist/tui/tools/generic-renderer.js +13 -0
  219. package/dist/tui/tools/list-renderer.js +14 -0
  220. package/dist/tui/tools/read-renderer.js +13 -0
  221. package/dist/tui/tools/registry.js +20 -0
  222. package/dist/tui/tools/row-format.js +187 -0
  223. package/dist/tui/tools/search-renderer.js +38 -0
  224. package/dist/tui/tools/shared.js +97 -0
  225. package/dist/tui/tools/write-renderer.js +15 -0
  226. package/dist/tui/transcript-model.js +441 -0
  227. package/dist/tui/ui-preferences.js +79 -0
  228. package/dist/tui/usage-view.js +77 -0
  229. package/dist/tui/vim-mode.js +99 -0
  230. package/dist/update/check.js +15 -0
  231. package/dist/update/npm.js +51 -0
  232. package/dist/update/run.js +129 -0
  233. package/dist/version.js +2 -0
  234. package/dist/workspace/checkpoint-store.js +210 -0
  235. package/dist/workspace/checkpoint.js +413 -0
  236. package/dist/workspace/restore.js +232 -0
  237. package/package.json +63 -5
  238. package/vendor/context-engine/package.json +11 -0
  239. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  240. package/vendor/context-engine/src/contracts.mjs +48 -0
  241. package/vendor/context-engine/src/engine.mjs +445 -0
  242. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  243. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  244. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  245. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  246. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  247. package/vendor/context-engine/src/profiles.mjs +10 -0
  248. package/vendor/context-engine/src/retrieval.mjs +104 -0
  249. package/vendor/context-engine/src/summary.mjs +97 -0
  250. package/vendor/context-engine/src/usage.mjs +34 -0
  251. package/vendor/harness-ui/package.json +38 -0
  252. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  253. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  254. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  255. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  256. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  257. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  258. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  259. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  260. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  261. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  262. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  263. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  264. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  265. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  266. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  267. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  268. package/vendor/harness-ui/src/config.mjs +697 -0
  269. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  270. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  271. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  272. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  273. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  274. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  275. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  276. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  277. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  278. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  279. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  280. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  281. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  282. package/vendor/harness-ui/src/doctor.mjs +142 -0
  283. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  284. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  285. package/vendor/harness-ui/src/document-report.mjs +331 -0
  286. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  287. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  288. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  289. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  290. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  291. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  292. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  293. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  294. package/vendor/harness-ui/src/git-service.mjs +626 -0
  295. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  296. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  297. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  298. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  299. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  300. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  301. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  302. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  303. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  304. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  305. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  306. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  307. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  308. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  309. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  310. package/vendor/harness-ui/src/library-store.mjs +652 -0
  311. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  312. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  313. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  314. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  315. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  316. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  317. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  318. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  319. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  320. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  321. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  322. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  323. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  324. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  325. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  326. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  327. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  328. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  329. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  330. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  331. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  332. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  333. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  334. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  335. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  336. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  337. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  338. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  339. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  340. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  341. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  342. package/vendor/harness-ui/src/research/card.mjs +235 -0
  343. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  344. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  345. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  346. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  347. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  348. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  349. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  350. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  351. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  352. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  353. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  354. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  355. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  356. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  357. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  358. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  359. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  360. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  361. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  362. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  363. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  364. package/vendor/harness-ui/src/research/report.mjs +203 -0
  365. package/vendor/harness-ui/src/research/run.mjs +527 -0
  366. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  367. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  368. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  369. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  370. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  371. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  372. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  373. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  374. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  375. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  376. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  377. package/vendor/harness-ui/src/session-store.mjs +220 -0
  378. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  379. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  380. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  381. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  382. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  383. package/vendor/harness-ui/src/static-files.mjs +96 -0
  384. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  385. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  386. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  387. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  388. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  389. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  390. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  391. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  392. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  393. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  394. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  395. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  396. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  397. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  398. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  399. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  400. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  401. package/vendor/manifest.json +170 -0
@@ -0,0 +1,51 @@
1
+ import { t } from "../../i18n/index.js";
2
+ import { centerTones, listOverlay } from "./scroll-window.js";
3
+ const UNSAFE = /[\\\x00-\x1f\x7f-\x9f\u2028\u2029\p{Bidi_Control}\p{Default_Ignorable_Code_Point}]/gu;
4
+ function inert(value, max = 360) { const safe = String(value ?? '').replace(UNSAFE, ch => { if (ch === '\\')
5
+ return '\\\\'; const cp = ch.codePointAt(0) ?? 0; return cp <= 0xffff ? '\\u' + cp.toString(16).padStart(4, '0') : '\\u{' + cp.toString(16) + '}'; }); return safe.length > max ? safe.slice(0, max - 1) + '…' : safe; }
6
+ function clamp(n, count) { return count ? Math.max(0, Math.min(count - 1, n)) : 0; }
7
+ export function createAutomationCenterModel(snapshot, selected = 0) { return { snapshot, selected: clamp(selected, snapshot.rows.length), detail: null }; }
8
+ export function moveAutomationCenterSelection(model, delta) { return { ...model, selected: clamp(model.selected + delta, model.snapshot.rows.length), detail: null }; }
9
+ export function selectedAutomation(model) { return model.snapshot.rows[model.selected] ?? null; }
10
+ export function setAutomationCenterDetail(model, detail) { return { ...model, detail }; }
11
+ export function renderAutomationCenterLines(model) {
12
+ const lines = [t('screen.automation.title')];
13
+ if (model.snapshot.state === 'invalid') {
14
+ lines.push('Store: invalid · ' + inert(model.snapshot.error?.message));
15
+ lines.push(t('screen.center.closeRefreshU'));
16
+ return lines;
17
+ }
18
+ if (!model.snapshot.rows.length) {
19
+ lines.push(t('screen.automation.empty'));
20
+ lines.push(t('screen.center.closeRefreshU'));
21
+ return lines;
22
+ }
23
+ lines.push(...model.snapshot.rows.map((row, index) => (index === model.selected ? '› ' : ' ') + (row.active ? 'active' : 'paused') + ' · ' + inert(row.name) + ' · ' + inert(row.id)));
24
+ const row = selectedAutomation(model), d = model.detail?.id === row?.id ? model.detail : null;
25
+ if (row) {
26
+ lines.push('ID: ' + inert(row.id) + ' · taskId ' + inert(row.taskId));
27
+ lines.push('Schedule: ' + inert(row.schedule) + ' · timezone ' + inert(row.timezone ?? 'legacy/UTC-store'));
28
+ if (d) {
29
+ lines.push('Missed-run: ' + inert(d.missedRun?.mode ?? 'legacy') + ' · grace ' + inert(d.missedRun?.graceMinutes ?? '—') + 'm · catch-up ' + inert(d.missedRun?.catchupWindowMinutes ?? '—') + 'm');
30
+ lines.push('Backoff: failures ' + inert(d.backoff?.consecutiveFailures ?? 0) + ' · next ' + inert(d.backoff?.nextRetryAt ?? 'none'));
31
+ lines.push('OS adapter: ' + inert(d.adapterHealth?.state ?? 'unknown'));
32
+ lines.push('dry-run: ' + inert(d.dryRun?.action ?? 'unavailable') + ' · ' + inert(d.dryRun?.reason ?? '') + (d.dryRun?.nextAt ? ' · next ' + inert(d.dryRun.nextAt) : ''));
33
+ lines.push('History:');
34
+ const history = Array.isArray(d.history) ? d.history.slice(-8) : [];
35
+ if (!history.length)
36
+ lines.push(' (none)');
37
+ else
38
+ for (const h of history)
39
+ lines.push(' ' + inert(h.attemptedAt) + ' · ' + inert(h.outcome) + (h.sessionId ? ' · session ' + inert(h.sessionId) : '') + (h.errorCode ? ' · ' + inert(h.errorCode) : ''));
40
+ }
41
+ else
42
+ lines.push('Detail: loading/unavailable');
43
+ }
44
+ lines.push(t('screen.automation.keys'));
45
+ return lines.map(line => line.length > 799 ? line.slice(0, 798) + '…' : line);
46
+ }
47
+ // R1 Q-R1-3: the same lines as a capped, scrolling overlay — the list keeps the selection visible, the detail scrolls.
48
+ export function automationCenterOverlay(model) {
49
+ const list = model.snapshot.state === 'invalid' ? 0 : model.snapshot.rows.length;
50
+ return listOverlay(renderAutomationCenterLines(model), { head: 1, list, selected: model.selected, foot: 1, tones: centerTones });
51
+ }
@@ -0,0 +1,47 @@
1
+ /*
2
+ * R5b D3 (owner decision Q-R5-13 and the owner's answers of 2026-09-25): the /undo picker. It lists this conversation's
3
+ * checkpoints, newest first, each with the files it changed; Enter asks first, with Cancel highlighted. On a row not yet
4
+ * undone the question is "go back to before this turn" (it and every newer one are undone, restore.ts rewindToCheckpoint);
5
+ * on an undone row it is "re-apply up to this turn" (it and every older undone one, reapplyToCheckpoint). Files only: the
6
+ * conversation stays.
7
+ */
8
+ import { t, tn } from "../../i18n/index.js";
9
+ import { checkpointChanges } from "../../workspace/checkpoint-store.js";
10
+ import { sessionAge } from "./session-picker.js";
11
+ /** `2 files: a.txt (modified), b.txt (added)`, or `no files changed` (also `talos checkpoint list`). */
12
+ export function checkpointFilesText(files) {
13
+ return files.length ? tn('screen.checkpoint.files', files.length, { list: files.map(row => `${row.path} (${t(`screen.checkpoint.change.${row.change}`)})`).join(', ') }) : t('screen.checkpoint.noFiles');
14
+ }
15
+ /** The checkpoints a choice touches, in the picker's order (newest first). */
16
+ export function undoRange(rows, selected) {
17
+ const chosen = rows[selected];
18
+ if (!chosen)
19
+ return { direction: 'rewind', steps: [] };
20
+ if (chosen.state === 'undone')
21
+ return { direction: 'reapply', steps: rows.slice(selected).filter(row => row.state === 'undone') };
22
+ return { direction: 'rewind', steps: rows.slice(0, selected + 1).filter(row => row.state !== 'undone') };
23
+ }
24
+ /** The files a set of checkpoints changed, each named once. */
25
+ export function rangeFiles(steps) {
26
+ const byPath = new Map();
27
+ for (const step of steps)
28
+ for (const change of checkpointChanges(step))
29
+ if (!byPath.has(change.path))
30
+ byPath.set(change.path, change);
31
+ return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
32
+ }
33
+ function rowText(row, now) {
34
+ const kind = t(row.operation === 'shell' ? 'screen.undo.kind.command' : 'screen.undo.kind.turn');
35
+ return `${row.state === 'undone' ? t('screen.undo.undone') + ' · ' : ''}${sessionAge(row.createdAt, now)} · ${kind} · ${checkpointFilesText(checkpointChanges(row))}`;
36
+ }
37
+ export function undoPickerOverlay(picker, { now = Date.now() } = {}) {
38
+ if (picker.confirm === null) {
39
+ const lines = picker.rows.map((row, index) => ({ text: rowText(row, now), tone: index === picker.selected ? 'selected' : row.state === 'undone' ? 'dim' : 'plain' }));
40
+ return { head: [{ text: t('screen.undo.title'), tone: 'title' }], sections: [{ lines, selected: picker.selected }], foot: [{ text: t('screen.undo.keys'), tone: 'dim' }, { text: t('screen.undo.note'), tone: 'dim' }] };
41
+ }
42
+ const { direction, steps } = undoRange(picker.rows, picker.selected);
43
+ const question = direction === 'rewind' ? tn('screen.undo.confirmRewind', steps.length) : tn('screen.undo.confirmReapply', steps.length);
44
+ const act = t(direction === 'rewind' ? 'screen.undo.goBack' : 'screen.undo.reapply');
45
+ const choices = [t('screen.undo.cancel'), act].map((text, index) => ({ text, tone: index === picker.confirm ? 'selected' : 'plain' }));
46
+ return { head: [{ text: question, tone: 'title' }, { text: checkpointFilesText(rangeFiles(steps)), tone: 'dim' }], sections: [{ lines: choices, selected: picker.confirm }], foot: [{ text: t('screen.undo.confirmKeys'), tone: 'dim' }] };
47
+ }
@@ -0,0 +1,36 @@
1
+ import { t } from "../../i18n/index.js";
2
+ import { truncateDisplay } from "../text-width.js";
3
+ import { listOverlay } from "./scroll-window.js";
4
+ function compact(value) {
5
+ if (value < 1000)
6
+ return String(Math.round(value));
7
+ if (value < 1_000_000)
8
+ return (value / 1000).toFixed(1).replace(/\.0$/u, '') + 'k';
9
+ return (value / 1_000_000).toFixed(1).replace(/\.0$/u, '') + 'm';
10
+ }
11
+ function fit(value, width) { return truncateDisplay(value, Math.max(1, Math.floor(width))); }
12
+ export function contextInspectorLines(status, { width }) {
13
+ const lines = [t('screen.context.title', { session: status.sessionId })];
14
+ if (!status.available || !status.measurement) {
15
+ lines.push(t('screen.context.unmeasured', { reason: status.reason ? ' · ' + status.reason : '' }));
16
+ lines.push('Auto compaction · trigger ' + status.autoCompaction.triggerPercent + '% policy when a measured profile is available');
17
+ }
18
+ else {
19
+ const m = status.measurement;
20
+ lines.push('Measured context · ' + compact(m.inputTokens) + ' / ' + compact(m.windowTokens) + ' · ' + m.percentOfWindow + '% · ' + m.method + ' measurement · ' + (m.exact ? 'exact' : 'not exact') + (m.stale ? ' · stale revision' : ''));
21
+ lines.push('Budget · window ' + compact(m.windowTokens) + ' · response reserve ' + compact(m.responseReserve) + (status.budget ? ' · safety margin ' + compact(status.budget.safetyMarginTokens) + ' · input limit ' + compact(status.budget.inputLimitTokens) : '') + (status.budget?.estimatedMarginTokens !== null && status.budget?.estimatedMarginTokens !== undefined ? ' · estimated count margin ' + compact(status.budget.estimatedMarginTokens) : '') + ' · ' + (status.budget?.provenance ?? 'measurement provenance unavailable'));
22
+ lines.push('Auto compaction · ' + (status.autoCompaction.enabled ? 'enabled' : 'disabled') + ' · trigger ' + status.autoCompaction.triggerPercent + '% of effective input budget' + (status.budget ? ' · threshold ' + compact(status.budget.triggerTokens) : '') + (status.autoCompaction.targetRatio !== null ? ' · target ' + (Math.round(status.autoCompaction.targetRatio * 1000) / 10) + '%' : ''));
23
+ }
24
+ lines.push(status.cache.percent === null
25
+ ? 'Cache · not measured · ' + status.cache.provenance
26
+ : 'Cache · ' + compact(status.cache.cachedTokens ?? 0) + ' / ' + compact(status.cache.promptTokens ?? 0) + ' prompt tokens · ' + status.cache.percent + '% · ' + status.cache.provenance);
27
+ lines.push('Cost · ' + (status.cost.usd === null ? 'not reported' : '$' + status.cost.usd) + ' · ' + status.cost.provenance);
28
+ lines.push('Sources · ' + (status.sources.activeSourceCount === null ? 'no active compacted source set' : status.sources.activeSourceCount) + (status.sources.summaryCitationCount !== null ? ' · citations ' + status.sources.summaryCitationCount : '') + ' · ' + status.sources.provenance);
29
+ lines.push(t('screen.center.closes'));
30
+ return lines.map(line => fit(line, width));
31
+ }
32
+ // R1 Q-R1-3: the same lines as a capped, scrolling overlay (overlays/scroll-window.ts).
33
+ // Unbounded width: the fit wraps each line to the box instead of cutting it, so no measurement is lost off the edge.
34
+ export function contextInspectorOverlay(status) {
35
+ return listOverlay(contextInspectorLines(status, { width: Number.POSITIVE_INFINITY }), { head: 1, list: 0, selected: 0, foot: 1, tones: (_line, index) => index === 0 ? 'accent' : 'dim' });
36
+ }
@@ -0,0 +1,110 @@
1
+ /*
2
+ * R5a (owner 2026-09-24): the reasoning-effort selector. First "a line of nodes in TALOS's glyph style"; then, after the owner
3
+ * compared it with Claude Code 2.1.282's (`Faster … Smarter`, evenly spaced levels, a ▲ under the chosen one, `←/→ to
4
+ * adjust · Enter to confirm`): "rendilo migliore e mi raccomando accento ambra e gli effort in ordine da default … max".
5
+ *
6
+ * Effort · Z.ai: GLM 5.3 Flash
7
+ * Faster Smarter
8
+ * ○────────○────────○────────●────────○ (a slider: amber up to and including ●, dim after it)
9
+ * ▲ (amber: the theme's accent)
10
+ * default low medium high max
11
+ * ──── (amber)
12
+ * High: deeper reasoning, slower, more tokens.
13
+ * In use now: medium
14
+ * ← → adjust · Enter confirm · Esc cancel
15
+ *
16
+ * The levels are always in TALOS's order (default, minimal, low, medium, high, xhigh, max; an unknown level last), never in
17
+ * the order a model declares them (the owner's frame showed `default — max — high — low`). Only the model's own levels are
18
+ * shown. Beyond Claude Code's: the level in use is named when another is highlighted, each level has its sentence, and a
19
+ * screen too narrow for every level trims the far ends with `…` around the chosen one. ASCII on a plain terminal.
20
+ * Pure: app.ts opens it and applies the choice.
21
+ */
22
+ import { t } from "../../i18n/index.js";
23
+ import { displayWidth } from "../text-width.js";
24
+ const ORDER = ['default', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
25
+ const KNOWN = new Set(['default', 'low', 'medium', 'high', 'xhigh', 'max']);
26
+ /** TALOS's order: default first, then the known levels from the least to the most reasoning; unknown ones last, as declared. */
27
+ export function orderedEfforts(levels) {
28
+ const rank = (level) => { const at = ORDER.indexOf(level); return at < 0 ? ORDER.length : at; };
29
+ return levels.map((level, index) => ({ level, index })).sort((a, b) => rank(a.level) - rank(b.level) || a.index - b.index).map(row => row.level);
30
+ }
31
+ /** One level's sentence (the known levels have their own; any other declared level is named as such). */
32
+ export function effortSentence(level) {
33
+ return KNOWN.has(level) ? t(`screen.effort.describe.${level}`) : t('screen.effort.describe.other', { level });
34
+ }
35
+ const glyphs = (unicode) => unicode ? { node: '○', chosen: '●', rule: '─', pointer: '▲', more: '…' } : { node: 'o', chosen: '*', rule: '-', pointer: '^', more: '...' };
36
+ /* The nodes lo..hi evenly spaced, `slot` columns apart, after `lead` columns (room for `…`); each label centred under its
37
+ node, as Claude Code centres its levels. */
38
+ function layout(choices, lo, hi, slot, lead, widest) {
39
+ const starts = [], centers = [];
40
+ let width = 0;
41
+ for (let i = lo; i <= hi; i++) {
42
+ const center = lead + Math.floor(widest / 2) + (i - lo) * slot, size = displayWidth(choices[i]), start = Math.max(lead, center - Math.floor(size / 2));
43
+ centers.push(center);
44
+ starts.push(start);
45
+ width = Math.max(width, start + size);
46
+ }
47
+ return { starts, centers, width };
48
+ }
49
+ const put = (row, at, text) => { for (let i = 0; i < text.length; i++)
50
+ row[at + i] = text[i]; };
51
+ const line = (row) => row.join('').replace(/\s+$/u, '');
52
+ export function effortLineRows({ choices, selected, width, unicode }) {
53
+ const g = glyphs(unicode), n = choices.length;
54
+ const chosen = Math.max(0, Math.min(n - 1, Math.trunc(selected) || 0));
55
+ const widest = Math.max(1, ...choices.map(c => displayWidth(c)));
56
+ const fit = (lo, hi) => {
57
+ const lead = lo > 0 ? displayWidth(g.more) + 1 : 0, tail = hi < n - 1 ? displayWidth(g.more) + 1 : 0, count = hi - lo;
58
+ /* Evenly spaced, as roomy as the width allows (a label plus 1 to 5 columns). */
59
+ const slot = count === 0 ? widest : Math.max(widest + 1, Math.min(widest + 5, Math.floor((width - lead - tail - widest) / count)));
60
+ const view = layout(choices, lo, hi, slot, lead, widest);
61
+ return { lo, hi, lead, tail, slot, view, fits: view.width + tail <= width };
62
+ };
63
+ let lo = 0, hi = n - 1, best = fit(lo, hi);
64
+ /* Too narrow for every level: drop the level farthest from the chosen one until it fits (or only the chosen one is left). */
65
+ while (!best.fits && (lo < chosen || hi > chosen)) {
66
+ if (chosen - lo >= hi - chosen)
67
+ lo++;
68
+ else
69
+ hi--;
70
+ best = fit(lo, hi);
71
+ }
72
+ const { view } = best;
73
+ const total = view.width + best.tail;
74
+ const blank = () => Array.from({ length: Math.max(total, 1) }, () => ' ');
75
+ const track = blank(), pointer = blank(), labels = blank(), underline = blank();
76
+ const first = view.centers[0], lastCenter = view.centers.at(-1);
77
+ for (let c = first; c <= lastCenter; c++)
78
+ track[c] = g.rule;
79
+ view.centers.forEach((c, k) => { track[c] = lo + k === chosen ? g.chosen : g.node; });
80
+ if (lo > 0) {
81
+ put(track, 0, g.more);
82
+ put(labels, 0, g.more);
83
+ }
84
+ if (hi < n - 1) {
85
+ put(track, view.width + 1, g.more);
86
+ put(labels, view.width + 1, g.more);
87
+ }
88
+ view.starts.forEach((s, k) => put(labels, s, choices[lo + k]));
89
+ const k = chosen - lo;
90
+ pointer[view.centers[k]] = g.pointer;
91
+ put(underline, view.starts[k], g.rule.repeat(displayWidth(choices[chosen])));
92
+ const faster = t('screen.effort.faster'), smarter = t('screen.effort.smarter');
93
+ const gap = Math.max(1, total - displayWidth(faster) - displayWidth(smarter));
94
+ return { axis: `${faster}${' '.repeat(gap)}${smarter}`, track: line(track), pointer: line(pointer), labels: line(labels), underline: line(underline), sentence: effortSentence(choices[chosen]), filledTo: view.centers[k] + 1 };
95
+ }
96
+ export function effortLineOverlay({ name, choices, selected, current = null, width, unicode }) {
97
+ const rows = effortLineRows({ choices, selected, width, unicode });
98
+ const chosen = choices[Math.max(0, Math.min(choices.length - 1, selected))] ?? null;
99
+ return {
100
+ head: [{ text: t('screen.effort.lineTitle', { model: name }), tone: 'title' }, { text: t('screen.effort.policy'), tone: 'dim' }],
101
+ sections: [{ lines: [
102
+ /* A slider: amber from the start up to and including the chosen node, dim after it (owner 2026-09-25). */
103
+ { text: rows.axis, tone: 'dim' }, { text: rows.track, tone: 'dim', accentTo: rows.filledTo }, { text: rows.pointer, tone: 'accent' },
104
+ { text: rows.labels, tone: 'plain' }, { text: rows.underline, tone: 'accent' },
105
+ { text: rows.sentence, tone: 'plain' },
106
+ ...(current && chosen && current !== chosen ? [{ text: t('screen.effort.inUse', { level: current }), tone: 'dim' }] : []),
107
+ ] }],
108
+ foot: [{ text: t('screen.effort.keys'), tone: 'dim' }],
109
+ };
110
+ }
@@ -0,0 +1,46 @@
1
+ import { t } from "../../i18n/index.js";
2
+ import { centerTones, listOverlay } from "./scroll-window.js";
3
+ const UNSAFE = /[\\\x00-\x1f\x7f-\x9f\u2028\u2029\p{Bidi_Control}\p{Default_Ignorable_Code_Point}]/gu;
4
+ function inert(value) { return String(value ?? '').replace(UNSAFE, ch => { if (ch === '\\')
5
+ return '\\\\'; const cp = ch.codePointAt(0) ?? 0; return cp <= 0xffff ? '\\u' + cp.toString(16).padStart(4, '0') : '\\u{' + cp.toString(16) + '}'; }); }
6
+ function clip(value, max = 700) { const text = inert(value); return text.length <= max ? text : text.slice(0, max - 1) + '…'; }
7
+ function clamp(n, count) { return count ? Math.max(0, Math.min(count - 1, n)) : 0; }
8
+ export function createForgeCenterModel(snapshot, selected = 0) { return { snapshot, selected: clamp(selected, snapshot.rows.length), detail: null }; }
9
+ export function moveForgeCenterSelection(model, delta) { return { ...model, selected: clamp(model.selected + delta, model.snapshot.rows.length), detail: null }; }
10
+ export function selectedForgeTool(model) { return model.snapshot.rows[model.selected] ?? null; }
11
+ export function setForgeCenterDetail(model, detail) { return { ...model, detail }; }
12
+ export function renderForgeCenterLines(model) {
13
+ const lines = [t('screen.forge.title')];
14
+ if (model.snapshot.state === 'invalid') {
15
+ lines.push('Store: invalid · ' + clip(model.snapshot.error?.message));
16
+ return lines;
17
+ }
18
+ if (!model.snapshot.rows.length) {
19
+ lines.push(t('screen.forge.empty'));
20
+ lines.push(t('screen.center.closeRefresh'));
21
+ return lines;
22
+ }
23
+ lines.push(...model.snapshot.rows.map((row, index) => (index === model.selected ? '› ' : ' ') + clip(row.title) + ' · ' + clip(row.id) + ' · ' + (row.enabled ? 'enabled' : 'disabled') + ' · ' + (row.ownerRevision === null ? 'legacy' : 'revision ' + row.ownerRevision)));
24
+ const row = selectedForgeTool(model), detail = model.detail?.id === row?.id ? model.detail : null;
25
+ if (row) {
26
+ lines.push('ID: ' + clip(row.id) + ' · ' + (row.enabled ? 'enabled' : 'disabled') + ' · ' + (row.ownerRevision === null ? 'legacy/model-created' : 'revision ' + row.ownerRevision) + ' · risk ' + clip(row.risk));
27
+ if (detail) {
28
+ const scan = detail.scan ?? detail.evidence?.scan;
29
+ if (scan) {
30
+ lines.push('Scan: ' + (scan.ok ? 'pass' : 'BLOCKED') + ' · capabilities ' + (scan.capabilities?.length ? scan.capabilities.map((value) => clip(value)).join(', ') : 'none') + ' · actions ' + (scan.actions?.length ? scan.actions.map((value) => clip(value)).join(', ') : 'none'));
31
+ }
32
+ if (detail.evidence) {
33
+ lines.push('Evidence: ' + clip(detail.evidence.packageDigest ?? 'unavailable') + ' · sideEffects ' + String(detail.evidence.sideEffects === true) + ' · sandbox ' + clip(detail.evidence.sandbox?.kind ?? 'unavailable'));
34
+ }
35
+ if (Array.isArray(detail.versions) && detail.versions.length)
36
+ lines.push('Versions: ' + detail.versions.map((v) => 'revision ' + String(v.revision ?? v.ownerRevision ?? '?')).join(', '));
37
+ }
38
+ }
39
+ lines.push(t('screen.forge.keys'));
40
+ return lines.map(line => line.length <= 799 ? line : line.slice(0, 798) + '…');
41
+ }
42
+ // R1 Q-R1-3: the same lines as a capped, scrolling overlay — the list keeps the selection visible, the detail scrolls.
43
+ export function forgeCenterOverlay(model) {
44
+ const invalid = model.snapshot.state === 'invalid';
45
+ return listOverlay(renderForgeCenterLines(model), { head: 1, list: invalid ? 0 : model.snapshot.rows.length, selected: model.selected, foot: invalid ? 0 : 1, tones: centerTones });
46
+ }
@@ -0,0 +1,16 @@
1
+ import { t } from "../../i18n/index.js";
2
+ import { KEYBINDINGS, keybindingHelp } from "../keybindings.js";
3
+ import { slashCommandHelp } from "../slash-commands.js";
4
+ /* R2 (Q-R2-1, gate E1): the headings are dictionary words; the per-locale copy table this file kept is the dictionary now. */
5
+ export function helpDialogText(keymap = KEYBINDINGS) {
6
+ return t('screen.help.commands') + '\n' + slashCommandHelp() + '\n\n' + t('screen.help.keyboard') + '\n' + keybindingHelp(keymap);
7
+ }
8
+ // R1 D-02 / Q-R1-3: the help is a text body that scrolls by `offset` (a line index) inside the budget; the title and the
9
+ // way out never scroll away. Measured before R1: 125 rows at 100 columns, drawn in one Text that Ink wrapped at will.
10
+ export function helpDialogOverlay(keymap = KEYBINDINGS, { offset = 0 } = {}) {
11
+ return {
12
+ head: [{ text: t('screen.help.title'), tone: 'title' }],
13
+ sections: [{ lines: helpDialogText(keymap).split('\n').map(text => ({ text, tone: 'plain' })), offset }],
14
+ foot: [{ text: t('screen.help.close'), tone: 'dim' }],
15
+ };
16
+ }
@@ -0,0 +1,37 @@
1
+ /*
2
+ * R5b (owner answer 2026-09-25: "I tuoi messaggi passati"): /history lists the messages the person sent in this project,
3
+ * this session's and the kept ones of earlier sessions (prompt-history-store.ts, secrets already masked), newest first.
4
+ * Typing filters them; Enter puts the chosen one back in the composer, collapsed pastes included, to edit or send again.
5
+ * It is Ctrl+R's list, made visible. Nothing is sent by choosing.
6
+ */
7
+ import { t } from "../../i18n/index.js";
8
+ import { truncateDisplay } from "../text-width.js";
9
+ /** One line per message: a collapsed paste as the composer shows it, line breaks as ⏎. */
10
+ export function historyRowText(draft) {
11
+ const pastes = [...draft.collapsedPastes].sort((a, b) => a.start - b.start);
12
+ let out = '', at = 0;
13
+ for (const paste of pastes) {
14
+ if (paste.start < at || paste.end > draft.text.length)
15
+ continue;
16
+ out += draft.text.slice(at, paste.start) + `[paste: ${paste.lines} lines, ${paste.chars} chars]`;
17
+ at = paste.end;
18
+ }
19
+ out += draft.text.slice(at);
20
+ return out.replace(/\r?\n/gu, ' ⏎ ').replace(/\s{2,}/gu, ' ').trim();
21
+ }
22
+ /** Messages only: a slash command (/history itself, /status…) stays in ↑ and Ctrl+R but is not "your message". */
23
+ export function filterHistory(rows, query) {
24
+ const q = query.trim().toLowerCase();
25
+ const messages = rows.filter(row => !row.text.trimStart().startsWith('/'));
26
+ return q ? messages.filter(row => row.text.toLowerCase().includes(q)) : messages;
27
+ }
28
+ export function historyPickerOverlay({ rows, picker, width }) {
29
+ const shown = filterHistory(rows, picker.query);
30
+ const room = Math.max(8, width - 6);
31
+ const head = [{ text: t('screen.history.title', { count: shown.length }), tone: 'title' }, { text: t('screen.history.search', { query: picker.query }), tone: 'dim' }];
32
+ const foot = [{ text: t('screen.history.keys'), tone: 'dim' }];
33
+ if (!shown.length)
34
+ return { head, sections: [{ lines: [{ text: picker.query.trim() ? t('screen.history.noMatch', { query: picker.query }) : t('screen.history.empty'), tone: 'dim' }] }], foot };
35
+ const selected = Math.max(0, Math.min(shown.length - 1, picker.selected));
36
+ return { head, sections: [{ lines: shown.map((row, index) => ({ text: (index === selected ? '› ' : ' ') + truncateDisplay(historyRowText(row), room), tone: index === selected ? 'selected' : 'plain' })), selected }], foot };
37
+ }
@@ -0,0 +1,70 @@
1
+ import { t } from "../../i18n/index.js";
2
+ import { centerTones, listOverlay } from "./scroll-window.js";
3
+ const UNSAFE = /[\\\x00-\x1f\x7f-\x9f\u2028\u2029\p{Bidi_Control}\p{Default_Ignorable_Code_Point}]/gu;
4
+ const BACKSLASH = String.fromCharCode(92);
5
+ function inert(value) {
6
+ return String(value).replace(UNSAFE, character => {
7
+ if (character === BACKSLASH)
8
+ return BACKSLASH + BACKSLASH;
9
+ const code = character.codePointAt(0);
10
+ return code > 0xffff ? BACKSLASH + 'u{' + code.toString(16) + '}' : BACKSLASH + 'u' + code.toString(16).padStart(4, '0');
11
+ });
12
+ }
13
+ function shown(value) {
14
+ if (typeof value === 'string')
15
+ return inert(value);
16
+ try {
17
+ return inert(JSON.stringify(value));
18
+ }
19
+ catch {
20
+ return inert(String(value));
21
+ }
22
+ }
23
+ function fingerprint(value) { return value ? shown(value) : '—'; }
24
+ export function createHookCenterModel(rows, selected = 0) {
25
+ const copy = rows.map(row => ({ ...row, lastExecutions: [...row.lastExecutions] }));
26
+ return { title: t('screen.hooks.title'), rows: copy, selected: copy.length ? Math.max(0, Math.min(copy.length - 1, selected)) : 0 };
27
+ }
28
+ export function moveHookCenterSelection(model, delta) {
29
+ if (!model.rows.length)
30
+ return model;
31
+ return { ...model, selected: Math.max(0, Math.min(model.rows.length - 1, model.selected + delta)) };
32
+ }
33
+ export function selectedHook(model) { return model.rows[model.selected] ?? null; }
34
+ export function replaceHookCenterRow(model, row) {
35
+ const index = model.rows.findIndex(value => value.id === row.id);
36
+ if (index < 0)
37
+ return model;
38
+ const rows = [...model.rows];
39
+ rows[index] = row;
40
+ return { ...model, rows };
41
+ }
42
+ export function renderHookCenterLines(model) {
43
+ const lines = [model.title, ''];
44
+ if (!model.rows.length)
45
+ return [...lines, t('screen.hooks.empty'), '', t('screen.center.escRefresh')];
46
+ for (const [index, row] of model.rows.entries()) {
47
+ const status = row.quarantine.active ? 'quarantined' : row.trust.trusted ? 'trusted' : row.trust.state;
48
+ lines.push((index === model.selected ? '›' : ' ') + ' ' + shown(row.id) + ' · ' + status + ' · ' + row.events.map(shown).join(', '));
49
+ }
50
+ const row = selectedHook(model);
51
+ if (!row)
52
+ return lines;
53
+ lines.push('', 'Selected: ' + shown(row.id), 'Trust: project ' + (row.trust.projectTrusted ? 'yes' : 'no') + ' · resource ' + row.trust.state + (row.trust.changed ? ' · fingerprint changed' : ''), 'Fingerprint current: ' + fingerprint(row.trust.currentFingerprint), 'Fingerprint trusted: ' + fingerprint(row.trust.trustedFingerprint), 'Quarantine: ' + (row.quarantine.active ? 'quarantined' + (row.quarantine.reason ? ' · ' + shown(row.quarantine.reason) : '') : 'not quarantined'), 'Events: ' + row.events.map(shown).join(', '), 'Command: ' + shown(row.launch.command), 'Dry run: executes the REAL hook process with a synthetic TALOS event; side effects are possible.', row.lastDryRun
54
+ ? 'Last dry run: ' + shown(row.lastDryRun.eventType) + ' · ' + shown(row.lastDryRun.result)
55
+ : 'Last dry run: not run in this center', 'Last executions');
56
+ if (!row.lastExecutions.length)
57
+ lines.push(' none recorded');
58
+ else
59
+ for (const execution of row.lastExecutions.slice(0, 8)) {
60
+ lines.push(' session ' + shown(execution.sessionId) + ' · started ' + shown(execution.sessionStartedAt ?? 'unknown') +
61
+ ' · sequence ' + execution.sequence + ' · ' + shown(execution.eventType) +
62
+ (execution.action ? ' · ' + shown(execution.action) : '') + ' · ' + shown(execution.result));
63
+ }
64
+ lines.push('', t('screen.hooks.keys', { event: row.events[0] ? shown(row.events[0]) : t('screen.hooks.declaredEvent') }));
65
+ return lines;
66
+ }
67
+ // R1 Q-R1-3: the same lines as a capped, scrolling overlay — the list keeps the selection visible, the detail scrolls.
68
+ export function hookCenterOverlay(model) {
69
+ return listOverlay(renderHookCenterLines(model), { head: 2, list: model.rows.length, selected: model.selected, foot: 1, tones: centerTones });
70
+ }
@@ -0,0 +1,55 @@
1
+ import { en, t } from "../../i18n/index.js";
2
+ import { centerTones, listOverlay } from "./scroll-window.js";
3
+ const UNSAFE = /[\\\x00-\x1f\x7f-\x9f\u2028\u2029\p{Bidi_Control}\p{Default_Ignorable_Code_Point}]/gu;
4
+ function inert(value) { return String(value ?? '').replace(UNSAFE, ch => { if (ch === '\\')
5
+ return '\\\\'; const cp = ch.codePointAt(0) ?? 0; return cp <= 0xffff ? '\\u' + cp.toString(16).padStart(4, '0') : '\\u{' + cp.toString(16) + '}'; }); }
6
+ /* R2 (gate §1.5, E4): the kernel's `origine` value (modello|persona) is mapped by value, never printed raw. */
7
+ function writerLabel(value) { const id = `screen.writer.${String(value)}`; return Object.hasOwn(en, id) ? t(id) : inert(value); }
8
+ function clamp(value, count) { return count ? Math.max(0, Math.min(count - 1, value)) : 0; }
9
+ export function createLibraryCenterModel(snapshot, selected = 0) { return { snapshot, selected: clamp(selected, snapshot.rows.length), preview: null, duplicateScan: null }; }
10
+ export function moveLibraryCenterSelection(model, delta) { return { ...model, selected: clamp(model.selected + delta, model.snapshot.rows.length), preview: null, duplicateScan: null }; }
11
+ export function selectedLibraryEntry(model) { return model.snapshot.rows[model.selected] ?? null; }
12
+ export function setLibraryCenterPreview(model, preview) { return { ...model, preview }; }
13
+ export function setLibraryCenterDuplicateScan(model, duplicateScan) { return { ...model, duplicateScan }; }
14
+ export function renderLibraryCenterLines(model) {
15
+ const lines = [t('screen.library.title')];
16
+ if (model.snapshot.state === 'invalid') {
17
+ lines.push('Store: invalid · ' + inert(model.snapshot.error?.code ?? 'LIBRARY_STORE_INVALID') + ' · ' + inert(model.snapshot.error?.message ?? 'unavailable'));
18
+ lines.push(t('screen.center.closeRefresh'));
19
+ return lines;
20
+ }
21
+ if (!model.snapshot.rows.length) {
22
+ lines.push(t('screen.library.empty'));
23
+ lines.push(t('screen.center.closeRefresh'));
24
+ return lines;
25
+ }
26
+ lines.push(...model.snapshot.rows.map((row, index) => (index === model.selected ? '› ' : ' ') + inert(row.name) + ' · ' + inert(row.mediaType) + ' · ' + inert(row.id)));
27
+ const row = selectedLibraryEntry(model);
28
+ if (row) {
29
+ lines.push('ID: ' + inert(row.id) + ' · ' + inert(row.fileType) + ' · origin ' + inert(row.origin));
30
+ lines.push('Creator: ' + writerLabel(row.creator.type) + (row.creator.model ? ' · ' + inert(row.creator.model) : '') + (row.creator.provider ? ' · ' + inert(row.creator.provider) : ''));
31
+ lines.push('Session: ' + (row.session ? inert(row.session.id) + (row.session.name ? ' · ' + inert(row.session.name) : '') : 'unavailable'));
32
+ lines.push('Context policy: explicit visible reference · auto-attach no · auto-submit no · ' + inert(row.context.persistence));
33
+ lines.push('Context token: ' + inert(row.context.token ?? 'unavailable'));
34
+ if (model.preview?.id === row.id) {
35
+ const preview = model.preview;
36
+ lines.push('Preview: ' + inert(preview.mode) + ' · ' + inert(preview.mediaType) + ' · ' + preview.size + ' bytes' + (preview.reason ? ' · ' + inert(preview.reason) : ''));
37
+ if (preview.text !== null)
38
+ lines.push('Preview text: ' + inert(preview.text) + (preview.truncated ? ' …[truncated]' : ''));
39
+ }
40
+ if (model.duplicateScan?.id === row.id) {
41
+ const scan = model.duplicateScan;
42
+ lines.push('Duplicates: ' + inert(scan.state) + ' · sha256 ' + inert(scan.sha256 ?? 'unavailable') + ' · verified unique ' + (scan.verifiedUnique ? 'yes' : 'no'));
43
+ lines.push('Exact duplicate IDs: ' + (scan.duplicateIds.length ? scan.duplicateIds.map(inert).join(', ') : 'none'));
44
+ if (scan.unreadableIds.length)
45
+ lines.push('Not compared: ' + scan.unreadableIds.map(inert).join(', '));
46
+ }
47
+ }
48
+ lines.push(t('screen.library.keys'));
49
+ return lines;
50
+ }
51
+ // R1 Q-R1-3: the same lines as a capped, scrolling overlay — the list keeps the selection visible, the detail scrolls.
52
+ export function libraryCenterOverlay(model) {
53
+ const list = model.snapshot.state === 'invalid' ? 0 : model.snapshot.rows.length;
54
+ return listOverlay(renderLibraryCenterLines(model), { head: 1, list, selected: model.selected, foot: 1, tones: centerTones });
55
+ }
@@ -0,0 +1,60 @@
1
+ import { t } from "../../i18n/index.js";
2
+ import { centerTones, listOverlay } from "./scroll-window.js";
3
+ const UNSAFE = /[\\\x00-\x1f\x7f-\x9f\u2028\u2029\p{Bidi_Control}\p{Default_Ignorable_Code_Point}]/gu;
4
+ const BACKSLASH = String.fromCharCode(92);
5
+ function inert(value) {
6
+ return String(value).replace(UNSAFE, character => {
7
+ if (character === BACKSLASH)
8
+ return BACKSLASH + BACKSLASH;
9
+ const code = character.codePointAt(0);
10
+ return code > 0xffff ? BACKSLASH + 'u{' + code.toString(16) + '}' : BACKSLASH + 'u' + code.toString(16).padStart(4, '0');
11
+ });
12
+ }
13
+ function shown(value) { return inert(String(value ?? '—')); }
14
+ function shortFingerprint(value) { return value ? shown(value) : '—'; }
15
+ function yesNo(value) { return value ? 'yes' : 'no'; }
16
+ export function createMcpCenterModel(rows, selected = 0) {
17
+ const copy = rows.map(row => ({ ...row }));
18
+ return { title: t('screen.mcp.title'), rows: copy, selected: copy.length ? Math.max(0, Math.min(copy.length - 1, selected)) : 0 };
19
+ }
20
+ export function moveMcpCenterSelection(model, delta) {
21
+ if (!model.rows.length)
22
+ return model;
23
+ return { ...model, selected: Math.max(0, Math.min(model.rows.length - 1, model.selected + delta)) };
24
+ }
25
+ export function selectedMcpServer(model) { return model.rows[model.selected] ?? null; }
26
+ export function replaceMcpCenterRow(model, row) {
27
+ const index = model.rows.findIndex(value => value.id === row.id);
28
+ if (index < 0)
29
+ return model;
30
+ const rows = [...model.rows];
31
+ rows[index] = row;
32
+ return { ...model, rows };
33
+ }
34
+ export function renderMcpCenterLines(model) {
35
+ const lines = [model.title, ''];
36
+ if (!model.rows.length)
37
+ return [...lines, t('screen.mcp.empty'), '', t('screen.center.escQRefresh')];
38
+ for (const [index, row] of model.rows.entries()) {
39
+ lines.push((index === model.selected ? '›' : ' ') + ' ' + shown(row.id) + ' · ' + row.health.state + ' · ' + (row.trust.trusted ? 'trusted' : row.trust.state));
40
+ }
41
+ const row = selectedMcpServer(model);
42
+ if (!row)
43
+ return lines;
44
+ const capability = row.capabilities
45
+ ? Object.entries(row.capabilities)
46
+ .filter(([key, value]) => key !== 'experimental' && value === true)
47
+ .map(([key]) => key)
48
+ .concat(row.capabilities.experimental.map(value => 'experimental:' + value))
49
+ .join(', ') || 'none reported'
50
+ : 'not probed';
51
+ const logs = row.logs.length
52
+ ? row.logs.slice(-8).map(entry => ' ' + entry.source + (entry.level ? ' · ' + shown(entry.level) : '') + ' · ' + shown(entry.text))
53
+ : [' none observed'];
54
+ lines.push('', 'Selected: ' + shown(row.id), 'Health: ' + row.health.state + (row.health.latencyMs !== null ? ' · ' + row.health.latencyMs + ' ms' : '') + (row.health.phase ? ' · ' + shown(row.health.phase) : '') + (row.health.message ? ' · ' + shown(row.health.message) : ''), 'Trust: project ' + yesNo(row.trust.projectTrusted) + ' · resource ' + row.trust.state + (row.trust.changed ? ' · fingerprint changed' : ''), 'Fingerprint current: ' + shortFingerprint(row.trust.currentFingerprint), 'Fingerprint trusted: ' + shortFingerprint(row.trust.trustedFingerprint), 'Server: ' + (row.server ? shown(row.server.name) + (row.server.version ? ' · ' + shown(row.server.version) : '') : 'not probed'), 'Protocol: ' + (row.protocol ? shown(row.protocol.version) + (row.protocol.era ? ' · ' + shown(row.protocol.era) : '') : 'not probed'), 'Capabilities: ' + capability, 'Permissions', ' configured allowlist: ' + (row.permission.configuredAllowlist.length ? row.permission.configuredAllowlist.map(shown).join(', ') : 'none'), ' effective tools: ' + (row.permission.effectiveTools.length ? row.permission.effectiveTools.map(shown).join(', ') : 'none measured'), ' per-call approval: ' + row.permission.perCallApproval, ...(row.permission.missingAllowlistedTools.length ? [' missing allowlisted: ' + row.permission.missingAllowlistedTools.map(shown).join(', ')] : []), 'Logs', ...logs, '', t('screen.mcp.keys'));
55
+ return lines;
56
+ }
57
+ // R1 Q-R1-3: the same lines as a capped, scrolling overlay — the list keeps the selection visible, the detail scrolls.
58
+ export function mcpCenterOverlay(model) {
59
+ return listOverlay(renderMcpCenterLines(model), { head: 2, list: model.rows.length, selected: model.selected, foot: 1, tones: centerTones });
60
+ }
@@ -0,0 +1,63 @@
1
+ import { en, t } from "../../i18n/index.js";
2
+ import { centerTones, listOverlay } from "./scroll-window.js";
3
+ const UNSAFE = /[\\\x00-\x1f\x7f-\x9f\u2028\u2029\p{Bidi_Control}\p{Default_Ignorable_Code_Point}]/gu;
4
+ function inert(value) {
5
+ return String(value ?? '').replace(UNSAFE, (ch) => {
6
+ if (ch === '\\')
7
+ return '\\\\';
8
+ const cp = ch.codePointAt(0) ?? 0;
9
+ return cp <= 0xffff ? '\\u' + cp.toString(16).padStart(4, '0') : '\\u{' + cp.toString(16) + '}';
10
+ });
11
+ }
12
+ /* R2 (gate §1.5, E4): the kernel's `origine` value (modello|persona) is mapped by value, never printed raw. */
13
+ function writerLabel(value) { const id = `screen.writer.${String(value)}`; return Object.hasOwn(en, id) ? t(id) : inert(value); }
14
+ function clamp(selected, count) { return count ? Math.max(0, Math.min(count - 1, selected)) : 0; }
15
+ export function createMemoryCenterModel(snapshot, selected = 0) {
16
+ return { snapshot, selected: clamp(selected, snapshot.rows.length) };
17
+ }
18
+ export function moveMemoryCenterSelection(model, delta) {
19
+ return { ...model, selected: clamp(model.selected + delta, model.snapshot.rows.length) };
20
+ }
21
+ export function selectedMemory(model) {
22
+ return model.snapshot.rows[model.selected] ?? null;
23
+ }
24
+ export function renderMemoryCenterLines(model) {
25
+ const lines = [
26
+ t('screen.memory.title'),
27
+ 'Scope: global (derived-store-contract) · Project: unavailable · Session: unavailable · Source: unavailable'
28
+ ];
29
+ if (model.snapshot.state === 'invalid') {
30
+ lines.push('Store: invalid · ' + inert(model.snapshot.error?.code ?? 'MEMORY_STORE_INVALID') + ' · ' + inert(model.snapshot.error?.message ?? 'unavailable'));
31
+ lines.push(t('screen.center.closeRefresh'));
32
+ return lines;
33
+ }
34
+ if (!model.snapshot.rows.length) {
35
+ lines.push(t('screen.memory.empty'));
36
+ lines.push(t('screen.center.closeRefresh'));
37
+ return lines;
38
+ }
39
+ lines.push(...model.snapshot.rows.map((row, index) => {
40
+ const marker = index === model.selected ? '›' : ' ';
41
+ return marker + ' ' + inert(row.id) + ' · ' + inert(row.title) + ' · ' + inert(row.kind ?? 'kind unavailable');
42
+ }));
43
+ const row = selectedMemory(model);
44
+ if (row) {
45
+ lines.push('ID: ' + inert(row.id));
46
+ lines.push('Title: ' + inert(row.title));
47
+ lines.push('Kind: ' + inert(row.kind ?? 'unavailable'));
48
+ lines.push('Writer: ' + writerLabel(row.provenance.writer) + ' (' + inert(row.provenance.writerEvidence) + ')');
49
+ lines.push('Provenance: scope ' + inert(row.provenance.scope) + ' (' + inert(row.provenance.scopeEvidence) + ') · project unavailable · session unavailable · source unavailable');
50
+ lines.push('Duplicate control: ' + inert(row.duplicateControl.mode) + ' · ' + inert(row.duplicateControl.normalization) + ' · create ' + inert(row.duplicateControl.onCreateDuplicate) + ' · update ' + inert(row.duplicateControl.onUpdate) + ' · semantic ' + (row.duplicateControl.semanticDetection ? 'yes' : 'no') + ' · merge ' + (row.duplicateControl.merge ? 'yes' : 'no'));
51
+ lines.push('Usage: ' + inert(row.usage.status) + ' · ' + inert(row.usage.reason));
52
+ lines.push('Cost: ' + inert(row.cost.status) + ' · ' + inert(row.cost.reason));
53
+ lines.push('Created: ' + inert(row.createdAt ?? 'unavailable') + ' · Updated: ' + inert(row.updatedAt ?? 'unavailable'));
54
+ lines.push('Content: ' + inert(row.content));
55
+ }
56
+ lines.push(t('screen.center.selectRefresh'));
57
+ return lines;
58
+ }
59
+ // R1 Q-R1-3: the same lines as a capped, scrolling overlay — the list keeps the selection visible, the detail scrolls.
60
+ export function memoryCenterOverlay(model) {
61
+ const list = model.snapshot.state === 'invalid' ? 0 : model.snapshot.rows.length;
62
+ return listOverlay(renderMemoryCenterLines(model), { head: 2, list, selected: model.selected, foot: 1, tones: centerTones });
63
+ }