@selesai/code 0.13.15 → 0.13.18

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 (473) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +2 -14
  3. package/dist/bun/cli.d.ts +3 -1
  4. package/dist/bun/cli.js +3 -9
  5. package/dist/bun/{register-bedrock.js → runtime-setup.js} +5 -0
  6. package/dist/bun/sandbox-env-setup.d.ts +1 -0
  7. package/dist/bun/sandbox-env-setup.js +3 -0
  8. package/dist/cli/auth-command.d.ts +23 -0
  9. package/dist/cli/auth-command.js +102 -0
  10. package/dist/cli/config-selector.js +2 -1
  11. package/dist/cli/setup.d.ts +1 -0
  12. package/dist/cli/setup.js +11 -0
  13. package/dist/cli.js +2 -14
  14. package/dist/config.d.ts +3 -2
  15. package/dist/config.js +9 -4
  16. package/dist/core/agent-session-runtime.js +15 -7
  17. package/dist/core/agent-session.d.ts +2 -6
  18. package/dist/core/agent-session.js +15 -4
  19. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  20. package/dist/core/compaction/branch-summarization.js +2 -1
  21. package/dist/core/extensions/loader.js +3 -0
  22. package/dist/core/extensions/types.d.ts +16 -2
  23. package/dist/core/http-dispatcher.js +2 -0
  24. package/dist/core/keybindings.d.ts +5 -0
  25. package/dist/core/keybindings.js +4 -0
  26. package/dist/core/messages.d.ts +1 -1
  27. package/dist/core/model-config.d.ts +15 -0
  28. package/dist/core/model-config.js +3 -0
  29. package/dist/core/model-runtime.d.ts +1 -0
  30. package/dist/core/model-runtime.js +5 -2
  31. package/dist/core/provider-composer.js +8 -2
  32. package/dist/core/session-export.d.ts +3 -0
  33. package/dist/core/session-export.js +31 -0
  34. package/dist/core/session-manager.d.ts +3 -2
  35. package/dist/core/session-manager.js +40 -13
  36. package/dist/core/skills.d.ts +1 -1
  37. package/dist/core/skills.js +4 -2
  38. package/dist/core/system-prompt.js +10 -10
  39. package/dist/core/tools/bash.js +3 -117
  40. package/dist/core/tools/edit.d.ts +1 -12
  41. package/dist/core/tools/edit.js +5 -163
  42. package/dist/core/tools/find.js +4 -55
  43. package/dist/core/tools/grep.js +4 -60
  44. package/dist/core/tools/ls.js +4 -49
  45. package/dist/core/tools/read.d.ts +4 -0
  46. package/dist/core/tools/read.js +10 -122
  47. package/dist/core/tools/renderers/bash.d.ts +11 -0
  48. package/dist/core/tools/renderers/bash.js +129 -0
  49. package/dist/core/tools/renderers/edit.d.ts +22 -0
  50. package/dist/core/tools/renderers/edit.js +166 -0
  51. package/dist/core/tools/renderers/find.d.ts +9 -0
  52. package/dist/core/tools/renderers/find.js +63 -0
  53. package/dist/core/tools/renderers/grep.d.ts +9 -0
  54. package/dist/core/tools/renderers/grep.js +68 -0
  55. package/dist/core/tools/renderers/index.d.ts +27 -0
  56. package/dist/core/tools/renderers/index.js +46 -0
  57. package/dist/core/tools/renderers/ls.d.ts +9 -0
  58. package/dist/core/tools/renderers/ls.js +57 -0
  59. package/dist/core/tools/renderers/read.d.ts +10 -0
  60. package/dist/core/tools/renderers/read.js +128 -0
  61. package/dist/core/tools/renderers/write.d.ts +9 -0
  62. package/dist/core/tools/renderers/write.js +152 -0
  63. package/dist/core/tools/write.js +5 -146
  64. package/dist/extensions/capability-gateway/index.ts +12 -1
  65. package/dist/extensions/capability-gateway/integration.test.ts +23 -0
  66. package/dist/extensions/pi-hermes-memory/CHANGELOG.md +18 -0
  67. package/dist/extensions/pi-hermes-memory/README.md +16 -12
  68. package/dist/extensions/pi-hermes-memory/docs/ROADMAP.md +1 -1
  69. package/dist/extensions/pi-hermes-memory/package-lock.json +587 -62
  70. package/dist/extensions/pi-hermes-memory/package.json +4 -4
  71. package/dist/extensions/pi-hermes-memory/src/config.ts +32 -0
  72. package/dist/extensions/pi-hermes-memory/src/constants.ts +12 -2
  73. package/dist/extensions/pi-hermes-memory/src/handlers/background-review.ts +109 -51
  74. package/dist/extensions/pi-hermes-memory/src/handlers/index-sessions.ts +9 -3
  75. package/dist/extensions/pi-hermes-memory/src/handlers/review-memory-ops.ts +257 -98
  76. package/dist/extensions/pi-hermes-memory/src/handlers/session-backfill.ts +9 -2
  77. package/dist/extensions/pi-hermes-memory/src/handlers/sync-markdown-memories.ts +5 -0
  78. package/dist/extensions/pi-hermes-memory/src/index.ts +22 -2
  79. package/dist/extensions/pi-hermes-memory/src/paths.ts +1 -1
  80. package/dist/extensions/pi-hermes-memory/src/store/db.ts +6 -0
  81. package/dist/extensions/pi-hermes-memory/src/store/fts-query.ts +58 -1
  82. package/dist/extensions/pi-hermes-memory/src/store/memory-store.ts +60 -8
  83. package/dist/extensions/pi-hermes-memory/src/store/session-indexer.ts +169 -7
  84. package/dist/extensions/pi-hermes-memory/src/store/session-search.ts +7 -21
  85. package/dist/extensions/pi-hermes-memory/src/store/sqlite-memory-store.ts +134 -15
  86. package/dist/extensions/pi-hermes-memory/src/tools/memory-search-tool.ts +4 -2
  87. package/dist/extensions/pi-hermes-memory/src/tools/memory-tool.ts +24 -9
  88. package/dist/extensions/pi-hermes-memory/src/types.ts +11 -0
  89. package/dist/extensions/pi-hermes-memory/tests/config.test.ts +42 -0
  90. package/dist/extensions/pi-hermes-memory/tests/handlers/background-review.test.ts +159 -3
  91. package/dist/extensions/pi-hermes-memory/tests/handlers/review-memory-ops.test.ts +452 -0
  92. package/dist/extensions/pi-hermes-memory/tests/handlers/session-backfill.test.ts +39 -0
  93. package/dist/extensions/pi-hermes-memory/tests/integration/corruption-recovery-integration.test.ts +91 -0
  94. package/dist/extensions/pi-hermes-memory/tests/store/db.test.ts +29 -0
  95. package/dist/extensions/pi-hermes-memory/tests/store/fts-query.test.ts +116 -0
  96. package/dist/extensions/pi-hermes-memory/tests/store/memory-store.test.ts +163 -0
  97. package/dist/extensions/pi-hermes-memory/tests/store/session-indexer.test.ts +233 -0
  98. package/dist/extensions/pi-hermes-memory/tests/store/session-search.test.ts +12 -0
  99. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-memory-store.test.ts +175 -0
  100. package/dist/extensions/pi-hermes-memory/tests/store/sqlite-native.test.ts +2 -2
  101. package/dist/extensions/pi-hermes-memory/tests/tools/memory-search-tool.test.ts +22 -0
  102. package/dist/extensions/pi-hermes-memory/tests/tools/memory-tool.test.ts +37 -1
  103. package/dist/extensions/pi-intercom/CHANGELOG.md +13 -0
  104. package/dist/extensions/pi-intercom/README.md +16 -2
  105. package/dist/extensions/pi-intercom/broker/spawn.test.ts +19 -2
  106. package/dist/extensions/pi-intercom/broker/spawn.ts +4 -4
  107. package/dist/extensions/pi-intercom/index.ts +1 -1
  108. package/dist/extensions/pi-intercom/intercom.integration.test.ts +121 -2
  109. package/dist/extensions/pi-intercom/package.json +1 -1
  110. package/dist/extensions/pi-intercom/project-agent.test.ts +1 -1
  111. package/dist/extensions/pi-intercom/project-agent.ts +1 -6
  112. package/dist/extensions/pi-intercom/skills/pi-intercom/SKILL.md +7 -6
  113. package/dist/extensions/pi-rewind-hook/index.test.ts +7 -0
  114. package/dist/extensions/pi-subagents/.github/workflows/test.yml +36 -4
  115. package/dist/extensions/pi-subagents/CHANGELOG.md +120 -0
  116. package/dist/extensions/pi-subagents/README.md +3 -3
  117. package/dist/extensions/pi-subagents/agents/researcher.md +23 -13
  118. package/dist/extensions/pi-subagents/agents/reviewer.md +1 -1
  119. package/dist/extensions/pi-subagents/agents/scout.md +1 -1
  120. package/dist/extensions/pi-subagents/docs/agents.md +32 -16
  121. package/dist/extensions/pi-subagents/docs/configuration.md +41 -15
  122. package/dist/extensions/pi-subagents/docs/extension-api.md +109 -6
  123. package/dist/extensions/pi-subagents/docs/missions.md +2 -0
  124. package/dist/extensions/pi-subagents/docs/models.md +58 -1
  125. package/dist/extensions/pi-subagents/docs/observability.md +50 -12
  126. package/dist/extensions/pi-subagents/docs/tool-reference.md +17 -10
  127. package/dist/extensions/pi-subagents/docs/watchdog.md +1 -1
  128. package/dist/extensions/pi-subagents/docs/workflows.md +26 -9
  129. package/dist/extensions/pi-subagents/package-lock.json +747 -177
  130. package/dist/extensions/pi-subagents/package.json +6 -4
  131. package/dist/extensions/pi-subagents/runner-server-preload.mjs +13 -0
  132. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +2 -1
  133. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  134. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +21 -4
  135. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +2 -1
  136. package/dist/extensions/pi-subagents/skills/pi-subagents/references/multi-lane-orchestration.md +2 -0
  137. package/dist/extensions/pi-subagents/src/agents/advertised-agent-prompt.ts +63 -0
  138. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +61 -16
  139. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
  140. package/dist/extensions/pi-subagents/src/agents/agents.ts +8 -0
  141. package/dist/extensions/pi-subagents/src/api/capability-ceiling.ts +0 -1
  142. package/dist/extensions/pi-subagents/src/api/{pi-args.ts → child-tool-plan.ts} +1 -1
  143. package/dist/extensions/pi-subagents/src/api/preflight.ts +7 -4
  144. package/dist/extensions/pi-subagents/src/api/shared-types.ts +2 -2
  145. package/dist/extensions/pi-subagents/src/api/workflow-resources.ts +6 -0
  146. package/dist/extensions/pi-subagents/src/extension/config.ts +4 -2
  147. package/dist/extensions/pi-subagents/src/extension/doctor.ts +2 -10
  148. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +9 -11
  149. package/dist/extensions/pi-subagents/src/extension/index.ts +99 -10
  150. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +13 -0
  151. package/dist/extensions/pi-subagents/src/extension/rpc.ts +7 -23
  152. package/dist/extensions/pi-subagents/src/extension/schemas.ts +11 -8
  153. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +25 -7
  154. package/dist/extensions/pi-subagents/src/integrations/pi-web-session-liveness.ts +73 -0
  155. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +228 -136
  156. package/dist/extensions/pi-subagents/src/intercom/supervisor-ui.ts +244 -0
  157. package/dist/extensions/pi-subagents/src/missions/workflow-state.ts +37 -16
  158. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +18 -18
  159. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +51 -25
  160. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +46 -3
  161. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -3
  162. package/dist/extensions/pi-subagents/src/runs/background/async-retention.ts +9 -0
  163. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +10 -12
  164. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +18 -15
  165. package/dist/extensions/pi-subagents/src/runs/background/auto-drain.ts +40 -29
  166. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +8 -0
  167. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +79 -247
  168. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +88 -12
  169. package/dist/extensions/pi-subagents/src/runs/background/owned-process-tree.ts +6 -6
  170. package/dist/extensions/pi-subagents/src/runs/background/process-terminal.ts +24 -24
  171. package/dist/extensions/pi-subagents/src/runs/background/retained-nested-route-tracker.ts +96 -0
  172. package/dist/extensions/pi-subagents/src/runs/background/run-child-session.ts +642 -0
  173. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +75 -6
  174. package/dist/extensions/pi-subagents/src/runs/background/runner-aliases.ts +163 -0
  175. package/dist/extensions/pi-subagents/src/runs/background/runner-child-launch.ts +86 -0
  176. package/dist/extensions/pi-subagents/src/runs/background/runner-child-sessions.ts +31 -0
  177. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +18 -4
  178. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +3 -1
  179. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +597 -1092
  180. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +3 -0
  181. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +4 -0
  182. package/dist/extensions/pi-subagents/src/runs/foreground/async-steering-action.ts +20 -17
  183. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +296 -394
  184. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-control.ts +4 -0
  185. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +3 -1
  186. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +9 -5
  187. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +630 -283
  188. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-detach-reconcile.ts +8 -5
  189. package/dist/extensions/pi-subagents/src/runs/foreground/workflow-foreground-steering.ts +78 -98
  190. package/dist/extensions/pi-subagents/src/runs/shared/abort-recovery.ts +3 -3
  191. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +16 -3
  192. package/dist/extensions/pi-subagents/src/runs/shared/agent-contract.ts +1 -1
  193. package/dist/extensions/pi-subagents/src/runs/shared/async-status-projection.ts +47 -47
  194. package/dist/extensions/pi-subagents/src/runs/shared/capability-ceiling.ts +1 -2
  195. package/dist/extensions/pi-subagents/src/runs/shared/child-hooks.ts +174 -0
  196. package/dist/extensions/pi-subagents/src/runs/shared/child-identity.ts +14 -3
  197. package/dist/extensions/pi-subagents/src/runs/shared/child-launch.ts +319 -0
  198. package/dist/extensions/pi-subagents/src/runs/shared/child-lifecycle.ts +25 -0
  199. package/dist/extensions/pi-subagents/src/runs/shared/child-runtime-config.ts +126 -0
  200. package/dist/extensions/pi-subagents/src/runs/shared/child-session.ts +373 -0
  201. package/dist/extensions/pi-subagents/src/runs/shared/child-tool-plan.ts +530 -0
  202. package/dist/extensions/pi-subagents/src/runs/shared/completion-evidence.ts +2 -2
  203. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +2 -1
  204. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-preflight.ts +16 -0
  205. package/dist/extensions/pi-subagents/src/runs/shared/host-step-status.ts +11 -11
  206. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +30 -20
  207. package/dist/extensions/pi-subagents/src/runs/shared/mcp-direct-tool-allowlist.ts +5 -4
  208. package/dist/extensions/pi-subagents/src/runs/shared/model-exclusions.ts +84 -15
  209. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +88 -12
  210. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +37 -53
  211. package/dist/extensions/pi-subagents/src/runs/shared/nested-path.ts +1 -1
  212. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +18 -7
  213. package/dist/extensions/pi-subagents/src/runs/shared/parallel-handoff.ts +57 -12
  214. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +3 -4
  215. package/dist/extensions/pi-subagents/src/runs/shared/permissions.ts +0 -11
  216. package/dist/extensions/pi-subagents/src/runs/shared/process-signal.ts +4 -1
  217. package/dist/extensions/pi-subagents/src/runs/shared/readonly-drain-observation.ts +42 -0
  218. package/dist/extensions/pi-subagents/src/runs/shared/readonly-model-continuation.ts +69 -0
  219. package/dist/extensions/pi-subagents/src/runs/shared/readonly-session-evidence.ts +307 -0
  220. package/dist/extensions/pi-subagents/src/runs/shared/run-fanout-budget.ts +8 -21
  221. package/dist/extensions/pi-subagents/src/runs/shared/runtime-acknowledged-extensions.ts +3 -30
  222. package/dist/extensions/pi-subagents/src/runs/shared/structured-output.ts +17 -4
  223. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +2 -0
  224. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +99 -386
  225. package/dist/extensions/pi-subagents/src/runs/shared/tool-availability.ts +18 -62
  226. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +0 -14
  227. package/dist/extensions/pi-subagents/src/runs/shared/worktree-cleanup-plan.ts +31 -9
  228. package/dist/extensions/pi-subagents/src/runs/shared/worktree-setup-command.ts +190 -0
  229. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +506 -226
  230. package/dist/extensions/pi-subagents/src/shared/child-session-name.ts +1 -1
  231. package/dist/extensions/pi-subagents/src/shared/jsonl-writer.ts +11 -0
  232. package/dist/extensions/pi-subagents/src/shared/model-response-aliases.ts +13 -0
  233. package/dist/extensions/pi-subagents/src/shared/thinking-ceiling.ts +0 -6
  234. package/dist/extensions/pi-subagents/src/shared/types.ts +145 -89
  235. package/dist/extensions/pi-subagents/src/shared/utils.ts +13 -6
  236. package/dist/extensions/pi-subagents/src/shared/watch-strategy.ts +2 -0
  237. package/dist/extensions/pi-subagents/src/shared/workflow-child-permit.ts +18 -13
  238. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +0 -6
  239. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +1 -1
  240. package/dist/extensions/pi-subagents/src/tui/fleet.ts +11 -6
  241. package/dist/extensions/pi-subagents/src/tui/render.ts +277 -46
  242. package/dist/extensions/pi-subagents/src/watchdog/child-status.ts +0 -1
  243. package/dist/extensions/pi-subagents/src/watchdog/register-child.ts +12 -12
  244. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +3 -3
  245. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +116 -15
  246. package/dist/extensions/pi-subagents/src/workflows/workflow-checklist.ts +15 -18
  247. package/dist/extensions/pi-subagents/src/workflows/workflow-child-summary.ts +57 -8
  248. package/dist/extensions/pi-subagents/src/workflows/workflow-preflight.ts +19 -19
  249. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +3 -3
  250. package/dist/extensions/pi-subagents/src/workflows/workflow-resources.ts +97 -22
  251. package/dist/extensions/pi-subagents/src/workflows/workflow-settlement.ts +3 -0
  252. package/dist/extensions/pi-subagents/test/integration/acceptance-file-report.test.ts +336 -21
  253. package/dist/extensions/pi-subagents/test/integration/async-execution.part-1.test.ts +1181 -0
  254. package/dist/extensions/pi-subagents/test/integration/async-execution.part-2.test.ts +2240 -0
  255. package/dist/extensions/pi-subagents/test/integration/async-execution.part-3.test.ts +2268 -0
  256. package/dist/extensions/pi-subagents/test/integration/async-execution.part-4.test.ts +1512 -0
  257. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +231 -16
  258. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +4 -18
  259. package/dist/extensions/pi-subagents/test/integration/foreign-workflow-steering.test.ts +95 -0
  260. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +9 -15
  261. package/dist/extensions/pi-subagents/test/integration/in-process-child.test.ts +374 -0
  262. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +20 -10
  263. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +18 -6
  264. package/dist/extensions/pi-subagents/test/integration/recovered-assistant-error.test.ts +151 -0
  265. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +42 -1
  266. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +150 -25
  267. package/dist/extensions/pi-subagents/test/integration/result-publication.test.ts +354 -0
  268. package/dist/extensions/pi-subagents/test/integration/shared-cwd-terminal-evidence.test.ts +117 -0
  269. package/dist/extensions/pi-subagents/test/integration/single-execution.part-1.test.ts +3678 -0
  270. package/dist/extensions/pi-subagents/test/integration/{single-execution.test.ts → single-execution.part-2.test.ts} +500 -4207
  271. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1 -2
  272. package/dist/extensions/pi-subagents/test/integration/workflow-result-publication.test.ts +127 -0
  273. package/dist/extensions/pi-subagents/test/integration/workflow-steer-inbox.test.ts +365 -0
  274. package/dist/extensions/pi-subagents/test/smoke/pi085-child.ts +31 -0
  275. package/dist/extensions/pi-subagents/test/smoke/pi085-clean-install.mjs +74 -0
  276. package/dist/extensions/pi-subagents/test/smoke/pi085-extension.ts +16 -0
  277. package/dist/extensions/pi-subagents/test/support/async-execution-fixture.ts +699 -0
  278. package/dist/extensions/pi-subagents/test/support/fake-child-session.ts +418 -0
  279. package/dist/extensions/pi-subagents/test/support/foreign-workflow-steer-host.mjs +13 -0
  280. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +30 -76
  281. package/dist/extensions/pi-subagents/test/support/notify-diagnostics-fixture.ts +84 -0
  282. package/dist/extensions/pi-subagents/test/support/result-publication-capacity-preload.mjs +70 -0
  283. package/dist/extensions/pi-subagents/test/support/runner-child-session-factory.ts +17 -0
  284. package/dist/extensions/pi-subagents/test/support/single-execution-fixture.ts +390 -0
  285. package/dist/extensions/pi-subagents/test/unit/acceptance-compaction.test.ts +120 -0
  286. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +24 -3
  287. package/dist/extensions/pi-subagents/test/unit/advertised-agent-prompt.test.ts +87 -0
  288. package/dist/extensions/pi-subagents/test/unit/advertised-agent-refresh.test.ts +163 -0
  289. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +70 -31
  290. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +78 -3
  291. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +2 -8
  292. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +1 -1
  293. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +73 -66
  294. package/dist/extensions/pi-subagents/test/unit/async-recovery-descriptor.test.ts +51 -1
  295. package/dist/extensions/pi-subagents/test/unit/async-retention.test.ts +49 -1
  296. package/dist/extensions/pi-subagents/test/unit/async-spawn-preload.test.ts +103 -0
  297. package/dist/extensions/pi-subagents/test/unit/async-status-projection.test.ts +3 -3
  298. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-agent-allowlist.test.ts +9 -8
  299. package/dist/extensions/pi-subagents/test/unit/capability-ceiling.test.ts +10 -7
  300. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +48 -6
  301. package/dist/extensions/pi-subagents/test/unit/child-runtime-config.test.ts +90 -0
  302. package/dist/extensions/pi-subagents/test/unit/{pi-args-permission-system.test.ts → child-tool-plan-permission-system.test.ts} +48 -86
  303. package/dist/extensions/pi-subagents/test/unit/child-tool-plan.test.ts +33 -0
  304. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +18 -18
  305. package/dist/extensions/pi-subagents/test/unit/completion-evidence.test.ts +9 -9
  306. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +8 -7
  307. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +225 -65
  308. package/dist/extensions/pi-subagents/test/unit/extension-bindings.test.ts +17 -22
  309. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +11 -1
  310. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +47 -3
  311. package/dist/extensions/pi-subagents/test/unit/foreign-workflow-steering.test.ts +74 -0
  312. package/dist/extensions/pi-subagents/test/unit/fork-cache-key.test.ts +23 -29
  313. package/dist/extensions/pi-subagents/test/unit/host-peer-runtime-imports.test.ts +96 -4
  314. package/dist/extensions/pi-subagents/test/unit/host-step-status.test.ts +4 -4
  315. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +116 -55
  316. package/dist/extensions/pi-subagents/test/unit/index-segment.test.ts +58 -1
  317. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +104 -3
  318. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +10 -1
  319. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +31 -1
  320. package/dist/extensions/pi-subagents/test/unit/model-exclusions.test.ts +102 -51
  321. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +196 -6
  322. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +181 -38
  323. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +62 -70
  324. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +58 -53
  325. package/dist/extensions/pi-subagents/test/unit/notify-diagnostics.test.ts +42 -0
  326. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +84 -0
  327. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +13 -5
  328. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +21 -5
  329. package/dist/extensions/pi-subagents/test/unit/parallel-handoff.test.ts +5 -4
  330. package/dist/extensions/pi-subagents/test/unit/permissions.test.ts +2 -6
  331. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +28 -2
  332. package/dist/extensions/pi-subagents/test/unit/pi-web-session-liveness.test.ts +209 -0
  333. package/dist/extensions/pi-subagents/test/unit/preflight.test.ts +38 -1
  334. package/dist/extensions/pi-subagents/test/unit/prompt-audit-optional-peers.test.ts +43 -0
  335. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +24 -2
  336. package/dist/extensions/pi-subagents/test/unit/readonly-drain-observation.test.ts +174 -0
  337. package/dist/extensions/pi-subagents/test/unit/readonly-model-continuation.test.ts +94 -0
  338. package/dist/extensions/pi-subagents/test/unit/readonly-session-evidence.test.ts +1329 -0
  339. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +29 -80
  340. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +11 -8
  341. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +2 -2
  342. package/dist/extensions/pi-subagents/test/unit/retained-nested-route-tracker.test.ts +175 -0
  343. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +38 -12
  344. package/dist/extensions/pi-subagents/test/unit/run-fanout-budget.test.ts +2 -5
  345. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +89 -1
  346. package/dist/extensions/pi-subagents/test/unit/runtime-acknowledged-extensions.test.ts +2 -16
  347. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +27 -3
  348. package/dist/extensions/pi-subagents/test/unit/scheduled-supervisor-demand.test.ts +215 -0
  349. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +15 -11
  350. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +237 -1
  351. package/dist/extensions/pi-subagents/test/unit/steering-action.test.ts +2 -5
  352. package/dist/extensions/pi-subagents/test/unit/streamed-progress-bounds.test.ts +2 -2
  353. package/dist/extensions/pi-subagents/test/unit/subagent-guide.test.ts +14 -1
  354. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +130 -739
  355. package/dist/extensions/pi-subagents/test/unit/supervisor-ask-registration.test.ts +682 -0
  356. package/dist/extensions/pi-subagents/test/unit/supervisor-ui.test.ts +137 -0
  357. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +1 -11
  358. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +23 -3
  359. package/dist/extensions/pi-subagents/test/unit/wait-completions.test.ts +8 -1
  360. package/dist/extensions/pi-subagents/test/unit/watchdog-tool-actions.test.ts +19 -1
  361. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +72 -4
  362. package/dist/extensions/pi-subagents/test/unit/workflow-checklist.test.ts +17 -3
  363. package/dist/extensions/pi-subagents/test/unit/workflow-child-summary.test.ts +34 -0
  364. package/dist/extensions/pi-subagents/test/unit/workflow-detach-reconcile.test.ts +12 -2
  365. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +46 -6
  366. package/dist/extensions/pi-subagents/test/unit/workflow-receipt.test.ts +3 -3
  367. package/dist/extensions/pi-subagents/test/unit/workflow-resources.test.ts +133 -1
  368. package/dist/extensions/pi-subagents/test/unit/workflow-resume-hint.test.ts +53 -2
  369. package/dist/extensions/pi-subagents/test/unit/worktree-cleanup-plan.test.ts +222 -14
  370. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +526 -85
  371. package/dist/extensions/pi-web-agent/src/backends/config.ts +1 -0
  372. package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +5 -3
  373. package/dist/extensions/pi-web-agent/src/search/tokenin.ts +7 -3
  374. package/dist/extensions/pi-web-agent/src/types.ts +1 -1
  375. package/dist/extensions/pi-zentui/README.md +17 -0
  376. package/dist/extensions/pi-zentui/assets/screenshots/footer-status-hyperlink.png +0 -0
  377. package/dist/extensions/pi-zentui/docs/configuration.md +71 -2
  378. package/dist/extensions/pi-zentui/extensions/zentui/config.ts +11 -0
  379. package/dist/extensions/pi-zentui/extensions/zentui/editor-transfer.ts +14 -0
  380. package/dist/extensions/pi-zentui/extensions/zentui/extension-status.ts +29 -13
  381. package/dist/extensions/pi-zentui/extensions/zentui/footer-layout.ts +5 -4
  382. package/dist/extensions/pi-zentui/extensions/zentui/footer-text.ts +11 -0
  383. package/dist/extensions/pi-zentui/extensions/zentui/footer.ts +11 -10
  384. package/dist/extensions/pi-zentui/extensions/zentui/index.ts +77 -3
  385. package/dist/extensions/pi-zentui/extensions/zentui/presets.ts +77 -0
  386. package/dist/extensions/pi-zentui/extensions/zentui/settings-command.ts +103 -2
  387. package/dist/extensions/pi-zentui/extensions/zentui/working-line-extension-segments.ts +115 -0
  388. package/dist/extensions/pi-zentui/extensions/zentui/working-line.ts +87 -5
  389. package/dist/extensions/pi-zentui/package-lock.json +277 -517
  390. package/dist/extensions/pi-zentui/package.json +7 -4
  391. package/dist/extensions/pi-zentui/test/config-presets.test.ts +238 -0
  392. package/dist/extensions/pi-zentui/test/editor-transfer.test.ts +24 -1
  393. package/dist/extensions/pi-zentui/test/extension-compliance.test.ts +570 -2
  394. package/dist/extensions/pi-zentui/test/extension-status.test.ts +90 -0
  395. package/dist/extensions/pi-zentui/test/footer-hyperlink.test.ts +243 -0
  396. package/dist/extensions/pi-zentui/test/package-contents.mjs +1 -0
  397. package/dist/extensions/pi-zentui/test/presets.test.ts +121 -0
  398. package/dist/extensions/pi-zentui/test/settings-command.test.ts +205 -1
  399. package/dist/extensions/pi-zentui/test/working-line-extension-segments.test.ts +139 -0
  400. package/dist/extensions/pi-zentui/test/working-line-lifecycle.integration.test.ts +237 -3
  401. package/dist/extensions/pi-zentui/test/working-line.test.ts +94 -2
  402. package/dist/index.d.ts +1 -1
  403. package/dist/main.js +4 -1
  404. package/dist/modes/interactive/chat-viewport.d.ts +19 -0
  405. package/dist/modes/interactive/chat-viewport.js +27 -0
  406. package/dist/modes/interactive/components/assistant-message.d.ts +6 -2
  407. package/dist/modes/interactive/components/assistant-message.js +30 -13
  408. package/dist/modes/interactive/components/custom-editor.d.ts +10 -1
  409. package/dist/modes/interactive/components/custom-editor.js +39 -1
  410. package/dist/modes/interactive/components/first-time-setup.js +1 -1
  411. package/dist/modes/interactive/components/index.d.ts +1 -1
  412. package/dist/modes/interactive/components/markdown-transform.d.ts +2 -0
  413. package/dist/modes/interactive/components/markdown-transform.js +18 -0
  414. package/dist/modes/interactive/components/model-selector.js +19 -17
  415. package/dist/modes/interactive/components/scoped-models-selector.js +21 -23
  416. package/dist/modes/interactive/components/settings-selector.d.ts +7 -1
  417. package/dist/modes/interactive/components/settings-selector.js +128 -20
  418. package/dist/modes/interactive/components/settings-submenu.d.ts +71 -0
  419. package/dist/modes/interactive/components/settings-submenu.js +164 -0
  420. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  421. package/dist/modes/interactive/components/status-indicator.js +10 -3
  422. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  423. package/dist/modes/interactive/components/thinking-selector.js +75 -18
  424. package/dist/modes/interactive/components/tool-execution.d.ts +21 -4
  425. package/dist/modes/interactive/components/tool-execution.js +35 -33
  426. package/dist/modes/interactive/components/trust-selector.js +2 -2
  427. package/dist/modes/interactive/interactive-mode.d.ts +13 -15
  428. package/dist/modes/interactive/interactive-mode.js +144 -80
  429. package/dist/modes/interactive/session-share.d.ts +15 -0
  430. package/dist/modes/interactive/session-share.js +183 -0
  431. package/dist/modes/interactive/theme/dark.json +2 -1
  432. package/dist/modes/interactive/theme/light.json +2 -1
  433. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  434. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  435. package/dist/modes/interactive/theme/theme-json.d.ts +83 -0
  436. package/dist/modes/interactive/theme/theme-json.js +129 -0
  437. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  438. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  439. package/dist/modes/interactive/theme/theme.js +18 -119
  440. package/dist/modes/interactive/tui-renderer.d.ts +20 -0
  441. package/dist/modes/interactive/tui-renderer.js +65 -0
  442. package/dist/utils/exif-orientation.js +2 -3
  443. package/dist/utils/management-http.d.ts +24 -0
  444. package/dist/utils/management-http.js +53 -0
  445. package/dist/utils/text.d.ts +7 -0
  446. package/dist/utils/text.js +8 -0
  447. package/dist/utils/tools-manager.d.ts +1 -0
  448. package/dist/utils/tools-manager.js +43 -21
  449. package/docs/docs.json +0 -4
  450. package/package.json +4 -4
  451. package/dist/extensions/pi-subagents/src/runs/shared/child-protocol.ts +0 -415
  452. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +0 -1059
  453. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +0 -116
  454. package/dist/extensions/pi-subagents/src/shared/post-exit-stdio-guard.ts +0 -85
  455. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +0 -224
  456. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -6986
  457. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +0 -447
  458. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +0 -185
  459. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +0 -311
  460. package/dist/extensions/pi-subagents/test/unit/capability-ceiling-pi-args.test.ts +0 -84
  461. package/dist/extensions/pi-subagents/test/unit/child-protocol.test.ts +0 -202
  462. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +0 -101
  463. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +0 -2347
  464. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +0 -104
  465. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +0 -49
  466. package/dist/skills/imagegen-frontend-mobile/SKILL.md +0 -1465
  467. package/dist/skills/imagegen-frontend-web/SKILL.md +0 -987
  468. package/dist/skills/implanger/SKILL.md +0 -70
  469. package/dist/skills/output/SKILL.md +0 -49
  470. package/dist/skills/planger/SKILL.md +0 -167
  471. package/dist/skills/workflow/SKILL.md +0 -95
  472. package/docs/workflows.md +0 -18
  473. /package/dist/bun/{register-bedrock.d.ts → runtime-setup.d.ts} +0 -0
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { stripVTControlCharacters } from "node:util";
3
- import type { Theme } from "@earendil-works/pi-coding-agent";
3
+ import type { EventBus, Theme } from "@earendil-works/pi-coding-agent";
4
4
  import { Loader, visibleWidth } from "@earendil-works/pi-tui";
