@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,4 +1,5 @@
1
1
  import type { FanoutMode, SearchProviderName } from '../types.js';
2
+ import { hasActiveTokenInAccount } from './settings-reader.js';
2
3
 
3
4
  export type SearxngOptions = {
4
5
  categories?: string[];
@@ -19,9 +19,11 @@ export function hasActiveTokenInAccount(authPath: string = join(getAgentDir(), '
19
19
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return false;
20
20
  const auth = parsed as { accounts?: unknown; activeId?: unknown };
21
21
  if (!Array.isArray(auth.accounts) || typeof auth.activeId !== 'string') return false;
22
- return auth.accounts.some(
23
- (a) => a && typeof a === 'object' && !Array.isArray(a) && (a as { id?: unknown }).id === auth.activeId
24
- );
22
+ return auth.accounts.some((a) => {
23
+ if (!a || typeof a !== 'object' || Array.isArray(a)) return false;
24
+ const account = a as { id?: unknown; apiKey?: unknown };
25
+ return account.id === auth.activeId && typeof account.apiKey === 'string' && account.apiKey.trim() !== '';
26
+ });
25
27
  } catch {
26
28
  return false;
27
29
  }
@@ -27,7 +27,9 @@ export function readActiveTokenInAccount(
27
27
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return undefined;
28
28
  const auth = parsed as TokenInAuth;
29
29
  if (!Array.isArray(auth.accounts) || typeof auth.activeId !== 'string') return undefined;
30
- return auth.accounts.find((a) => a && typeof a === 'object' && a.id === auth.activeId && typeof a.apiKey === 'string');
30
+ return auth.accounts.find(
31
+ (a) => a && typeof a === 'object' && a.id === auth.activeId && typeof a.apiKey === 'string' && a.apiKey.trim() !== ''
32
+ );
31
33
  } catch {
32
34
  return undefined;
33
35
  }
@@ -82,8 +84,10 @@ export function createTokenInSearchTool({
82
84
  });
83
85
  }
84
86
 
85
- const baseUrl = (account.baseUrl ?? TOKENIN_DEFAULT_BASE_URL).replace(/\/+$/, '');
86
- const url = `${baseUrl}/v1/search/${TOKENIN_SEARCH_TOOL_NAME}`;
87
+ const rootUrl = (account.baseUrl ?? TOKENIN_DEFAULT_BASE_URL)
88
+ .replace(/\/+$/, '')
89
+ .replace(/\/v1$/, '');
90
+ const url = `${rootUrl}/v1/search/${TOKENIN_SEARCH_TOOL_NAME}`;
87
91
 
88
92
  try {
89
93
  const response = await fetchImpl(url, {
@@ -34,7 +34,7 @@ export type ToolError = {
34
34
  export type SearchMetadata = {
35
35
  backend: SearchProviderName;
36
36
  cacheHit: boolean;
37
- fallbackFrom?: Exclude<SearchProviderName, 'tokenin'>;
37
+ fallbackFrom?: SearchProviderName;
38
38
  fallbackReason?: string;
39
39
  fanout?: FanoutMetadata;
40
40
  };
@@ -37,6 +37,8 @@ Editor, User messages, Thinking (Experimental), Working line, and selector borde
37
37
 
38
38
  The Starship Footer shows directory, Git, runtime, context, tokens, and cost. Optional segments include model/provider, package version, session duration, `user@host`, time, OS, Git commit, Git metrics, and third-party extension statuses. The layout is segment-driven by default and supports a complete Starship-style format template.
39
39
 
40
+ When the Working line is enabled, third-party extensions can publish keyed text segments through Pi's shared event bus. Zentui composes those segments into the owned row so Classic and KITT animate across them with the built-in content. See the [Working-line extension integration](https://github.com/lmilojevicc/pi-zentui/blob/main/docs/configuration.md#working-line-extension-integration) reference.
41
+
40
42
  Zentui detects a broad set of runtime and language modules, preserves Nerd Font icons with an ASCII mode, and can source colors from the active Pi theme or directly from the terminal palette.
41
43
 
42
44
  ## Screenshots
@@ -95,6 +97,21 @@ Run `/zentui` inside Pi to configure Appearance, Editor, User messages, Thinking
95
97
  ~/.pi/agent/zentui.json
96
98
  ```
97
99
 
100
+ ### Component presets
101
+
102
+ Choose **Appearance → Preset** in `/zentui`, or run `/zentui preset <id>`:
103
+
104
+ | ID | Editor (enabled) | Footer | User messages |
105
+ | --- | --- | --- | --- |
106
+ | `opencode` | Opencode | Starship | Framed |
107
+ | `opencode-copy-friendly` | Opencode (copy-friendly) | Starship | Framed (copy-friendly) |
108
+ | `rail` | Accent Rail | Starship | Compact (accent rail) |
109
+ | `minimalist` | Minimalist | Hidden (zero rows) | Zentui styling disabled |
110
+
111
+ Presets apply once, saving only these component selections. Colors, color sources, style options, icons, Footer segments/formats, selector borders, Working line, and Thinking remain unchanged. Minimalist preserves the dormant message style and leaves Pi's native message presentation alone. Hidden suppresses the Footer; it is not Native.
112
+
113
+ The displayed preset is derived from your current selections: individual changes may show **Custom**, and returning to a matching combination restores its name. No `preset` config key is saved or reapplied at startup. Defaults are unchanged and match Opencode. Selecting a preset keeps settings open for further adjustments. Editor installation waits until the panel closes; if editor ownership prevents application, the saved choice may require reloading Pi.
114
+
98
115
  A small starter config:
99
116
 
100
117
  ```json
@@ -8,7 +8,7 @@ Zentui reads optional user configuration from `~/.pi/agent/zentui.json`. Missing
8
8
 
9
9
  The interactive `/zentui` menu is split into nine component-oriented sections. Use `Tab` and `Shift+Tab` to switch sections:
10
10
 
11
- 1. **Appearance** — selector-border enablement, style, and colors; icon mode.
11
+ 1. **Appearance** — component Preset; selector-border enablement, style, and colors; icon mode.
12
12
  2. **Editor** — enablement, style, colors, model label, border behavior, viewport indicators, settings for the selected editor style, and a static synthetic preview.
13
13
  3. **User messages** — enablement, style, colors, and a static synthetic Markdown preview.
14
14
  4. **Thinking (Experimental)** — private Rail, Tree, or Streaming rendering; active Streaming can switch live to Rail or Tree, Rail and Tree can switch live between each other, and the private renderer may break after Pi updates.
@@ -45,6 +45,44 @@ Useful slash-command shortcuts:
45
45
 
46
46
  `footer`, `statusline`, `status`, and `status line` are aliases. Enable selects Starship, disable selects Native, and toggle selects Native only from Starship; Native or Hidden toggle to Starship.
47
47
 
48
+ ### Component presets
49
+
50
+ Use the first **Appearance → Preset** row, or one of these exact commands:
51
+
52
+ ```text
53
+ /zentui preset opencode
54
+ /zentui preset opencode-copy-friendly
55
+ /zentui preset rail
56
+ /zentui preset minimalist
57
+ ```
58
+
59
+ | Preset ID | `components.editor` | `components.footer.style` | `components.userMessages` |
60
+ | --- | --- | --- | --- |
61
+ | `opencode` | `enabled: true`, `style: "opencode"` | `"starship"` | `enabled: true`, `style: "framed"` |
62
+ | `opencode-copy-friendly` | `enabled: true`, `style: "opencode-copy-friendly"` | `"starship"` | `enabled: true`, `style: "framed-copy-friendly"` |
63
+ | `rail` | `enabled: true`, `style: "accent-rail"` | `"starship"` | `enabled: true`, `style: "compact"` |
64
+ | `minimalist` | `enabled: true`, `style: "minimalist"` | `"hidden"` | `enabled: false` (style preserved) |
65
+
66
+ These are one-time, atomic selection patches, not ongoing profiles. Only the listed leaves are written, with the existing obsolete copy-friendly/Footer-enabled migration flags removed when their styles are explicitly selected. Colors and color sources, per-style options, icons, Footer segments/templates/path settings, all other components, and unknown fields remain untouched. Unsupported future style IDs are preserved unless the preset explicitly replaces that style; Minimalist even preserves an unsupported dormant message style. Existing legacy option resolution continues to work.
67
+
68
+ **Custom** is a derived display state, not a selectable preset or a saved key. Matching checks only the listed selection leaves, not colors/options or current runtime ownership; Minimalist ignores dormant message style. Unsupported active selected styles do not match. Hand-editing or individually changing a selection can show Custom; returning to a matching combination restores the preset label. No `preset` config key is used, and nothing is automatically reapplied on startup. New-install defaults remain unchanged and match Opencode.
69
+
70
+ The existing Minimalist editor stays enabled with its saved metadata/options. Disabled User messages releases only Zentui styling, leaving native or predecessor rendering intact. Hidden installs an owned zero-row Footer, whereas Native releases Zentui's Footer to Pi or a predecessor; `/zentui statusline disable` still selects Native, not Hidden.
71
+
72
+ Selecting presets keeps the settings panel open and preserves its focus. Config, Footer, and User messages update immediately; editor installation waits until the panel closes and Pi restores the draft. Only the latest editor settings are reconciled on exit, and shutdown cancels pending installation. With public editor-text APIs available, opening settings expands nonempty drafts before Pi snapshots them, preserving collapsed paste contents; this can move the cursor to the end and add an undo step. Empty drafts are untouched. Older hosts without these APIs retain Pi's existing draft-restoration behavior. Saves fail without changing active settings or overwriting corrupt/unreadable JSON. Live application reconciles only Editor, User messages, Footer, and dependent timers. Editor ownership restrictions are reported as saved-but-not-applied/reload-required; Footer host failures retain existing fail-open behavior. Direct preset commands also save in non-TUI modes without installing TUI components. Unknown IDs, missing IDs, and extra arguments do not change settings.
73
+
74
+ ### Extension-status hyperlinks
75
+
76
+ An extension's **Original** color mode preserves SGR styling and HTTP/HTTPS
77
+ OSC 8 hyperlinks supplied by that extension. For example, set
78
+ `components.footer.styles.starship.extensionStatuses.colorModes.github-pr` to
79
+ `"original"` to retain a PR link from a GitHub status extension. Open it with
80
+ your terminal's link-opening gesture. Other URL schemes and unrelated terminal
81
+ controls (including clipboard, title, and cursor commands) are removed.
82
+
83
+ Zentui color mode continues to show plain status text. Zentui does not infer a
84
+ URL when an extension supplies only a label.
85
+
48
86
  ## Complete default configuration
49
87
 
50
88
  Copy this example and change only the values you need. Optional editor source-aware overrides such as `editorRail`, `editorGitBranch`, and `editorThinkingMax` are intentionally omitted.
@@ -428,7 +466,38 @@ Classic and KITT move color across message and segments. **Animate spinner color
428
466
 
429
467
  Both speeds accept `30..1000` ms. Classic/KITT combine both cadences through one Pi Loader interval; exact cycles are used within 1024-frame/512-KiB limits. Pathological custom pairs use a bounded evenly distributed schedule with at most half a spinner-cycle and half a text-step rounding. Legacy `intervalMs` is accepted only as migration input for `spinnerIntervalMs` when the canonical field is absent.
430
468
 
431
- Content reserves the complete Tokens label first, then Message, Thought, Elapsed, and Tool allocation, while preserving visual order **Message · Tool · Elapsed · Thought · Tokens** within the 80-column Loader-row contract. Active thought starts as `thinking 0s`; completed positive thought becomes `thought for Ns`. Rebuilds preserve spinner and visible color phase. Pi's working-row APIs are global and unkeyed, so another extension may win by writing last.
469
+ Content reserves the complete Tokens label and active extension segments first, then Message, Thought, Elapsed, and Tool allocation, while preserving visual order **Message · Tool · Elapsed · Thought · Tokens · Extensions** within the 80-column Loader-row contract. Active thought starts as `thinking 0s`; completed positive thought becomes `thought for Ns`. Rebuilds preserve spinner and visible color phase. Pi's working-row APIs are global and unkeyed, so another extension may win by writing last.
470
+
471
+ ### Working-line extension integration
472
+
473
+ Third-party extensions can add dynamic text to Zentui's owned Working line through Pi's shared event bus. Protocol version 1 uses keyed segments that are sanitized, ordered by key, width-bounded, and included in the same Classic/KITT animation frames as Zentui's built-in content.
474
+
475
+ Probe the capability when an interaction starts so an extension can fall back to Pi's public `setWorkingMessage()` slot when Zentui's Working line is unavailable:
476
+
477
+ ```typescript
478
+ const capability = { supported: false, active: false };
479
+ pi.events.emit("zentui:working-line-segment-capability", capability);
480
+
481
+ if (capability.active) {
482
+ pi.events.emit("zentui:working-line-segment", {
483
+ key: "@scope/my-extension:throughput",
484
+ text: "24.3 tok/s · TTFT 820ms",
485
+ });
486
+ }
487
+ ```
488
+
489
+ Update a segment by emitting the same key with new text. Remove it when the interaction settles or the publishing extension shuts down:
490
+
491
+ ```typescript
492
+ pi.events.emit("zentui:working-line-segment", {
493
+ key: "@scope/my-extension:throughput",
494
+ text: undefined,
495
+ });
496
+ ```
497
+
498
+ All publishers share one global key namespace. Collisions are last-update-wins, and removal by either publisher removes the value for that key. Publishers must therefore use stable, package-qualified keys such as `@scope/package:segment`; each publisher owns removal and lifecycle cleanup for its keys. `text: ""` also removes a segment. Published state is scoped to the current session and Working-row ownership: Zentui discards it on a new session, disable, shutdown, or ownership release. Positive updates while capability is inactive are ignored rather than retained, so publishers must probe again and republish their current value after capability becomes active.
499
+
500
+ Zentui accepts at most 16 unique keys, keys up to 64 code units, and values up to 256 code units. Extra segments are omitted or truncated when the complete row reaches its fixed width. `supported` reports whether this Zentui version understands the protocol. Zentui also adds `version: 1` to the mutable capability response; probes that initialize only `supported` and `active`, as above, remain compatible. `active` additionally requires an enabled Working line in an active TUI session where Zentui successfully installed and still claims both required Pi working-row surfaces. Pi's unkeyed, last-writer-wins APIs provide no way to prove that another extension has not overwritten a surface after installation, so publishers should probe at each interaction and retain their normal fallback.
432
501
 
433
502
  ## Git status icons
434
503
 
@@ -24,6 +24,7 @@ import {
24
24
  type ResolvedIcons,
25
25
  resolveConfiguredIcons,
26
26
  } from "./icons";
27
+ import type { ComponentPreset } from "./presets";
27
28
  import { isSupportedColorSpec } from "./style";
28
29
  import { normalizeWorkingLineMessages } from "./working-line";
29
30
  import { PI_WORKING_LINE_MESSAGES } from "./working-line-messages";
@@ -1651,6 +1652,16 @@ function deleteLegacyMessageCopyFriendly(record: ConfigRecord): void {
1651
1652
  if (isRecord(framed)) delete framed.copyFriendly;
1652
1653
  }
1653
1654
 
1655
+ /** A preset is a one-time, selection-only transaction; do not normalize unrelated leaves. */
1656
+ export function saveComponentPreset(preset: ComponentPreset, path = configPath): PolishedTuiConfig {
1657
+ return mutateConfig(path, (record) => {
1658
+ record.components = overlayKnown(record.components, preset.components);
1659
+ deleteLegacyEditorCopyFriendly(record);
1660
+ deleteLegacyFooterEnabled(record);
1661
+ if (preset.components.userMessages.style !== undefined) deleteLegacyMessageCopyFriendly(record);
1662
+ });
1663
+ }
1664
+
1654
1665
  function applyEditorComponentPatch(
1655
1666
  component: EditorComponentConfig,
1656
1667
  patch: Partial<
@@ -19,6 +19,20 @@ export type EditorTransferResult =
19
19
  | { ok: true }
20
20
  | { ok: false; reason: EditorTransferFailureReason };
21
21
 
22
+ /**
23
+ * Pi custom UI saves raw editor text and restores it with setText(), which clears
24
+ * collapsed paste storage. Expand nonempty drafts before that snapshot is taken.
25
+ * Hosts without public text APIs retain their existing custom-UI behavior.
26
+ */
27
+ export function prepareEditorTextForCustomUi(
28
+ ui: Pick<EditorTransferUi, "getEditorText" | "setEditorText">,
29
+ ): void {
30
+ if (typeof ui.getEditorText !== "function" || typeof ui.setEditorText !== "function") return;
31
+ const text = ui.getEditorText();
32
+ if (typeof text !== "string") throw new Error("expanded editor text could not be read safely");
33
+ if (text) ui.setEditorText(text);
34
+ }
35
+
22
36
  /**
23
37
  * Prepare the active editor's public expanded text before Pi replaces its factory.
24
38
  * This intentionally transfers prompt contents only; editor-private state is never inspected.
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { stripVTControlCharacters } from "node:util";
2
3
  import type { ExtensionStatusColorMode, ExtensionStatusPlacement, ZentuiConfig } from "./config";
3
4
  import {
@@ -19,9 +20,6 @@ export type ExtensionStatusSegmentsByPlacement = {
19
20
  right: ExtensionStatusSegment[];
20
21
  };
21
22
 
22
- const safeSgrPattern = /\x1b\[[0-9;:]*m/g;
23
- const sgrPlaceholderPattern = /__ZENTUI_SGR_(\d+)__/g;
24
-
25
23
  function compareKeys(a: ExtensionStatusSegment, b: ExtensionStatusSegment): number {
26
24
  return a.key < b.key ? -1 : a.key > b.key ? 1 : 0;
27
25
  }
@@ -43,18 +41,36 @@ function hasVisibleStatusText(value: string): boolean {
43
41
  }
44
42
 
45
43
  export function sanitizeExtensionStatusOriginalText(value: string): string {
46
- const safeSequences: string[] = [];
47
- const protectedValue = value.replace(safeSgrPattern, (sequence) => {
48
- const index = safeSequences.push(sequence) - 1;
49
- return `__ZENTUI_SGR_${index}__`;
50
- });
44
+ // Preserve only SGR and HTTP(S) OSC 8 links. Never pass title/clipboard/cursor controls.
45
+ const marker = `__ZENTUI_${randomUUID()}_`;
46
+ const sequences: Array<{ sequence: string; url: string | undefined }> = [];
47
+ const protectedValue = value.replace(
48
+ /\x1b\[[0-9;:]*m|\x1b\]8;[^;\x07\x1b]*;([^\x07\x1b]*)(?:\x07|\x1b\\)/g,
49
+ (sequence, url: string | undefined) => `${marker}${sequences.push({ sequence, url }) - 1}__`,
50
+ );
51
51
  const cleaned = normalizeStatusWhitespace(stripVTControlCharacters(protectedValue));
52
- const restored = cleaned.replace(sgrPlaceholderPattern, (_match, indexText: string) => {
53
- const index = Number.parseInt(indexText, 10);
54
- return safeSequences[index] ?? "";
52
+ // Other controls can swallow placeholders. Track only sequences that survive
53
+ // stripping, including invalid targets that must end any surviving link.
54
+ let activeLink = false;
55
+ const restored = cleaned.replace(new RegExp(`${marker}(\\d+)__`, "g"), (_match, index) => {
56
+ const entry = sequences[Number(index)];
57
+ if (!entry) return "";
58
+ const { sequence, url } = entry;
59
+ if (url === undefined) return sequence;
60
+ const close = activeLink ? "\x1b]8;;\x07" : "";
61
+ activeLink = false;
62
+ if (!url || /[\s\x00-\x1f\x7f-\x9f]/.test(url)) return close;
63
+ try {
64
+ const parsed = new URL(url);
65
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return close;
66
+ activeLink = true;
67
+ return `${close}\x1b]8;;${parsed.href}\x07`;
68
+ } catch {
69
+ return close;
70
+ }
55
71
  });
56
-
57
- return hasVisibleStatusText(restored) ? restored : "";
72
+ const result = restored + (activeLink ? "\x1b]8;;\x07" : "");
73
+ return hasVisibleStatusText(result) ? result : "";
58
74
  }
59
75
 
60
76
  export function collectExtensionStatusSegments(
@@ -1,6 +1,7 @@
1
- import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
1
+ import { visibleWidth } from "@earendil-works/pi-tui";
2
2
  import type { CompactFooterMaxLines } from "./config";
3
3
  import type { CompactBoundaryKind } from "./footer-format";
4
+ import { truncateFooterText } from "./footer-text";
4
5
 
5
6
  export type FooterZones = {
6
7
  left: string;
@@ -59,7 +60,7 @@ function fitChunk(chunk: string, innerWidth: number): PackedRow {
59
60
  return { text: chunk, endsWithRendererEllipsis: false };
60
61
  }
61
62
  return {
62
- text: truncateToWidth(chunk, innerWidth, "…"),
63
+ text: truncateFooterText(chunk, innerWidth, "…"),
63
64
  endsWithRendererEllipsis: true,
64
65
  };
65
66
  }
@@ -68,7 +69,7 @@ function appendOmissionMarker(row: PackedRow, innerWidth: number): PackedRow {
68
69
  if (row.endsWithRendererEllipsis) return row;
69
70
  if (innerWidth <= 1) return { text: "…", endsWithRendererEllipsis: true };
70
71
  return {
71
- text: `${truncateToWidth(row.text, innerWidth - 1, "")}…`,
72
+ text: `${truncateFooterText(row.text, innerWidth - 1, "")}…`,
72
73
  endsWithRendererEllipsis: true,
73
74
  };
74
75
  }
@@ -118,5 +119,5 @@ export function packCompactChunks(
118
119
  }
119
120
 
120
121
  if (current) rows.push(omitted ? appendOmissionMarker(current, innerWidth) : current);
121
- return rows.map((row) => truncateToWidth(row.text, innerWidth, ""));
122
+ return rows.map((row) => truncateFooterText(row.text, innerWidth, ""));
122
123
  }
@@ -0,0 +1,11 @@
1
+ import { truncateToWidth } from "@earendil-works/pi-tui";
2
+
3
+ /** Close retained links before joining/padding footer fragments, even on Pi < 0.84.0. */
4
+ export function truncateFooterText(text: string, width: number, ellipsis: string): string {
5
+ const truncated = truncateToWidth(text, width, ellipsis);
6
+ let activeLink = false;
7
+ for (const match of truncated.matchAll(/\x1b\]8;[^;\x07\x1b]*;([^\x07\x1b]*)(?:\x07|\x1b\\)/g)) {
8
+ activeLink = Boolean(match[1]);
9
+ }
10
+ return truncated + (activeLink ? "\x1b]8;;\x07" : "");
11
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2
+ import { visibleWidth } from "@earendil-works/pi-tui";
3
3
  import type { SeparatorStyle, ZentuiConfig } from "./config";
4
4
  import { FOOTER_FORMAT_ALIASES } from "./config";
5
5
  import { sanitizeEditorMetadataText } from "./editor-metadata-format";
@@ -22,6 +22,7 @@ import {
22
22
  packCompactChunks,
23
23
  reflowFullFooter,
24
24
  } from "./footer-layout";
25
+ import { truncateFooterText } from "./footer-text";
25
26
  import {
26
27
  buildContextDisplayLabel,
27
28
  buildSessionDurationLabel,
@@ -77,7 +78,7 @@ function fitStatusTexts(statusTexts: string[], maxWidth: number, separator: stri
77
78
  }
78
79
 
79
80
  if (fitted.length === 0) {
80
- return maxWidth > 1 ? truncateToWidth(text, maxWidth, "…") : "";
81
+ return maxWidth > 1 ? truncateFooterText(text, maxWidth, "…") : "";
81
82
  }
82
83
  break;
83
84
  }
@@ -101,10 +102,10 @@ function composeBuiltInFooterContent(left: string, right: string, innerWidth: nu
101
102
  const leftWidth = visibleWidth(left);
102
103
  const rightWidth = visibleWidth(right);
103
104
  return leftWidth >= innerWidth
104
- ? truncateToWidth(left, innerWidth, "")
105
+ ? truncateFooterText(left, innerWidth, "")
105
106
  : leftWidth + 1 + rightWidth <= innerWidth
106
107
  ? `${left}${" ".repeat(innerWidth - leftWidth - rightWidth)}${right}`
107
- : truncateToWidth(left, innerWidth, "");
108
+ : truncateFooterText(left, innerWidth, "");
108
109
  }
109
110
 
110
111
  function composeFooterContent(
@@ -700,8 +701,8 @@ export function installFooter(
700
701
  );
701
702
  const frameRows = (rows: string[]) =>
702
703
  rows.map((row) => {
703
- const framed = width > 2 ? ` ${truncateToWidth(row, width - 2, "")} ` : row;
704
- return truncateToWidth(framed, width, "");
704
+ const framed = width > 2 ? ` ${truncateFooterText(row, width - 2, "")} ` : row;
705
+ return truncateFooterText(framed, width, "");
705
706
  });
706
707
 
707
708
  if (!config.components.footer.styles.starship.responsive)
@@ -732,8 +733,8 @@ export function installFooter(
732
733
  if (reflowed) return frameRows(reflowed);
733
734
 
734
735
  const chunkBudget = compactChunkBudget(innerWidth);
735
- const compactCwdLabel = truncateToWidth(cwdLabel, chunkBudget, "…");
736
- const compactSessionNameLabel = truncateToWidth(
736
+ const compactCwdLabel = truncateFooterText(cwdLabel, chunkBudget, "…");
737
+ const compactSessionNameLabel = truncateFooterText(
737
738
  sessionNameLabel,
738
739
  Math.max(1, chunkBudget - visibleWidth("in ")),
739
740
  "…",
@@ -742,7 +743,7 @@ export function installFooter(
742
743
  1,
743
744
  chunkBudget - visibleWidth("on ") - (statusBlock ? visibleWidth(statusBlock) + 1 : 0),
744
745
  );
745
- const compactBranchLabel = truncateToWidth(
746
+ const compactBranchLabel = truncateFooterText(
746
747
  renderVariable("git_branch"),
747
748
  compactBranchBudget,
748
749
  "…",
@@ -784,7 +785,7 @@ export function installFooter(
784
785
  );
785
786
  const references = collectFooterFormatReferences(chunk.tokens, FOOTER_FORMAT_ALIASES);
786
787
  if (["cwd", "session_name", "git_branch"].some((name) => references.has(name))) {
787
- rendered = truncateToWidth(rendered, chunkBudget, "…");
788
+ rendered = truncateFooterText(rendered, chunkBudget, "…");
788
789
  }
789
790
  if (rendered) compactChunks.push({ text: rendered, boundary: chunk.boundary });
790
791
  }
@@ -36,6 +36,7 @@ import {
36
36
  type SelectorBordersComponentConfig,
37
37
  type SeparatorStyle,
38
38
  saveAccentRailEditorStylePatch,
39
+ saveComponentPreset,
39
40
  saveEditorComponentPatch,
40
41
  saveExtensionStatusColorMode,
41
42
  saveExtensionStatusDefaultPlacement,
@@ -74,6 +75,7 @@ import {
74
75
  } from "./interaction-summary";
75
76
  import { LiveContextController } from "./live-context";
76
77
  import { readPackageVersionResult } from "./package-version";
78
+ import { getComponentPreset } from "./presets";
77
79
  import {
78
80
  createProjectRefreshScheduler,
79
81
  type ProjectRefreshRun,
@@ -97,6 +99,7 @@ import {
97
99
  snapshotWorkingLineHighStyle,
98
100
  WorkingLineController,
99
101
  } from "./working-line";
102
+ import { WorkingLineExtensionSegments } from "./working-line-extension-segments";
100
103
 
101
104
  const ZENTUI_EDITOR_FACTORY = Symbol.for("pi-zentui.editor-factory");
102
105
  const ZENTUI_EDITOR_BASE_FACTORY = Symbol.for("pi-zentui.editor-base-factory");
@@ -293,6 +296,7 @@ export default function (pi: ExtensionAPI) {
293
296
  const liveContext = new LiveContextController(sessionLifecycle, refresh);
294
297
  const getActiveTheme = () => activeTheme;
295
298
  const getCurrentConfig = () => currentConfig;
299
+ let invalidateWorkingLinePublisherState = () => {};
296
300
  const workingLine = new WorkingLineController(
297
301
  getCurrentConfig,
298
302
  () => activeTheme as Theme,
@@ -300,7 +304,29 @@ export default function (pi: ExtensionAPI) {
300
304
  Math.random,
301
305
  Date.now,
302
306
  () => interactionMetrics.currentThought(),
307
+ () => invalidateWorkingLinePublisherState(),
303
308
  );
309
+ let workingLineSessionReady = false;
310
+ const workingLineExtensions = new WorkingLineExtensionSegments(
311
+ pi.events,
312
+ () =>
313
+ workingLineSessionReady &&
314
+ sessionLifecycle.isCurrent() &&
315
+ currentConfig.components.workingLine.enabled &&
316
+ activeTuiContext !== undefined &&
317
+ workingLine.isAvailable(),
318
+ (segments) => {
319
+ if (activeTuiContext && sessionLifecycle.isCurrent()) {
320
+ const applied = workingLine.updateExtensionSegments(segments, activeTuiContext);
321
+ if (!applied && !workingLine.isAvailable()) {
322
+ workingLine.invalidateExtensionSegments();
323
+ }
324
+ return applied;
325
+ }
326
+ return false;
327
+ },
328
+ );
329
+ invalidateWorkingLinePublisherState = () => workingLineExtensions.invalidate();
304
330
  const getContextSnapshot = (ctx: ExtensionContext) => resolveContextUsage(ctx, liveContext.get());
305
331
  const getContextWindow = (ctx: ExtensionContext): number | undefined =>
306
332
  getContextSnapshot(ctx).contextWindow;
@@ -995,7 +1021,11 @@ export default function (pi: ExtensionAPI) {
995
1021
  !options.allowStaleZentui
996
1022
  ) {
997
1023
  clearEditorOwnership();
998
- return;
1024
+ return {
1025
+ ok: false,
1026
+ reason:
1027
+ "another Zentui instance is currently managing the editor; reload Pi to apply this change",
1028
+ };
999
1029
  }
1000
1030
  const editorMissingOrReplaced = !editorInstalled || !isOwnedEditorFactory(currentFactory);
1001
1031
  if (editorMissingOrReplaced) return installEditor(ctx);
@@ -1147,6 +1177,12 @@ export default function (pi: ExtensionAPI) {
1147
1177
 
1148
1178
  pi.on("session_start", async (_event, ctx) => {
1149
1179
  const lifecycleGeneration = sessionLifecycle.start();
1180
+ // A new generation must not expose or route extension segments through the previous
1181
+ // session while asynchronous TUI startup is still pending.
1182
+ workingLineSessionReady = false;
1183
+ workingLineExtensions.invalidate();
1184
+ if (activeTuiContext) workingLine.dispose(activeTuiContext);
1185
+ else workingLine.invalidateExtensionSegments();
1150
1186
  // Reload synchronously so private ownership uses this session's disk snapshot before
1151
1187
  // any await or transcript restoration.
1152
1188
  currentConfig = loadConfig();
@@ -1173,6 +1209,7 @@ export default function (pi: ExtensionAPI) {
1173
1209
  if (!sessionLifecycle.isCurrent(lifecycleGeneration)) return;
1174
1210
  liveContext.clear();
1175
1211
  interactionMetrics.shutdown();
1212
+ workingLineExtensions.invalidate();
1176
1213
  state.sessionStartEpoch = Date.now();
1177
1214
  invalidateUsageTotalsCache();
1178
1215
  resetAgentTimer();
@@ -1181,16 +1218,50 @@ export default function (pi: ExtensionAPI) {
1181
1218
  repositoryRoots.reset();
1182
1219
  installUi(ctx);
1183
1220
  workingLine.startSession(ctx);
1221
+ workingLineSessionReady = true;
1184
1222
  scheduleEditorReconciliation(ctx);
1185
1223
  });
1186
1224
 
1187
1225
  registerZentuiSettingsCommand(pi, {
1188
1226
  sessionLifecycle,
1189
1227
  getConfig: getCurrentConfig,
1190
- setEditorComponent(patch: Partial<EditorComponentConfig>, ctx: ExtensionContext) {
1228
+ applyPreset(id, ctx, options) {
1229
+ const preset = getComponentPreset(id);
1230
+ if (!preset) throw new Error(`Unknown Zentui preset: ${id}`);
1231
+ const previousFooterStyle = effectiveFooterStyle();
1232
+ currentConfig = saveComponentPreset(preset);
1233
+ if (!isTuiContext(ctx)) return { applied: true };
1234
+ activeTheme = ctx.ui.theme;
1235
+ const result = options?.deferEditor ? undefined : reconcileEditor(ctx);
1236
+ if (currentConfig.components.editor.style !== "minimalist") {
1237
+ setMinimalistDecorationActive(false);
1238
+ }
1239
+ reconcileUserMessages();
1240
+ reconcileFooter(ctx);
1241
+ reconcileProjectRefresh(ctx, effectiveFooterStyle() !== previousFooterStyle);
1242
+ reconcileSessionTimer();
1243
+ reconcileAgentTimer();
1244
+ refresh();
1245
+ return {
1246
+ applied: !result || result.ok,
1247
+ reason: result && !result.ok ? result.reason : undefined,
1248
+ };
1249
+ },
1250
+ reconcilePresetEditor(ctx) {
1251
+ if (!isTuiContext(ctx) || !sessionLifecycle.isCurrent()) return { applied: true };
1252
+ const result = reconcileEditor(ctx);
1253
+ reconcileProjectRefresh(ctx);
1254
+ reconcileAgentTimer();
1255
+ refresh();
1256
+ return {
1257
+ applied: !result || result.ok,
1258
+ reason: result && !result.ok ? result.reason : undefined,
1259
+ };
1260
+ },
1261
+ setEditorComponent(patch: Partial<EditorComponentConfig>, ctx: ExtensionContext, options) {
1191
1262
  currentConfig = saveEditorComponentPatch(patch);
1192
1263
  let result: EditorChangeResult | undefined;
1193
- if (patch.enabled !== undefined && isTuiContext(ctx)) {
1264
+ if (patch.enabled !== undefined && isTuiContext(ctx) && !options?.deferEditor) {
1194
1265
  result = reconcileEditor(ctx);
1195
1266
  }
1196
1267
  if (patch.style !== undefined && patch.style !== "minimalist") {
@@ -1241,6 +1312,7 @@ export default function (pi: ExtensionAPI) {
1241
1312
  },
1242
1313
  setWorkingLineComponent(patch: WorkingLineComponentPatch, ctx: ExtensionContext) {
1243
1314
  currentConfig = saveWorkingLineComponentPatch(patch);
1315
+ if (patch.enabled === false) workingLineExtensions.clear();
1244
1316
  return workingLine.reconcile(ctx);
1245
1317
  },
1246
1318
  setSelectorBordersComponent(
@@ -1326,10 +1398,12 @@ export default function (pi: ExtensionAPI) {
1326
1398
  });
1327
1399
 
1328
1400
  pi.on("session_shutdown", async (_event, ctx) => {
1401
+ workingLineSessionReady = false;
1329
1402
  thinkingExperimental.shutdown();
1330
1403
  liveContext.clear();
1331
1404
  interactionMetrics.shutdown();
1332
1405
  workingLine.dispose(ctx);
1406
+ workingLineExtensions.invalidate();
1333
1407
  cleanupUi(ctx);
1334
1408
  });
1335
1409