5
5
  import { describe, expect, it, vi } from "vitest";
6
6
  import { defaultConfig, type PolishedTuiConfig } from "../extensions/zentui/config";
@@ -35,6 +35,10 @@ import {
35
35
  WorkingLineController,
36
36
  workingLineSpinnerWidth,
37
37
  } from "../extensions/zentui/working-line";
38
+ import {
39
+ WorkingLineExtensionSegments,
40
+ ZENTUI_WORKING_LINE_SEGMENT_EVENT,
41
+ } from "../extensions/zentui/working-line-extension-segments";
38
42
 
39
43
  function theme(): Theme {
40
44
  return {
@@ -968,6 +972,42 @@ describe("working-line row composition", () => {
968
972
  expect(constrained.row).not.toContain("parallel");
969
973
  });
970
974
 
975
+ it("composes sanitized extension segments into every animated frame", () => {
976
+ const current = config();
977
+ const component = current.components.workingLine;
978
+ const runtime = {
979
+ tokens: { input: 12, output: 3 },
980
+ extensions: ["24.3 tok/s · TTFT 820ms", "\x1b[31mqueue 2\x1b[0m"],
981
+ };
982
+ const composed = composeWorkingLineRow(component, "Working…", runtime);
983
+ expect(composed.row).toBe("Working… · ↑12 ↓3 · 24.3 tok/s · TTFT 820ms · queue 2");
984
+ const generated = buildWorkingLineFrames(
985
+ component,
986
+ current.colors,
987
+ theme(),
988
+ "Working…",
989
+ runtime,
990
+ );
991
+ expect(strippedFrames(generated.frames).every((frame) => frame.includes(composed.row))).toBe(
992
+ true,
993
+ );
994
+ });
995
+
996
+ it("bounds extension segments inside the complete Working-line row", () => {
997
+ const component = config().components.workingLine;
998
+ component.spinner = "pulse";
999
+ const composed = composeWorkingLineRow(component, "Working…", {
1000
+ tokens: { input: Number.MAX_SAFE_INTEGER, output: Number.MAX_SAFE_INTEGER },
1001
+ extensions: ["extension ".repeat(50), "must-not-fit"],
1002
+ });
1003
+ expect(composed.row).toContain("extension");
1004
+ expect(composed.row).toContain("…");
1005
+ expect(composed.row).not.toContain("must-not-fit");
1006
+ expect(
1007
+ workingLineSpinnerWidth(component.spinner) + 1 + visibleWidth(composed.row) + 3,
1008
+ ).toBeLessThanOrEqual(MAX_WORKING_LINE_ROW_CELLS);
1009
+ });
1010
+
971
1011
  it("reserves exact Tokens, then allocates Message, Elapsed, and truncated Tool within 80 cells", () => {
972
1012
  const component = config().components.workingLine;
973
1013
  const composed = composeWorkingLineRow(component, "x".repeat(43), {
@@ -1061,6 +1101,7 @@ describe("working-line runtime ownership", () => {
1061
1101
  harness.controller.finishAgent(harness.ctx);
1062
1102
  harness.controller.dispose(harness.ctx);
1063
1103
  harness.clock.reset();
1104
+ expect(harness.controller.isAvailable()).toBe(false);
1064
1105
  expect(harness.calls).toEqual([]);
1065
1106
  });
1066
1107
 
@@ -1069,6 +1110,7 @@ describe("working-line runtime ownership", () => {
1069
1110
  for (const ui of [{ setWorkingMessage: vi.fn() }, { setWorkingIndicator: vi.fn() }]) {
1070
1111
  const result = harness.controller.startSession({ hasUI: true, mode: "tui", ui });
1071
1112
  expect(result.applied).toBe(false);
1113
+ expect(harness.controller.isAvailable()).toBe(false);
1072
1114
  expect(Object.values(ui).every((fn) => !fn.mock.calls.length)).toBe(true);
1073
1115
  }
1074
1116
  expect(harness.calls).toEqual([]);
@@ -1099,6 +1141,42 @@ describe("working-line runtime ownership", () => {
1099
1141
  ]);
1100
1142
  });
1101
1143
 
1144
+ it("accepts render-equivalent extension text and deduplicates its identical replay", () => {
1145
+ const harness = runtime(true);
1146
+ harness.controller.startSession(harness.ctx);
1147
+ const eventHandlers = new Map<string, Set<(data: unknown) => void>>();
1148
+ const events: EventBus = {
1149
+ emit(channel: string, data: unknown) {
1150
+ for (const handler of eventHandlers.get(channel) ?? []) handler(data);
1151
+ },
1152
+ on(channel: string, handler: (data: unknown) => void) {
1153
+ const current = eventHandlers.get(channel) ?? new Set();
1154
+ current.add(handler);
1155
+ eventHandlers.set(channel, current);
1156
+ return () => current.delete(handler);
1157
+ },
1158
+ };
1159
+ const publish = vi.fn((segments: readonly string[]) =>
1160
+ harness.controller.updateExtensionSegments(segments, harness.ctx),
1161
+ );
1162
+ new WorkingLineExtensionSegments(events, () => harness.controller.isAvailable(), publish);
1163
+
1164
+ events.emit(ZENTUI_WORKING_LINE_SEGMENT_EVENT, { key: "publisher:value", text: "queue 2" });
1165
+ const indicatorWrites = harness.calls.filter(([name]) => name === "indicator").length;
1166
+ events.emit(ZENTUI_WORKING_LINE_SEGMENT_EVENT, {
1167
+ key: "publisher:value",
1168
+ text: " queue 2 ",
1169
+ });
1170
+ expect(harness.calls.filter(([name]) => name === "indicator")).toHaveLength(indicatorWrites);
1171
+ expect(publish).toHaveBeenCalledTimes(2);
1172
+
1173
+ events.emit(ZENTUI_WORKING_LINE_SEGMENT_EVENT, {
1174
+ key: "publisher:value",
1175
+ text: " queue 2 ",
1176
+ });
1177
+ expect(publish).toHaveBeenCalledTimes(2);
1178
+ });
1179
+
1102
1180
  it("does not rewrite Static frames for text-speed or spinner-color participation changes", () => {
1103
1181
  const harness = runtime(true);
1104
1182
  harness.current.components.workingLine.textAnimation = "disabled";
@@ -1454,7 +1532,7 @@ describe("working-line runtime ownership", () => {
1454
1532
  let generationPending = true;
1455
1533
  const delayedTheme = theme();
1456
1534
  const baseFg = delayedTheme.fg.bind(delayedTheme);
1457
- delayedTheme.fg = (color, text) => {
1535
+ delayedTheme.fg = (color: Parameters<typeof baseFg>[0], text: Parameters<typeof baseFg>[1]) => {
1458
1536
  if (generationPending) {
1459
1537
  now += 250;
1460
1538
  generationPending = false;
@@ -1739,6 +1817,7 @@ describe("working-line runtime ownership", () => {
1739
1817
  };
1740
1818
  const controller = new WorkingLineController(() => current, theme);
1741
1819
  expect(controller.startSession(ctx).applied).toBe(true);
1820
+ expect(controller.isAvailable()).toBe(true);
1742
1821
  current.components.workingLine.spinnerIntervalMs += 1;
1743
1822
  failureSets = 2;
1744
1823
  expect(controller.reconcile(ctx).applied).toBe(false);
@@ -1750,10 +1829,12 @@ describe("working-line runtime ownership", () => {
1750
1829
  ]);
1751
1830
  expect(indicatorSurface).toBeUndefined();
1752
1831
  expect(messageSurface).toBeUndefined();
1832
+ expect(controller.isAvailable()).toBe(false);
1753
1833
  const afterRelease = calls.length;
1754
1834
  controller.updateTokens({ input: 1, output: 1 }, ctx);
1755
1835
  expect(calls).toHaveLength(afterRelease);
1756
1836
  expect(controller.reconcile(ctx).applied).toBe(true);
1837
+ expect(controller.isAvailable()).toBe(true);
1757
1838
  expect(calls.slice(-2)).toEqual(["message-set", "indicator-set"]);
1758
1839
  });
1759
1840
 
@@ -1999,10 +2080,17 @@ describe("working-line runtime ownership", () => {
1999
2080
  harness.controller.startTurn(harness.ctx);
2000
2081
  harness.current.components.workingLine.enabled = true;
2001
2082
  expect(harness.controller.reconcile(harness.ctx).applied).toBe(true);
2083
+ expect(harness.controller.isAvailable()).toBe(true);
2002
2084
  expect(harness.controller.currentMessage()).toBe("B");
2003
2085
  harness.current.components.workingLine.enabled = false;
2086
+ expect(harness.controller.isAvailable()).toBe(false);
2004
2087
  harness.controller.reconcile(harness.ctx);
2088
+ expect(harness.controller.isAvailable()).toBe(false);
2089
+ harness.current.components.workingLine.enabled = true;
2090
+ expect(harness.controller.reconcile(harness.ctx).applied).toBe(true);
2091
+ expect(harness.controller.isAvailable()).toBe(true);
2005
2092
  harness.controller.dispose(harness.ctx);
2093
+ expect(harness.controller.isAvailable()).toBe(false);
2006
2094
  harness.controller.dispose(harness.ctx);
2007
2095
  expect(
2008
2096
  harness.calls.map(([name, value]) => [name, value === undefined ? "reset" : "set"]),
@@ -2011,6 +2099,10 @@ describe("working-line runtime ownership", () => {
2011
2099
  ["indicator", "set"],
2012
2100
  ["indicator", "reset"],
2013
2101
  ["message", "reset"],
2102
+ ["message", "set"],
2103
+ ["indicator", "set"],
2104
+ ["indicator", "reset"],
2105
+ ["message", "reset"],
2014
2106
  ]);
2015
2107
  });
2016
2108
  });
package/dist/index.d.ts CHANGED
@@ -26,7 +26,7 @@ export { type BashOperations, type BashSpawnContext, type BashSpawnHook, type Ba
26
26
  export { hasTrustRequiringProjectResources, type ProjectTrustDecision, ProjectTrustStore, type ProjectTrustStoreEntry, type ProjectTrustUpdate, } from "./core/trust-manager.ts";
27
27
  export { type MainOptions, main } from "./main.ts";
28
28
  export { InteractiveMode, type InteractiveModeOptions, type ModelInfo, type PrintModeOptions, RpcClient, type RpcClientOptions, type RpcCommand, type RpcEventListener, type RpcExtensionUIRequest, type RpcExtensionUIResponse, type RpcResponse, type RpcSessionState, runPrintMode, runRpcMode, } from "./modes/index.ts";
29
- export { ArminComponent, AssistantMessageComponent, BashExecutionComponent, BorderedLoader, BranchSummaryMessageComponent, CompactionSummaryMessageComponent, CustomEditor, CustomMessageComponent, DynamicBorder, ExtensionEditorComponent, ExtensionInputComponent, ExtensionSelectorComponent, FooterComponent, keyHint, keyText, LoginDialogComponent, ModelSelectorComponent, OAuthSelectorComponent, type RenderDiffOptions, rawKeyHint, renderDiff, SessionSelectorComponent, type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent, ShowImagesSelectorComponent, SkillInvocationMessageComponent, ThemeSelectorComponent, ThinkingSelectorComponent, ToolExecutionComponent, type ToolExecutionOptions, TreeSelectorComponent, truncateToVisualLines, UserMessageComponent, UserMessageSelectorComponent, type VisualTruncateResult, } from "./modes/interactive/components/index.ts";
29
+ export { ArminComponent, AssistantMessageComponent, BashExecutionComponent, BorderedLoader, BranchSummaryMessageComponent, CompactionSummaryMessageComponent, CustomEditor, type CustomEditorOptions, CustomMessageComponent, DynamicBorder, ExtensionEditorComponent, ExtensionInputComponent, ExtensionSelectorComponent, FooterComponent, keyHint, keyText, LoginDialogComponent, ModelSelectorComponent, OAuthSelectorComponent, type RenderDiffOptions, rawKeyHint, renderDiff, SessionSelectorComponent, type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent, ShowImagesSelectorComponent, SkillInvocationMessageComponent, ThemeSelectorComponent, ThinkingSelectorComponent, ToolExecutionComponent, type ToolExecutionOptions, TreeSelectorComponent, truncateToVisualLines, UserMessageComponent, UserMessageSelectorComponent, type VisualTruncateResult, } from "./modes/interactive/components/index.ts";
30
30
  export { getLanguageFromPath, getMarkdownTheme, getSelectListTheme, getSettingsListTheme, highlightCode, initTheme, Theme, type ThemeColor, } from "./modes/interactive/theme/theme.ts";
31
31
  export { copyToClipboard } from "./utils/clipboard.ts";
32
32
  export { parseFrontmatter, stripFrontmatter } from "./utils/frontmatter.ts";
package/dist/main.js CHANGED
@@ -34,7 +34,8 @@ import { hasTrustRequiringProjectResources, ProjectTrustStore } from "./core/tru
34
34
  import { builtInExtensions } from "./core/built-in-extensions.js";
35
35
  import { runMigrations, showDeprecationWarnings } from "./migrations.js";
36
36
  import { InteractiveMode, runPrintMode, runRpcMode } from "./modes/index.js";
37
- import { initTheme, stopThemeWatcher } from "./modes/interactive/theme/theme.js";
37
+ import { initTheme, setThemeJsonValidator, stopThemeWatcher } from "./modes/interactive/theme/theme.js";
38
+ import { validateThemeJson } from "./modes/interactive/theme/theme-json.js";
38
39
  import { handleConfigCommand, handlePackageCommand } from "./package-manager-cli.js";
39
40
  import { isLocalPath, normalizePath, resolvePath } from "./utils/paths.js";
40
41
  import { cleanupWindowsSelfUpdateQuarantine } from "./utils/windows-self-update.js";
@@ -686,6 +687,8 @@ export async function main(args, options) {
686
687
  time("readPipedStdin");
687
688
  const { initialMessage, initialImages } = await prepareInitialMessage(parsed, settingsManager.getImageAutoResize(), stdinContent);
688
689
  time("prepareInitialMessage");
690
+ // pi reads user-authored themes, so it opts into full validation before any theme loads.
691
+ setThemeJsonValidator(validateThemeJson);
689
692
  initTheme(settingsManager.getTheme(), appMode === "interactive");
690
693
  time("initTheme");
691
694
  // Show deprecation warnings in interactive mode
@@ -0,0 +1,19 @@
1
+ import { type Component, ScrollView, type ScrollViewScrollbar } from "@earendil-works/pi-tui";
2
+ export interface ChatViewportOptions {
3
+ readonly document: Component;
4
+ readonly pendingMessages: Component;
5
+ readonly status: Component;
6
+ readonly editor: Component;
7
+ readonly footer: Component;
8
+ readonly widgetsAbove?: Component;
9
+ readonly widgetsBelow?: Component;
10
+ readonly scrollbar?: ScrollViewScrollbar;
11
+ readonly scrollbarTrackStyle?: (text: string) => string;
12
+ readonly scrollbarThumbStyle?: (text: string) => string;
13
+ }
14
+ export interface ChatViewport {
15
+ readonly root: Component;
16
+ readonly transcript: ScrollView;
17
+ }
18
+ /** Shared fullscreen transcript and fixed input-dock layout. */
19
+ export declare function createChatViewport(options: ChatViewportOptions): ChatViewport;
@@ -0,0 +1,27 @@
1
+ import { ScrollView, VStack } from "@earendil-works/pi-tui";
2
+ /** Shared fullscreen transcript and fixed input-dock layout. */
3
+ export function createChatViewport(options) {
4
+ const transcript = new ScrollView(options.document, {
5
+ follow: "end",
6
+ primary: true,
7
+ overscroll: "chain",
8
+ scrollbar: options.scrollbar ?? "auto",
9
+ ...(options.scrollbarTrackStyle === undefined ? {} : { scrollbarTrackStyle: options.scrollbarTrackStyle }),
10
+ ...(options.scrollbarThumbStyle === undefined ? {} : { scrollbarThumbStyle: options.scrollbarThumbStyle }),
11
+ });
12
+ const dock = new VStack([
13
+ { component: options.pendingMessages, shrink: 1, minSize: 0 },
14
+ { component: options.status, shrink: 1, minSize: 0 },
15
+ ...(options.widgetsAbove === undefined ? [] : [{ component: options.widgetsAbove, shrink: 1, minSize: 0 }]),
16
+ { component: options.editor, shrink: 1, minSize: 3 },
17
+ ...(options.widgetsBelow === undefined ? [] : [{ component: options.widgetsBelow, shrink: 1, minSize: 0 }]),
18
+ { component: options.footer, shrink: 1, minSize: 1 },
19
+ ]);
20
+ return {
21
+ transcript,
22
+ root: new VStack([
23
+ { component: transcript, basis: 0, grow: 1, shrink: 1, minSize: 1 },
24
+ { component: dock, basis: "auto", grow: 0, shrink: 1, minSize: 1 },
25
+ ]),
26
+ };
27
+ }
@@ -1,5 +1,6 @@
1
1
  import type { AssistantMessage } from "@earendil-works/pi-ai";
2
2
  import { Container, type MarkdownTheme } from "@earendil-works/pi-tui";
3
+ import type { MarkdownTransformer } from "../../../core/extensions/types.ts";
3
4
  /**
4
5
  * Component that renders a complete assistant message
5
6
  */
@@ -9,13 +10,16 @@ export declare class AssistantMessageComponent extends Container {
9
10
  private markdownTheme;
10
11
  private hiddenThinkingLabel;
11
12
  private outputPad;
13
+ private markdownTransformers;
12
14
  private lastMessage?;
13
15
  private hasToolCalls;
14
- constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, markdownTheme?: MarkdownTheme, hiddenThinkingLabel?: string, outputPad?: number);
16
+ private isStreaming;
17
+ private thinkingVisibilityOverrides;
18
+ constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, markdownTheme?: MarkdownTheme, hiddenThinkingLabel?: string, outputPad?: number, markdownTransformers?: readonly MarkdownTransformer[]);
15
19
  invalidate(): void;
16
20
  setHideThinkingBlock(hide: boolean): void;
17
21
  setHiddenThinkingLabel(label: string): void;
18
22
  setOutputPad(padding: number): void;
19
23
  render(width: number): string[];
20
- updateContent(message: AssistantMessage): void;
24
+ updateContent(message: AssistantMessage, isStreaming?: boolean): void;
21
25
  }
@@ -1,6 +1,7 @@
1
- import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
1
+ import { Container, Markdown, MouseRegion, Spacer, Text } from "@earendil-works/pi-tui";
2
2
  import { normalizeAssistantThinkingTags } from "../../../utils/thinking-tags.js";
3
3
  import { getMarkdownTheme, theme } from "../theme/theme.js";
4
+ import { createMarkdownTransform } from "./markdown-transform.js";
4
5
  const OSC133_ZONE_START = "\x1b]133;A\x07";
5
6
  const OSC133_ZONE_END = "\x1b]133;B\x07";
6
7
  const OSC133_ZONE_FINAL = "\x1b]133;C\x07";
@@ -13,14 +14,18 @@ export class AssistantMessageComponent extends Container {
13
14
  markdownTheme;
14
15
  hiddenThinkingLabel;
15
16
  outputPad;
17
+ markdownTransformers;
16
18
  lastMessage;
17
19
  hasToolCalls = false;
18
- constructor(message, hideThinkingBlock = false, markdownTheme = getMarkdownTheme(), hiddenThinkingLabel = "Thinking...", outputPad = 1) {
20
+ isStreaming = false;
21
+ thinkingVisibilityOverrides = new Map();
22
+ constructor(message, hideThinkingBlock = false, markdownTheme = getMarkdownTheme(), hiddenThinkingLabel = "Thinking...", outputPad = 1, markdownTransformers = []) {
19
23
  super();
20
24
  this.hideThinkingBlock = hideThinkingBlock;
21
25
  this.markdownTheme = markdownTheme;
22
26
  this.hiddenThinkingLabel = hiddenThinkingLabel;
23
27
  this.outputPad = outputPad;
28
+ this.markdownTransformers = markdownTransformers;
24
29
  // Container for text/thinking content
25
30
  this.contentContainer = new Container();
26
31
  this.addChild(this.contentContainer);
@@ -36,6 +41,7 @@ export class AssistantMessageComponent extends Container {
36
41
  }
37
42
  setHideThinkingBlock(hide) {
38
43
  this.hideThinkingBlock = hide;
44
+ this.thinkingVisibilityOverrides.clear();
39
45
  if (this.lastMessage) {
40
46
  this.updateContent(this.lastMessage);
41
47
  }
@@ -61,9 +67,10 @@ export class AssistantMessageComponent extends Container {
61
67
  lines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];
62
68
  return lines;
63
69
  }
64
- updateContent(message) {
70
+ updateContent(message, isStreaming = this.isStreaming) {
65
71
  message = normalizeAssistantThinkingTags(message);
66
72
  this.lastMessage = message;
73
+ this.isStreaming = isStreaming;
67
74
  // Clear content container
68
75
  this.contentContainer.clear();
69
76
  const hasVisibleContent = message.content.some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
@@ -71,12 +78,15 @@ export class AssistantMessageComponent extends Container {
71
78
  this.contentContainer.addChild(new Spacer(1));
72
79
  }
73
80
  // Render content in order
81
+ let thinkingRunIndex = 0;
74
82
  for (let i = 0; i < message.content.length; i++) {
75
83
  const content = message.content[i];
76
84
  if (content.type === "text" && content.text.trim()) {
77
85
  // Assistant text messages with no background - trim the text
78
86
  // Set paddingY=0 to avoid extra spacing before tool executions
79
- this.contentContainer.addChild(new Markdown(content.text.trim(), this.outputPad, 0, this.markdownTheme));
87
+ this.contentContainer.addChild(new Markdown(content.text.trim(), this.outputPad, 0, this.markdownTheme, undefined, {
88
+ transform: createMarkdownTransform("assistant", this.isStreaming, this.markdownTransformers),
89
+ }));
80
90
  }
81
91
  else if (content.type === "thinking") {
82
92
  const thinkingBlocks = [];
@@ -99,17 +109,24 @@ export class AssistantMessageComponent extends Container {
99
109
  const hasVisibleContentAfter = message.content
100
110
  .slice(i + 1)
101
111
  .some((c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()));
102
- if (this.hideThinkingBlock) {
103
- // Show one static label for each run of thinking blocks when hidden.
104
- this.contentContainer.addChild(new Text(theme.italic(theme.fg("thinkingText", this.hiddenThinkingLabel)), this.outputPad, 0));
105
- }
106
- else {
107
- // Render each run of thinking blocks as one Markdown section.
108
- this.contentContainer.addChild(new Markdown(thinkingBlocks.join("\n\n"), this.outputPad, 0, this.markdownTheme, {
112
+ const runIndex = thinkingRunIndex++;
113
+ const hidden = this.thinkingVisibilityOverrides.get(runIndex) ?? this.hideThinkingBlock;
114
+ const thinkingComponent = hidden
115
+ ? new Text(theme.italic(theme.fg("thinkingText", this.hiddenThinkingLabel)), this.outputPad, 0)
116
+ : new Markdown(thinkingBlocks.join("\n\n"), this.outputPad, 0, this.markdownTheme, {
109
117
  color: (text) => theme.fg("thinkingText", text),
110
118
  italic: true,
111
- }));
112
- }
119
+ }, {
120
+ transform: createMarkdownTransform("assistant-thinking", this.isStreaming, this.markdownTransformers),
121
+ });
122
+ this.contentContainer.addChild(new MouseRegion(thinkingComponent, (event) => {
123
+ if (event.type !== "click" || event.button !== "left")
124
+ return undefined;
125
+ this.thinkingVisibilityOverrides.set(runIndex, !hidden);
126
+ if (this.lastMessage)
127
+ this.updateContent(this.lastMessage);
128
+ return { handled: true };
129
+ }));
113
130
  if (hasVisibleContentAfter) {
114
131
  this.contentContainer.addChild(new Spacer(1));
115
132
  }
@@ -1,17 +1,26 @@
1
1
  import { Editor, type EditorOptions, type EditorTheme, type TUI } from "@earendil-works/pi-tui";
2
2
  import type { AppKeybinding, KeybindingsManager } from "../../../core/keybindings.ts";
3
+ import type { WorkingStatusIndicator } from "./status-indicator.ts";
4
+ export type CustomEditorOptions = EditorOptions & {
5
+ /** Render the streaming working status in the editor's top border. */
6
+ embedWorkingStatus?: boolean;
7
+ };
3
8
  /**
4
9
  * Custom editor that handles app-level keybindings for coding-agent.
5
10
  */
6
11
  export declare class CustomEditor extends Editor {
7
12
  private keybindings;
13
+ private workingStatusIndicator;
14
+ readonly embedWorkingStatus: boolean;
8
15
  actionHandlers: Map<AppKeybinding, () => void>;
9
16
  onEscape?: () => void;
10
17
  onCtrlD?: () => void;
11
18
  onPasteImage?: () => void;
12
19
  /** Handler for extension-registered shortcuts. Returns true if handled. */
13
20
  onExtensionShortcut?: (data: string) => boolean;
14
- constructor(tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager, options?: EditorOptions);
21
+ constructor(tui: TUI, theme: EditorTheme, keybindings: KeybindingsManager, options?: CustomEditorOptions);
22
+ setWorkingStatusIndicator(indicator: WorkingStatusIndicator | undefined): void;
23
+ protected renderTopBorder(width: number, hiddenLineCount: number): string;
15
24
  /**
16
25
  * Register a handler for an app action.
17
26
  */
@@ -1,9 +1,11 @@
1
- import { Editor } from "@earendil-works/pi-tui";
1
+ import { Editor, visibleWidth } from "@earendil-works/pi-tui";
2
2
  /**
3
3
  * Custom editor that handles app-level keybindings for coding-agent.
4
4
  */
5
5
  export class CustomEditor extends Editor {
6
6
  keybindings;
7
+ workingStatusIndicator;
8
+ embedWorkingStatus;
7
9
  actionHandlers = new Map();
8
10
  // Special handlers that can be dynamically replaced
9
11
  onEscape;
@@ -14,6 +16,42 @@ export class CustomEditor extends Editor {
14
16
  constructor(tui, theme, keybindings, options) {
15
17
  super(tui, theme, options);
16
18
  this.keybindings = keybindings;
19
+ this.embedWorkingStatus = options?.embedWorkingStatus ?? false;
20
+ }
21
+ setWorkingStatusIndicator(indicator) {
22
+ this.workingStatusIndicator = indicator;
23
+ }
24
+ renderTopBorder(width, hiddenLineCount) {
25
+ if (!this.embedWorkingStatus || !this.workingStatusIndicator || width <= 0) {
26
+ return super.renderTopBorder(width, hiddenLineCount);
27
+ }
28
+ let status = this.workingStatusIndicator.renderInBorder(Math.max(1, width - 5));
29
+ let statusWidth = visibleWidth(status);
30
+ if (statusWidth === 0)
31
+ return super.renderTopBorder(width, hiddenLineCount);
32
+ const overflowLabel = hiddenLineCount > 0 ? ` ↑ ${hiddenLineCount} more ` : undefined;
33
+ const overflowLabelWidth = overflowLabel ? visibleWidth(overflowLabel) : 0;
34
+ const overflowStart = Math.floor((width - overflowLabelWidth) / 2);
35
+ const canFitOverflow = () => overflowLabel !== undefined && overflowLabelWidth + 2 <= width && overflowStart - (3 + statusWidth + 1) >= 1;
36
+ if (overflowLabel && !canFitOverflow()) {
37
+ status = this.workingStatusIndicator.renderSpinnerInBorder(width);
38
+ statusWidth = visibleWidth(status);
39
+ }
40
+ if (canFitOverflow()) {
41
+ const leftBlockWidth = 3 + statusWidth + 1;
42
+ return (this.borderColor("── ") +
43
+ status +
44
+ this.borderColor(` ${"─".repeat(overflowStart - leftBlockWidth)}${overflowLabel}${"─".repeat(width - overflowStart - overflowLabelWidth)}`));
45
+ }
46
+ if (width >= statusWidth + 5) {
47
+ return this.borderColor("── ") + status + this.borderColor(` ${"─".repeat(width - statusWidth - 4)}`);
48
+ }
49
+ status = this.workingStatusIndicator.renderSpinnerInBorder(width);
50
+ statusWidth = visibleWidth(status);
51
+ const prefixWidth = Math.min(3, Math.max(0, width - statusWidth));
52
+ return (this.borderColor("─".repeat(prefixWidth)) +
53
+ status +
54
+ this.borderColor("─".repeat(Math.max(0, width - prefixWidth - statusWidth))));
17
55
  }
18
56
  /**
19
57
  * Register a handler for an app action.
@@ -37,7 +37,7 @@ export class FirstTimeSetupComponent extends Container {
37
37
  // ponytail: one Text with embedded newlines, no new component.
38
38
  this.addChild(new Text(theme.fg("text", `${APP_NAME} is a fork of the Pi Coding Agent. Your Pi setup carries over:`), 1, 0));
39
39
  this.addChild(new Spacer(1));
40
- this.addChild(new Text(theme.fg("muted", " • Extensions in ~/.pi/agent/extensions load automatically.\n" +
40
+ this.addChild(new Text(theme.fg("muted", " • Extensions in ~/.selesai/agent/extensions load automatically.\n" +
41
41
  " • If the same extension exists in ~/.selesai/agent/extensions,\n" +
42
42
  " the selesai copy wins (set extensionHost in settings.json to change).\n\n" +
43
43
  " Selesai is tuned to match or outperform leading agent harnesses\n" +
@@ -4,7 +4,7 @@ export { BashExecutionComponent } from "./bash-execution.ts";
4
4
  export { BorderedLoader } from "./bordered-loader.ts";
5
5
  export { BranchSummaryMessageComponent } from "./branch-summary-message.ts";
6
6
  export { CompactionSummaryMessageComponent } from "./compaction-summary-message.ts";
7
- export { CustomEditor } from "./custom-editor.ts";
7
+ export { CustomEditor, type CustomEditorOptions } from "./custom-editor.ts";
8
8
  export { CustomMessageComponent } from "./custom-message.ts";
9
9
  export { DaxnutsComponent } from "./daxnuts.ts";
10
10
  export { type RenderDiffOptions, renderDiff } from "./diff.ts";
@@ -0,0 +1,2 @@
1
+ import type { MarkdownTransformContext, MarkdownTransformer } from "../../../core/extensions/types.ts";
2
+ export declare function createMarkdownTransform(messageType: MarkdownTransformContext["messageType"], isStreaming: boolean, transformers: readonly MarkdownTransformer[]): (markdown: string, availableWidth: number) => string;
@@ -0,0 +1,18 @@
1
+ export function createMarkdownTransform(messageType, isStreaming, transformers) {
2
+ return (markdown, availableWidth) => applyMarkdownTransformers(markdown, { messageType, isStreaming, availableWidth }, transformers);
3
+ }
4
+ function applyMarkdownTransformers(markdown, context, transformers) {
5
+ let transformedMarkdown = markdown;
6
+ for (const transformer of transformers) {
7
+ try {
8
+ const transformed = transformer(transformedMarkdown, context);
9
+ if (typeof transformed === "string") {
10
+ transformedMarkdown = transformed;
11
+ }
12
+ }
13
+ catch {
14
+ // Keep the current Markdown and continue with the next transformer.
15
+ }
16
+ }
17
+ return transformedMarkdown;
18
+ }
@@ -4,7 +4,7 @@ import { getModelSelectorSearchText } from "../model-search.js";
4
4
  import { refreshModelCatalogs } from "../model-catalog-refresh.js";
5
5
  import { theme } from "../theme/theme.js";
6
6
  import { DynamicBorder } from "./dynamic-border.js";
7
- import { keyHint } from "./keybinding-hints.js";
7
+ import { keyDisplayText, keyHint } from "./keybinding-hints.js";
8
8
  /**
9
9
  * Component that renders a model selector with search
10
10
  */
@@ -85,6 +85,9 @@ export class ModelSelectorComponent extends Container {
85
85
  this.listContainer = new Container();
86
86
  this.addChild(this.listContainer);
87
87
  this.addChild(new Spacer(1));
88
+ if (this.options.persistSelection !== false) {
89
+ this.addChild(new Text(theme.fg("dim", ` ${keyDisplayText("tui.select.confirm")} to select · ${keyDisplayText("app.models.save")} to set as default · ${keyDisplayText("tui.select.cancel")} to cancel`), 0, 0));
90
+ }
88
91
  // Add bottom border
89
92
  this.addChild(new DynamicBorder());
90
93
  // Render the current snapshot immediately, then refresh in the background.
@@ -223,20 +226,13 @@ export class ModelSelectorComponent extends Container {
223
226
  continue;
224
227
  const isSelected = i === this.selectedIndex;
225
228
  const isCurrent = modelsAreEqual(this.currentModel, item.model);
226
- let line = "";
227
- if (isSelected) {
228
- const prefix = theme.fg("accent", "→ ");
229
- const modelText = `${item.id}`;
230
- const providerBadge = theme.fg("muted", `[${item.provider}]`);
231
- const checkmark = isCurrent ? theme.fg("success", " ✓") : "";
232
- line = `${prefix + theme.fg("accent", modelText)} ${providerBadge}${checkmark}`;
233
- }
234
- else {
235
- const modelText = ` ${item.id}`;
236
- const providerBadge = theme.fg("muted", `[${item.provider}]`);
237
- const checkmark = isCurrent ? theme.fg("success", " ✓") : "";
238
- line = `${modelText} ${providerBadge}${checkmark}`;
239
- }
229
+ const isDefault = item.provider === this.settingsManager.getDefaultProvider() && item.id === this.settingsManager.getDefaultModel();
230
+ const defaultBadge = isDefault ? theme.fg("muted", " · default") : "";
231
+ const cursor = isSelected ? theme.fg("accent", "→ ") : " ";
232
+ const currentMarker = isCurrent ? theme.fg("accent", "✓ ") : " ";
233
+ const modelText = isSelected ? theme.fg("accent", item.id) : item.id;
234
+ const providerBadge = item.provider ? theme.fg("muted", `[${item.provider}]`) : "";
235
+ const line = `${cursor}${currentMarker}${modelText}${providerBadge ? ` ${providerBadge}` : ""}${defaultBadge}`;
240
236
  this.listContainer.addChild(new Text(line, 0, 0));
241
237
  }
242
238
  // Add scroll indicator if needed
@@ -303,6 +299,14 @@ export class ModelSelectorComponent extends Container {
303
299
  this.close();
304
300
  this.onCancelCallback();
305
301
  }
302
+ // Select and save as default. Vision-only selectors opt out via persistSelection.
303
+ else if (kb.matches(keyData, "app.models.save") && this.options.persistSelection !== false) {
304
+ const selectedModel = this.filteredModels[this.selectedIndex];
305
+ if (selectedModel && selectedModel.model.id !== "off") {
306
+ this.settingsManager.setDefaultModelAndProvider(selectedModel.model.provider, selectedModel.model.id);
307
+ this.handleSelect(selectedModel.model);
308
+ }
309
+ }
306
310
  // Pass everything else to search input
307
311
  else {
308
312
  this.searchInput.handleInput(keyData);
@@ -311,8 +315,6 @@ export class ModelSelectorComponent extends Container {
311
315
  }
312
316
  handleSelect(model) {
313
317
  this.close();
314
- if (model.id !== "off" && this.options.persistSelection !== false)
315
- this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
316
318
  this.onSelectCallback(model.id === "off" ? undefined : model);
317
319
  }
318
320
  getSearchInput() {