pi-ui-extend 1.0.42 → 1.0.46

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 (233) hide show
  1. package/README.md +41 -12
  2. package/dist/app/app.d.ts +10 -0
  3. package/dist/app/app.js +132 -8
  4. package/dist/app/cli/install.js +2 -2
  5. package/dist/app/commands/command-controller.d.ts +4 -0
  6. package/dist/app/commands/command-controller.js +11 -0
  7. package/dist/app/commands/command-git-actions.d.ts +25 -0
  8. package/dist/app/commands/command-git-actions.js +381 -0
  9. package/dist/app/commands/command-host.d.ts +3 -0
  10. package/dist/app/commands/command-host.js +5 -2
  11. package/dist/app/commands/command-model-actions.d.ts +1 -0
  12. package/dist/app/commands/command-model-actions.js +51 -34
  13. package/dist/app/commands/command-navigation-actions.d.ts +1 -0
  14. package/dist/app/commands/command-navigation-actions.js +16 -3
  15. package/dist/app/commands/command-registry.d.ts +2 -0
  16. package/dist/app/commands/command-registry.js +14 -0
  17. package/dist/app/commands/reload-context-inventory.js +12 -1
  18. package/dist/app/input/autocomplete-controller.js +61 -34
  19. package/dist/app/input/input-action-controller.d.ts +2 -0
  20. package/dist/app/input/input-action-controller.js +8 -1
  21. package/dist/app/input/input-controller.d.ts +2 -1
  22. package/dist/app/input/input-controller.js +7 -2
  23. package/dist/app/input/prompt-enhancer-controller.js +53 -35
  24. package/dist/app/input/voice-controller.d.ts +41 -45
  25. package/dist/app/input/voice-controller.js +351 -419
  26. package/dist/app/model/model-usage-controller.js +17 -7
  27. package/dist/app/model/model-usage-status.d.ts +4 -1
  28. package/dist/app/model/model-usage-status.js +270 -46
  29. package/dist/app/popup/menu-items-controller.d.ts +13 -3
  30. package/dist/app/popup/menu-items-controller.js +37 -21
  31. package/dist/app/popup/popup-action-controller.d.ts +12 -2
  32. package/dist/app/popup/popup-action-controller.js +77 -24
  33. package/dist/app/popup/popup-menu-controller.d.ts +36 -14
  34. package/dist/app/popup/popup-menu-controller.js +239 -69
  35. package/dist/app/rendering/dcp-stats.d.ts +9 -4
  36. package/dist/app/rendering/dcp-stats.js +40 -425
  37. package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
  38. package/dist/app/rendering/popup-menu-renderer.js +42 -37
  39. package/dist/app/rendering/render-controller.js +23 -2
  40. package/dist/app/rendering/status-line-renderer.d.ts +5 -0
  41. package/dist/app/rendering/status-line-renderer.js +41 -5
  42. package/dist/app/rendering/tab-line-renderer.js +26 -20
  43. package/dist/app/runtime.d.ts +12 -1
  44. package/dist/app/runtime.js +120 -12
  45. package/dist/app/screen/mouse-controller.d.ts +2 -0
  46. package/dist/app/screen/mouse-controller.js +17 -7
  47. package/dist/app/screen/status-controller.d.ts +4 -0
  48. package/dist/app/screen/status-controller.js +5 -0
  49. package/dist/app/session/lazy-session-manager.js +34 -0
  50. package/dist/app/session/session-event-controller.d.ts +1 -0
  51. package/dist/app/session/session-event-controller.js +10 -1
  52. package/dist/app/session/session-history.d.ts +1 -0
  53. package/dist/app/session/session-history.js +12 -1
  54. package/dist/app/session/session-lifecycle-controller.d.ts +6 -1
  55. package/dist/app/session/session-lifecycle-controller.js +10 -1
  56. package/dist/app/session/tabs-controller.d.ts +25 -1
  57. package/dist/app/session/tabs-controller.js +442 -153
  58. package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
  59. package/dist/app/subagents/subagents-widget-controller.js +6 -0
  60. package/dist/app/types.d.ts +11 -1
  61. package/dist/app/workspace/workspace-actions-controller.js +10 -2
  62. package/dist/app/workspace/workspace-undo.d.ts +1 -0
  63. package/dist/app/workspace/workspace-undo.js +1 -0
  64. package/dist/bundled-extensions/question/index.js +9 -1
  65. package/dist/bundled-extensions/question/remote.d.ts +4 -0
  66. package/dist/bundled-extensions/question/remote.js +33 -0
  67. package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
  68. package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
  69. package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
  70. package/dist/bundled-extensions/telegram-connector/config.js +87 -0
  71. package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
  72. package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
  73. package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
  74. package/dist/bundled-extensions/telegram-connector/index.js +195 -0
  75. package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
  76. package/dist/bundled-extensions/terminal-bell/index.js +0 -76
  77. package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
  78. package/dist/bundled-extensions/workspace-undo/index.js +191 -0
  79. package/dist/config.d.ts +21 -2
  80. package/dist/config.js +159 -32
  81. package/dist/default-pix-config.js +20 -10
  82. package/dist/schemas/pi-tools-suite-schema.d.ts +8 -4
  83. package/dist/schemas/pi-tools-suite-schema.js +11 -5
  84. package/dist/schemas/pix-schema.d.ts +12 -2
  85. package/dist/schemas/pix-schema.js +37 -9
  86. package/dist/schemas/tasks-schema.js +1 -1
  87. package/docs/concurrency.md +3 -1
  88. package/docs/desktop-mvp.md +10 -5
  89. package/docs/desktop-task-manager.md +81 -28
  90. package/docs/release.md +30 -6
  91. package/external/pi-tools-suite/README.md +165 -39
  92. package/external/pi-tools-suite/docs/session-recovery.md +7 -0
  93. package/external/pi-tools-suite/docs/subagent-model-pools.md +12 -5
  94. package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
  95. package/external/pi-tools-suite/package.json +6 -1
  96. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
  97. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
  98. package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
  99. package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
  100. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
  101. package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
  102. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
  103. package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
  104. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
  105. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
  106. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
  107. package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
  108. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
  109. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
  110. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
  111. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
  112. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
  113. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
  114. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
  115. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
  116. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
  117. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
  118. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
  119. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
  120. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
  121. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
  122. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
  123. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
  124. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
  125. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
  126. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
  127. package/external/pi-tools-suite/src/async-subagents/commands.ts +1 -1
  128. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
  129. package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +0 -2
  130. package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
  131. package/external/pi-tools-suite/src/async-subagents/core/config.ts +40 -17
  132. package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
  133. package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
  134. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +7 -4
  135. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +34 -24
  136. package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
  137. package/external/pi-tools-suite/src/async-subagents/lib.ts +9 -1
  138. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +0 -1
  139. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +2 -2
  140. package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
  141. package/external/pi-tools-suite/src/config.ts +14 -0
  142. package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
  143. package/external/pi-tools-suite/src/context-gateway/config.ts +66 -1
  144. package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
  145. package/external/pi-tools-suite/src/context-gateway/enforcement.ts +123 -3
  146. package/external/pi-tools-suite/src/context-gateway/index.ts +137 -4
  147. package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +5 -5
  148. package/external/pi-tools-suite/src/context-gateway/telemetry.ts +2 -2
  149. package/external/pi-tools-suite/src/context-gateway/types.ts +8 -1
  150. package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
  151. package/external/pi-tools-suite/src/dcp/auto-compress.ts +41 -14
  152. package/external/pi-tools-suite/src/dcp/commands.ts +14 -156
  153. package/external/pi-tools-suite/src/dcp/compress-tool.ts +8 -2
  154. package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
  155. package/external/pi-tools-suite/src/dcp/config.ts +58 -4
  156. package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
  157. package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
  158. package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
  159. package/external/pi-tools-suite/src/dcp/index.ts +338 -66
  160. package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
  161. package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
  162. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +92 -2
  163. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
  164. package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
  165. package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
  166. package/external/pi-tools-suite/src/dcp/state.ts +9 -0
  167. package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
  168. package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
  169. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +68 -16
  170. package/external/pi-tools-suite/src/lib/project.ts +36 -1
  171. package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
  172. package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
  173. package/external/pi-tools-suite/src/resource-registry/index.ts +282 -21
  174. package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
  175. package/external/pi-tools-suite/src/todo/index.ts +21 -0
  176. package/external/pi-tools-suite/src/todo/todo.ts +19 -1
  177. package/external/pi-tools-suite/src/tool-descriptions.ts +44 -15
  178. package/package.json +9 -10
  179. package/schemas/pi-tools-suite.json +44 -12
  180. package/schemas/pix.json +109 -15
  181. package/schemas/tasks.json +1 -2
  182. package/skills/simplify/SKILL.md +33 -5
  183. package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -196
  184. package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
  185. package/skills/playwright-cli/SKILL.md +0 -420
  186. package/skills/playwright-cli/references/element-attributes.md +0 -23
  187. package/skills/playwright-cli/references/playwright-tests.md +0 -50
  188. package/skills/playwright-cli/references/request-mocking.md +0 -87
  189. package/skills/playwright-cli/references/running-code.md +0 -241
  190. package/skills/playwright-cli/references/session-management.md +0 -273
  191. package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
  192. package/skills/playwright-cli/references/storage-state.md +0 -290
  193. package/skills/playwright-cli/references/test-generation.md +0 -142
  194. package/skills/playwright-cli/references/tracing.md +0 -154
  195. package/skills/playwright-cli/references/video-recording.md +0 -147
  196. package/skills/spec-wiki/SKILL.md +0 -55
  197. package/skills/spec-wiki/evals/evals.json +0 -132
  198. package/skills/spec-wiki/evals/files/bootstrap/architecture/rfc-007.md +0 -14
  199. package/skills/spec-wiki/evals/files/bootstrap/docs/how-to-pay.md +0 -4
  200. package/skills/spec-wiki/evals/files/bootstrap/docs/overview.md +0 -9
  201. package/skills/spec-wiki/evals/files/bootstrap/requirements/payment-behavior.md +0 -17
  202. package/skills/spec-wiki/evals/files/bootstrap/src/payments.ts +0 -12
  203. package/skills/spec-wiki/evals/files/bootstrap/tests/payments.test.ts +0 -11
  204. package/skills/spec-wiki/evals/files/focused/src/session.ts +0 -5
  205. package/skills/spec-wiki/evals/files/focused/tests/session.test.ts +0 -17
  206. package/skills/spec-wiki/evals/files/maintenance/low-signal/incoming/capture.txt +0 -2
  207. package/skills/spec-wiki/evals/files/maintenance/low-signal/src/capture.ts +0 -3
  208. package/skills/spec-wiki/evals/files/maintenance/move/docs/old-job-contract.md +0 -18
  209. package/skills/spec-wiki/evals/files/maintenance/move/src/job.ts +0 -5
  210. package/skills/spec-wiki/evals/files/maintenance/new-relation/docs/session-refresh.md +0 -24
  211. package/skills/spec-wiki/evals/files/maintenance/new-relation/incoming/refresh-worker.ts +0 -7
  212. package/skills/spec-wiki/evals/files/maintenance/new-relation/src/session.ts +0 -7
  213. package/skills/spec-wiki/evals/files/maintenance/no-impact/docs/auth-contract.md +0 -17
  214. package/skills/spec-wiki/evals/files/maintenance/no-impact/incoming/telemetry-format.ts +0 -3
  215. package/skills/spec-wiki/evals/files/maintenance/no-impact/src/auth.ts +0 -3
  216. package/skills/spec-wiki/evals/files/query/docs/how-to-run-jobs.md +0 -4
  217. package/skills/spec-wiki/evals/files/query/docs/spec-overview.md +0 -9
  218. package/skills/spec-wiki/evals/files/query/features/jobs/cancellation-contract.md +0 -15
  219. package/skills/spec-wiki/evals/files/query/features/jobs/retry-spec.md +0 -6
  220. package/skills/spec-wiki/evals/maintenance_regression.py +0 -311
  221. package/skills/spec-wiki/evals/retrieval-eval.md +0 -37
  222. package/skills/spec-wiki/evals/retrieval-evals.json +0 -37
  223. package/skills/spec-wiki/evals/trigger-evals.json +0 -94
  224. package/skills/spec-wiki/references/bootstrap.md +0 -100
  225. package/skills/spec-wiki/references/focused-spec.md +0 -117
  226. package/skills/spec-wiki/references/index-schema.md +0 -330
  227. package/skills/spec-wiki/references/maintenance.md +0 -134
  228. package/skills/spec-wiki/references/query.md +0 -74
  229. package/skills/spec-wiki/scripts/spec_wiki.py +0 -2076
  230. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
  231. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
  232. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
  233. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.mjs +0 -0
package/README.md CHANGED
@@ -34,7 +34,7 @@ Pi provides the agent runtime, models, tools, skills, extensions, and persistent
34
34
  - **Keep projects organized.** Tabs are scoped to the working directory and survive restarts. Search, resume, fork, clone, jump through, import, export, share, or delete sessions without leaving the terminal.
35
35
  - **Stay in flow.** Run quick commands with `!`, open a raw interactive terminal with `!!`, paste images, follow file links, dictate in English or Russian, and improve a prompt before sending it.
36
36
  - **Know what the agent is doing.** The status area exposes model, thinking level, context, usage, workspace, todos, sub-agents, voice state, and prompt actions — with mouse targets where useful.
37
- - **Bring a serious toolkit.** Pix ships with `pi-tools-suite`: 18 integrated modules for indexed repository discovery, AST edits, LSP diagnostics, parallel agents, durable todos, context compression, web access, provider accounts, and more.
37
+ - **Bring a serious toolkit.** Pix ships with `pi-tools-suite`: 21 integrated modules for indexed repository discovery, AST edits, LSP diagnostics, parallel agents, durable todos, context compression, web access, provider accounts, and more.
38
38
  - **Use the models you want.** Pix runs on Pi's provider ecosystem and supports model switching, scoped model lists, per-model thinking levels, usage views, autocomplete, and fallback-aware helper workflows.
39
39
 
40
40
  Pix is not a separate agent protocol or an RPC wrapper around Pi. It runs on the Pi SDK, so the runtime, session format, extensions, skills, prompts, and tools remain part of the same ecosystem.
@@ -97,6 +97,8 @@ On startup, Pix tries to link the bundled suite into the active Pi agent directo
97
97
  | Providers and quotas | `antigravity-auth`, `opencode-import`, `usage` | Antigravity OAuth/account failover, OpenCode credential import, and multi-provider usage views. |
98
98
  | Reusable workflows | `prompt-commands`, `resource-registry`, `session-name` | Prompt-command CRUD, private Git-backed skill/agent install/update/push/remote-remove/local-uninstall with reload-on-change, project-scoped `.pi/tasks.jsonc` + `.pi/plans/` + `.pi/TODO.md` sync, a Desktop Registry sidebar for status/actions/configuration, and session naming. |
99
99
  | Model compatibility | `coding-discipline`, `model-tools`, `codex-reasoning-fix` | Model-specific discipline and vision lookup, compatibility aliases, and a Codex reasoning payload workaround. |
100
+ | Agent session context | `session-recovery` | Bounded session overview/search/section tools so agents can recover prior context. |
101
+ | Payload hygiene | `context-gateway`, `truncation-metadata-normalizer`, `credential-firewall` | Observe-only result-chain gateway, opt-in truncation-metadata cleanup, and an opt-in secret firewall. |
100
102
 
101
103
  Every module can be disabled. Optional integrations only activate when their requirements are available — for example, repository tools require an index, web tools require local Ollama web search, and LSP servers must be configured and trusted.
102
104
 
@@ -136,8 +138,8 @@ Recommended:
136
138
 
137
139
  Optional:
138
140
 
139
- - the `vosk` optional dependency for local voice input
140
- - an audio recorder for dictation: SoX (`rec`/`sox`), `ffmpeg`, or `arecord` on Linux
141
+ - a Deepgram API key in `dictation.apiKey` inside `~/.config/pi/pix.jsonc` for voice input; `DEEPGRAM_API_KEY` remains supported as a fallback
142
+ - an audio recorder for terminal dictation: SoX (`rec`/`sox`), `ffmpeg`, or `arecord` on Linux
141
143
  - `tmux` and `rsvg-convert` only if you want to regenerate README screenshots
142
144
  - Ollama with web search enabled for the bundled `web_search` and `web_fetch` tools
143
145
  - language servers for the LSP module (see [LSP setup](external/pi-tools-suite/README.md#lsp-setup) for install commands and ready-to-paste configs for popular servers, including Svelte, Vue, Go, clangd, Lua, and bash)
@@ -210,7 +212,7 @@ Pix uses Pi's model and authentication stores. Environment-based API keys and cr
210
212
  | Model-backed helpers | No | Review `promptEnhancer`, `autocomplete`, and `sessionTitle` in `pix.jsonc` | Each configured helper model needs credentials for its own provider; it need not use the main session provider. |
211
213
  | Ollama/Tavily web access | No | Use local Ollama without a cloud key, set `OLLAMA_API_KEY`/`TAVILY_API_KEY`, or run `/web-credentials` | Stored keys live in `~/.config/pi/pi-tools-suite-credentials.json` with mode `0600`. |
212
214
  | Context7 documentation skill | No | Export `CONTEXT7_API_KEY` | The skill fails before making a network request when the variable is absent. |
213
- | Telegram terminal bell | No | Configure `terminalBell.telegram` or `PI_TERMINAL_BELL_TELEGRAM_BOT_TOKEN` plus `PI_TERMINAL_BELL_TELEGRAM_CHAT_ID` | Used only for the optional terminal notification integration. |
215
+ | Telegram task connector | No | Configure `telegramConnector.botToken` + `telegramConnector.chatId`, or `PIX_TELEGRAM_BOT_TOKEN` + `PIX_TELEGRAM_CHAT_ID` | Completion/question notifications; reply to continue the exact live session, or use `/new <task>` for a fresh session. |
214
216
 
215
217
  `pix install` reports whether these sources appear configured, but never displays, copies, or overwrites token values.
216
218
 
@@ -267,6 +269,12 @@ Pix can provide inline, model-backed autocomplete with configurable debounce, ti
267
269
  - While it runs, submit editor text to its stdin; use `Ctrl-C` to interrupt.
268
270
  - `!!command` opens a raw interactive terminal for REPLs, TUIs, debuggers, and development servers. Exit it to return to Pix.
269
271
 
272
+ ### Git helpers
273
+
274
+ - `/code-review` reviews staged, unstaged, and untracked Git changes with the configured review model and prints the findings locally in the TUI.
275
+ - `/commit-message` generates a message from staged changes, shows the full message, and asks for confirmation before running `git commit`. If the staged diff changes before confirmation completes, Pix refuses the commit and asks you to regenerate the message.
276
+ - These helpers use the existing `desktop.git.reviewModelRef` / `reviewFallbackModels` and `desktop.git.commitMessageModelRef` / `commitMessageFallbackModels` settings for compatibility. `/commit-message` never stages changes or bypasses Git hooks.
277
+
270
278
  ### Images, clipboard, and files
271
279
 
272
280
  - Paste an image from the clipboard with `Ctrl+V`/`Cmd+V` in supported terminals.
@@ -274,14 +282,25 @@ Pix can provide inline, model-backed autocomplete with configurable debounce, ti
274
282
  - `/copy` copies the last assistant message.
275
283
  - Pix integrates with the native clipboard on macOS and Windows and common Wayland/X11 helpers on Linux.
276
284
 
277
- ### Local voice input
285
+ ### Voice input
286
+
287
+ Put the Deepgram key in the user Pix config:
288
+
289
+ ```jsonc
290
+ {
291
+ "dictation": {
292
+ "apiKey": "your-deepgram-api-key"
293
+ }
294
+ }
295
+ ```
296
+
297
+ The file is `~/.config/pi/pix.jsonc`. Keep the key in this user config rather than a project `.pi/pix.jsonc`; project config is intentionally not allowed to override the secret. `DEEPGRAM_API_KEY` remains supported as a compatibility fallback when `dictation.apiKey` is not set.
278
298
 
279
- Press `Ctrl+G` or click the microphone in the status area to start/stop dictation. Pix supports configurable local Vosk models and ships defaults for:
299
+ Then press `Ctrl+G` or click the microphone in the terminal status area to start/stop dictation. Pix streams microphone audio to Deepgram Nova-3 and inserts finalized text into the editor so you can review it before sending. The default dictation languages are English and Russian and can be changed with the existing language control.
280
300
 
281
- - English: `vosk-model-small-en-us-0.15`
282
- - Russian: `vosk-model-small-ru-0.22`
301
+ Pix Desktop exposes the same voice-input action directly in the message composer. The desktop client uses Nova-3 and respects `dictation.language` plus the selected language's `deepgramLanguage` value from the user Pix config. It requests a short-lived Deepgram token from the local Tauri backend; the permanent API key is read from the user Pix config (or the environment fallback) by Rust and is never exposed to the WebView. The Settings UI also exposes `Dictation · Api Key` as a sensitive field because it is generated from the Pix schema.
283
302
 
284
- The selected model is downloaded on first use. Recognition is local; the resulting text is inserted into the editor so you can review it before sending.
303
+ Voice audio is sent to Deepgram while recording. Pix no longer downloads or installs local speech-recognition models.
285
304
 
286
305
  ### Workspace undo
287
306
 
@@ -312,6 +331,7 @@ Type `/` for the live, searchable list. Extensions can add more commands than th
312
331
  | Models | `/model`, `/default-model`, `/scoped-models`, `/thinking`, `/default-thinking`, `/autocomplete` |
313
332
  | Project context | `/no-context-files`, `/compact` |
314
333
  | Prompt workflow | `/enhance`, `/queue`, `/copy` |
334
+ | Git | `/code-review`, `/commit-message` |
315
335
  | Sessions | `/new`, `/new_tab`, `/resume`, `/name`, `/session`, `/search`, `/history`, `/jump`, `/tree` |
316
336
  | Branching and files | `/fork`, `/clone`, `/delete`, `/export`, `/import`, `/share` |
317
337
  | Status | `/usage` |
@@ -361,6 +381,7 @@ Both support the published schema:
361
381
  "$schema": "https://unpkg.com/pi-ui-extend/schemas/pix.json",
362
382
  "defaultModel": {
363
383
  "modelRef": "openai-codex/gpt-5.6-sol",
384
+ "fallbackModels": [],
364
385
  "thinking": "medium"
365
386
  },
366
387
  "toolRenderer": {
@@ -371,9 +392,10 @@ Both support the published schema:
371
392
  "apply_patch": { "defaultExpanded": true }
372
393
  }
373
394
  },
374
- "promptEnhancer": { "modelRef": "openai-codex/gpt-5.6-luna" },
395
+ "promptEnhancer": { "modelRef": "openai-codex/gpt-5.6-luna", "fallbackModels": [] },
375
396
  "autocomplete": {
376
397
  "modelRef": "zai/glm-5-turbo",
398
+ "fallbackModels": [],
377
399
  "debounceMs": 350,
378
400
  "timeoutMs": 3000
379
401
  },
@@ -387,6 +409,12 @@ Both support the published schema:
387
409
  }
388
410
  ```
389
411
 
412
+ Singular model selectors resolve with an explicit ordered fallback array, even
413
+ when it is empty. Existing configs that omit the array remain compatible and
414
+ normalize to `[]`. Pix uses `fallbackModels` for object selectors; Desktop Git
415
+ uses `reviewFallbackModels` and `commitMessageFallbackModels` beside the
416
+ corresponding primary model refs.
417
+
390
418
  Configurable areas include:
391
419
 
392
420
  - default model and thinking level;
@@ -449,7 +477,7 @@ Install `wl-clipboard` on Wayland or `xclip`/`xsel` on X11, then run `pix instal
449
477
 
450
478
  ### Voice input is unavailable
451
479
 
452
- Ensure the optional `vosk` dependency installed successfully, the configured model URL is reachable for its first download, and an audio recorder is available: SoX (`rec`/`sox`), `ffmpeg`, or `arecord` on Linux. Voice support can be omitted without affecting the rest of Pix.
480
+ Ensure `dictation.apiKey` is set in `~/.config/pi/pix.jsonc`, or set the compatibility fallback `DEEPGRAM_API_KEY` in the environment that launches Pix. Terminal voice input also needs an audio recorder: SoX (`rec`/`sox`), `ffmpeg`, or `arecord` on Linux. Pix Desktop additionally needs microphone permission from the operating system. Voice support can be omitted without affecting the rest of Pix.
453
481
 
454
482
  ### A provider login dialog is missing
455
483
 
@@ -508,7 +536,8 @@ npm run watch:all
508
536
  `watch:all` rebuilds only the affected project parts. It keeps the last working
509
537
  Desktop process alive while changes compile, then replaces it only after the
510
538
  entire queued build succeeds. A failed build is reported without replacing the
511
- running Desktop.
539
+ running Desktop. Desktop web changes rebuild the Vite bundle first and then
540
+ invalidate the native Tauri asset embedding before the replacement app starts.
512
541
 
513
542
  Regenerate the README screenshots without accounts or live model traffic:
514
543
 
package/dist/app/app.d.ts CHANGED
@@ -68,9 +68,18 @@ export declare class PiUiExtendApp {
68
68
  private voicePartialText;
69
69
  private resumeSessions;
70
70
  private resumeLoading;
71
+ private draftModels;
72
+ private draftModelRef;
73
+ private draftThinkingLevel;
74
+ private draftModelOverrideRef;
71
75
  constructor(options: AppOptions);
72
76
  private createRuntime;
73
77
  private loadStartupConfig;
78
+ private refreshDraftModelCatalog;
79
+ private resetDraftModelSelection;
80
+ private selectDraftModel;
81
+ private draftModelState;
82
+ private draftModelStatus;
74
83
  private applyPixConfig;
75
84
  private updateOutputFilters;
76
85
  start(): Promise<void>;
@@ -78,6 +87,7 @@ export declare class PiUiExtendApp {
78
87
  private bindCurrentSession;
79
88
  private awaitCurrentSessionExtensions;
80
89
  private activateRuntime;
90
+ private deactivateRuntimeForDraft;
81
91
  private createExtensionEventBus;
82
92
  private subagentTypesForRuntime;
83
93
  private contextInventoryText;
package/dist/app/app.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { THEMES } from "../theme.js";
2
2
  import { InputEditor } from "../input-editor.js";
3
- import { compileOutputFilterPatterns, defaultPixConfig, loadPixConfig, resolveToolRule, } from "../config.js";
3
+ import { compileOutputFilterPatterns, defaultPixConfig, loadPixConfig, resolveDefaultModelRef, resolveToolRule, savePixThinkingLevelForModel, savePixVisibleModels, } from "../config.js";
4
4
  import { AppCommandController } from "./commands/command-controller.js";
5
5
  import { ConversationViewport } from "./rendering/conversation-viewport.js";
6
6
  import { renderRegisteredExtensionEntry } from "./rendering/extension-entry-renderer.js";
@@ -23,7 +23,8 @@ import { AppAutocompleteController } from "./input/autocomplete-controller.js";
23
23
  import { AppQueuedMessageController } from "./session/queued-message-controller.js";
24
24
  import { AppRequestHistory } from "./session/request-history.js";
25
25
  import { AppRenderController } from "./rendering/render-controller.js";
26
- import { createPixRuntime } from "./runtime.js";
26
+ import { createPixDraftModelCatalog, createPixRuntime } from "./runtime.js";
27
+ import { parseModelRef } from "./model/model-ref.js";
27
28
  import { ScreenStyler } from "./screen/screen-styler.js";
28
29
  import { AppScrollController } from "./screen/scroll-controller.js";
29
30
  import { searchResultScrollNeedles, searchResultTargetEntry } from "./session/session-search.js";
@@ -142,6 +143,10 @@ export class PiUiExtendApp {
142
143
  voicePartialText;
143
144
  resumeSessions = [];
144
145
  resumeLoading = false;
146
+ draftModels = [];
147
+ draftModelRef;
148
+ draftThinkingLevel = "off";
149
+ draftModelOverrideRef;
145
150
  constructor(options) {
146
151
  this.options = options;
147
152
  this.theme = THEMES[options.themeName];
@@ -170,6 +175,7 @@ export class PiUiExtendApp {
170
175
  theme: this.theme,
171
176
  blinkController: this.blinkController,
172
177
  runtimeSession: () => this.runtime?.session,
178
+ draftModelStatus: () => this.draftModelStatus(),
173
179
  render: () => this.scheduleRender(),
174
180
  });
175
181
  this.agentPauseController = new AgentPauseController({
@@ -186,7 +192,7 @@ export class PiUiExtendApp {
186
192
  maxProjectSessions: () => this.pixConfig.maxProjectSessions,
187
193
  blinkController: this.blinkController,
188
194
  runtime: () => this.runtime,
189
- createRuntimeForNewSession: () => this.createRuntime(
195
+ createRuntimeForNewSession: (modelRef) => this.createRuntime(
190
196
  // Never reuse services across tabs. The SDK ties the extension
191
197
  // runtime (resourceLoader.getExtensions().runtime) to the
192
198
  // resourceLoader, and that runtime is shared by every session
@@ -197,12 +203,18 @@ export class PiUiExtendApp {
197
203
  // then throw "ctx is stale after session replacement or reload".
198
204
  // Fresh services per session = fresh extension runtime = no
199
205
  // cross-session invalidation.
200
- newTabRuntimeOptions(this.options)),
206
+ {
207
+ ...newTabRuntimeOptions(this.options),
208
+ ...(modelRef ? { modelRef } : {}),
209
+ }),
201
210
  createRuntimeForSession: (sessionPath) => this.createRuntime({
202
211
  ...this.options,
203
212
  noSession: false,
204
213
  sessionPath,
205
214
  }),
215
+ draftModelOverrideRef: () => this.draftModelOverrideRef,
216
+ resetDraftModelSelection: () => this.resetDraftModelSelection(),
217
+ deactivateRuntimeForDraft: () => this.deactivateRuntimeForDraft(),
206
218
  awaitCurrentSessionExtensions: (runtime) => this.awaitCurrentSessionExtensions(runtime),
207
219
  activateRuntime: (runtime, options) => this.activateRuntime(runtime, options),
208
220
  disposeRuntime: (runtime) => this.terminalController.disposeRuntime(runtime),
@@ -224,6 +236,12 @@ export class PiUiExtendApp {
224
236
  restoreAutoUserMessages: (messages) => this.queuedMessages.restoreAutoUserMessages(messages),
225
237
  captureDeferredUserMessages: () => this.queuedMessages.captureDeferredUserMessages(),
226
238
  restoreDeferredUserMessages: (messages) => this.queuedMessages.restoreDeferredUserMessages(messages),
239
+ openDraftSessionSelector: () => {
240
+ void this.commandController.runDraftSessionSelector();
241
+ },
242
+ closeDraftSessionSelector: () => {
243
+ this.popupMenus.closeDraftSessionSelectorForTabLifecycle();
244
+ },
227
245
  contextInventoryText: (runtime, heading) => this.contextInventoryText(runtime, heading),
228
246
  addEntry: (entry) => this.addEntry(entry),
229
247
  showToast: (message, kind) => this.showToast(message, kind),
@@ -262,9 +280,12 @@ export class PiUiExtendApp {
262
280
  }, this.pixConfig.dictation);
263
281
  this.menuItems = new AppMenuItemsController({
264
282
  runtime: () => this.runtime,
283
+ draftModelState: () => this.draftModelState(),
284
+ visibleModels: () => this.pixConfig.visibleModels,
265
285
  getBuiltinSlashCommands: () => this.slashCommands,
266
286
  getEntries: () => this.entries,
267
287
  getResumeSessions: () => this.resumeSessions,
288
+ getOpenSessionPaths: () => this.tabsController.openSessionPaths(),
268
289
  });
269
290
  const popupMenuRenderer = new PopupMenuRenderer({
270
291
  theme: this.theme,
@@ -279,6 +300,8 @@ export class PiUiExtendApp {
279
300
  this.popupMenus = new AppPopupMenuController({
280
301
  get entries() { return app.entries; },
281
302
  get session() { return app.runtime?.session; },
303
+ currentThinkingLevel: () => this.runtime?.session.thinkingLevel ?? this.draftModelState()?.thinkingLevel,
304
+ rememberedThinkingLevel: (modelRef) => this.pixConfig.thinkingByModel?.[modelRef],
282
305
  get resumeLoading() { return app.resumeLoading; },
283
306
  get resumeSessionCount() { return app.resumeSessions.length; },
284
307
  isRunning: () => this.running,
@@ -286,9 +309,9 @@ export class PiUiExtendApp {
286
309
  setInput: (value) => this.setInput(value),
287
310
  parseSlashInput: (text) => this.menuItems.parseSlashInput(text),
288
311
  getSlashCommandMenuItems: (query) => this.menuItems.getSlashCommandMenuItems(query),
289
- getModelMenuItems: (query) => this.menuItems.getModelMenuItems(query),
312
+ getModelMenuItems: (query, includeHidden) => this.menuItems.getModelMenuItems(query, includeHidden),
290
313
  getThinkingMenuItems: (query) => this.menuItems.getThinkingMenuItems(query),
291
- getResumeMenuItems: (query, limit) => this.menuItems.getResumeMenuItems(query, limit),
314
+ getResumeMenuItems: (query, limit, options) => this.menuItems.getResumeMenuItems(query, limit, options),
292
315
  getUserMessageMenuItems: () => this.menuItems.getUserMessageMenuItems(),
293
316
  getUserMessageJumpMenuItems: (query) => this.menuItems.getUserMessageJumpMenuItems(query),
294
317
  getQueueMessageMenuItems: () => this.menuItems.getQueueMessageMenuItems(),
@@ -302,6 +325,7 @@ export class PiUiExtendApp {
302
325
  theme: this.theme,
303
326
  screenStyler: this.screenStyler,
304
327
  get session() { return app.runtime?.session; },
328
+ draftModelStatus: () => this.draftModelStatus(),
305
329
  modelColors: this.pixConfig.modelColors,
306
330
  get sessionActivity() { return app.statusController.sessionActivity; },
307
331
  get statusDotBright() { return app.statusController.statusDotBright; },
@@ -377,6 +401,7 @@ export class PiUiExtendApp {
377
401
  });
378
402
  this.subagentsWidgetController = new AppSubagentsWidgetController({
379
403
  cwd: this.options.cwd,
404
+ hasActiveRuntime: () => this.runtime !== undefined,
380
405
  sessionFile: () => this.runtime?.session.sessionFile,
381
406
  isRunning: () => this.running,
382
407
  render: () => this.scheduleRender(),
@@ -513,6 +538,7 @@ export class PiUiExtendApp {
513
538
  this.commandController = new AppCommandController({
514
539
  options: this.options,
515
540
  runtime: () => this.runtime,
541
+ inputScopeKey: () => this.tabsController.activeInputTabId(),
516
542
  subagentTypes: (runtime) => this.subagentTypesForRuntime(runtime),
517
543
  requestHistory: () => this.requestHistory,
518
544
  getInput: () => this.input,
@@ -558,6 +584,7 @@ export class PiUiExtendApp {
558
584
  setDirectPopupMenuQuery: (query) => {
559
585
  this.popupMenus.setDirectQuery(query);
560
586
  },
587
+ setResumeMenuMode: (mode) => this.popupMenus.setResumeMenuMode(mode),
561
588
  refreshUserMessageJumpMenuItems: () => this.menuItems.refreshUserMessageJumpMenuItems(),
562
589
  getResumeLoading: () => this.resumeLoading,
563
590
  getResumeSessions: () => this.resumeSessions,
@@ -576,6 +603,10 @@ export class PiUiExtendApp {
576
603
  });
577
604
  this.popupActions = new AppPopupActionController({
578
605
  runtime: () => this.runtime,
606
+ inputScopeKey: () => this.tabsController.activeInputTabId(),
607
+ isDraftTabActive: () => this.tabsController.isDraftTabActive(),
608
+ materializeDraftSession: () => this.tabsController.materializeActiveDraftTab(),
609
+ selectDraftModel: (model, thinkingLevel) => this.selectDraftModel(model, thinkingLevel),
579
610
  getBuiltinSlashCommands: () => this.slashCommands,
580
611
  isRunning: () => this.running,
581
612
  setInput: (value) => this.setInput(value),
@@ -583,9 +614,20 @@ export class PiUiExtendApp {
583
614
  setStatus: (status) => this.setStatus(status),
584
615
  setSessionStatus: (session) => this.setSessionStatus(session),
585
616
  showToast: (message, kind) => this.showToast(message, kind),
617
+ visibleModels: () => this.pixConfig.visibleModels,
618
+ saveVisibleModels: (modelRefs) => {
619
+ const saved = savePixVisibleModels(modelRefs);
620
+ this.pixConfig.visibleModels = [...saved];
621
+ return saved;
622
+ },
623
+ saveThinkingLevelForModel: (modelRef, thinkingLevel) => {
624
+ const saved = savePixThinkingLevelForModel(modelRef, thinkingLevel);
625
+ this.pixConfig.thinkingByModel = { ...saved };
626
+ },
586
627
  render: () => this.render(),
587
628
  awaitCurrentSessionExtensions: (runtime) => this.awaitCurrentSessionExtensions(runtime),
588
629
  afterSessionReplacement: (message) => this.afterSessionReplacement(message),
630
+ openSessionInActiveDraftTab: (sessionPath) => this.tabsController.openSessionInActiveDraftTab(sessionPath),
589
631
  scrollToConversationEntry: (entryId) => this.scrollController.scrollToConversationEntry(entryId),
590
632
  scrollToUserMessageJumpTarget: (target) => this.scrollToUserMessageJumpTarget(target),
591
633
  }, this.popupMenus, this.commandController, this.menuItems, this.queuedMessages, this.workspaceActions);
@@ -697,6 +739,8 @@ export class PiUiExtendApp {
697
739
  this.inputActions = new AppInputActionController({
698
740
  runtime: () => this.runtime,
699
741
  inputScopeKey: () => this.tabsController.activeInputTabId(),
742
+ isDraftTabActive: () => this.tabsController.isDraftTabActive(),
743
+ materializeDraftSession: () => this.tabsController.materializeActiveDraftTab(),
700
744
  isRunning: () => this.running,
701
745
  isSessionSwitching: () => this.tabsController.isSwitching(),
702
746
  inputEditor: () => this.inputEditor,
@@ -736,6 +780,7 @@ export class PiUiExtendApp {
736
780
  resetInputMenuDismissals: () => this.popupMenus.resetInputMenuDismissals(),
737
781
  render: () => this.render(),
738
782
  moveActivePopupMenuSelection: (delta) => this.popupMenus.moveActivePopupMenuSelection(delta),
783
+ moveActiveModelThinkingLevel: (delta) => this.popupMenus.moveActiveModelThinkingLevel(delta),
739
784
  navigateRequestHistory: (delta) => this.requestHistory.navigate(delta),
740
785
  scrollByLines: (delta) => this.scrollController.scrollByLines(delta),
741
786
  scrollByPage: (delta) => this.scrollController.scrollByPage(delta),
@@ -744,8 +789,8 @@ export class PiUiExtendApp {
744
789
  handleInterrupt: () => this.inputActions.handleInterrupt(),
745
790
  handleEscape: () => this.inputActions.handleEscape(),
746
791
  handleDirectPopupInput: (char) => this.popupMenus.handleDirectPopupInput(char),
792
+ toggleModelVisibilityMode: () => this.popupMenus.toggleModelVisibilityMode(),
747
793
  autocompleteModel: () => this.popupMenus.autocompleteModel(),
748
- autocompleteThinking: () => this.popupMenus.autocompleteThinking(),
749
794
  acceptAutocompleteSuggestion: () => this.autocompleteController.acceptSuggestion(),
750
795
  autocompleteSlashCommand: () => this.popupMenus.autocompleteSlashCommand(),
751
796
  toggleVoiceRecording: () => {
@@ -780,7 +825,10 @@ export class PiUiExtendApp {
780
825
  });
781
826
  this.sessionLifecycle = new AppSessionLifecycleController({
782
827
  options: this.options,
783
- createRuntime: () => this.createRuntime(this.options),
828
+ prepareStartupRuntime: () => this.tabsController.prepareStartupRuntime(),
829
+ createRuntime: (startup) => this.createRuntime(startup?.sessionPath
830
+ ? { ...this.options, noSession: false, sessionPath: startup.sessionPath }
831
+ : this.options),
784
832
  entries: this.entries,
785
833
  runtime: () => this.runtime,
786
834
  setRuntime: (runtime) => {
@@ -863,6 +911,59 @@ export class PiUiExtendApp {
863
911
  async loadStartupConfig() {
864
912
  await yieldToEventLoop();
865
913
  this.applyPixConfig(loadPixConfig(this.options.cwd));
914
+ await this.refreshDraftModelCatalog();
915
+ }
916
+ async refreshDraftModelCatalog() {
917
+ try {
918
+ this.draftModels = await createPixDraftModelCatalog({ cwd: this.options.cwd });
919
+ }
920
+ catch {
921
+ this.draftModels = [];
922
+ }
923
+ this.resetDraftModelSelection();
924
+ }
925
+ resetDraftModelSelection() {
926
+ this.draftModelOverrideRef = undefined;
927
+ const configuredRef = this.options.modelRef ?? resolveDefaultModelRef(this.pixConfig);
928
+ if (configuredRef) {
929
+ try {
930
+ const parsed = parseModelRef(configuredRef);
931
+ this.draftModelRef = `${parsed.provider}/${parsed.modelId}`;
932
+ this.draftThinkingLevel = parsed.thinkingLevel ?? "off";
933
+ return;
934
+ }
935
+ catch {
936
+ // Runtime creation will surface an invalid configured model. Keep the
937
+ // draft picker usable so the user can select a valid model first.
938
+ }
939
+ }
940
+ const first = this.draftModels[0];
941
+ this.draftModelRef = first ? `${first.provider}/${first.id}` : undefined;
942
+ this.draftThinkingLevel = "off";
943
+ }
944
+ selectDraftModel(model, thinkingLevel) {
945
+ if (!this.tabsController.isDraftTabActive())
946
+ return;
947
+ const ref = `${model.provider}/${model.id}`;
948
+ this.draftModelRef = ref;
949
+ this.draftThinkingLevel = thinkingLevel;
950
+ this.draftModelOverrideRef = `${ref}:${thinkingLevel}`;
951
+ this.setStatus("new conversation");
952
+ }
953
+ draftModelState() {
954
+ if (!this.tabsController.isDraftTabActive())
955
+ return undefined;
956
+ return {
957
+ models: this.draftModels,
958
+ ...(this.draftModelRef ? { modelRef: this.draftModelRef } : {}),
959
+ thinkingLevel: this.draftThinkingLevel,
960
+ };
961
+ }
962
+ draftModelStatus() {
963
+ const state = this.draftModelState();
964
+ if (!state?.modelRef)
965
+ return undefined;
966
+ return { modelLabel: state.modelRef, thinkingLabel: state.thinkingLevel };
866
967
  }
867
968
  applyPixConfig(config) {
868
969
  replaceRecord(this.pixConfig.toolRenderer, config.toolRenderer);
@@ -876,6 +977,14 @@ export class PiUiExtendApp {
876
977
  delete this.pixConfig.defaultModel;
877
978
  else
878
979
  this.pixConfig.defaultModel = { ...config.defaultModel };
980
+ if (config.visibleModels === undefined)
981
+ delete this.pixConfig.visibleModels;
982
+ else
983
+ this.pixConfig.visibleModels = [...config.visibleModels];
984
+ if (config.thinkingByModel === undefined)
985
+ delete this.pixConfig.thinkingByModel;
986
+ else
987
+ this.pixConfig.thinkingByModel = { ...config.thinkingByModel };
879
988
  this.pixConfig.ignoreContextFiles = config.ignoreContextFiles;
880
989
  this.pixConfig.maxProjectSessions = config.maxProjectSessions;
881
990
  this.updateOutputFilters();
@@ -946,6 +1055,20 @@ export class PiUiExtendApp {
946
1055
  });
947
1056
  await this.bindCurrentSession(options);
948
1057
  }
1058
+ deactivateRuntimeForDraft() {
1059
+ const runtime = this.runtime;
1060
+ if (!runtime)
1061
+ return;
1062
+ const scopeKey = this.activeExtensionUiScope();
1063
+ // Clear the visible runtime before any UI cleanup. cancelCustomUi() can
1064
+ // synchronously render; leaving the old runtime attached during that render
1065
+ // lets tab synchronization incorrectly reactivate the previous real tab.
1066
+ this.runtime = undefined;
1067
+ this.extensionUiController.cancelCustomUi(scopeKey);
1068
+ this.extensionUiController.clearWidgets(scopeKey, { cancelCustomUi: false });
1069
+ void this.voiceController.stopRecording();
1070
+ this.sessionLifecycle.unsubscribeSession();
1071
+ }
949
1072
  createExtensionEventBus() {
950
1073
  return createIsolatedExtensionEventBus((channel, data) => {
951
1074
  if (channel === TERMINAL_BELL_ATTENTION_EVENT)
@@ -1276,6 +1399,7 @@ export class PiUiExtendApp {
1276
1399
  if (contentVersion === this.lastInputEditorContentVersion)
1277
1400
  return;
1278
1401
  this.lastInputEditorContentVersion = contentVersion;
1402
+ this.tabsController.noteActiveDraftInputChanged();
1279
1403
  this.scrollController.scrollToBottom();
1280
1404
  }
1281
1405
  conversationQuickScrollDirections() {
@@ -59,7 +59,7 @@ export function inspectPixOnboarding(homeDir = homedir(), env = process.env) {
59
59
  opencodeAntigravityExists: pixInstallDeps.existsSync(join(opencodeConfigDir, "antigravity-accounts.json")),
60
60
  webCredentialsConfigured: Boolean(env.OLLAMA_API_KEY?.trim() || env.TAVILY_API_KEY?.trim()) || jsonObjectHasEntries(webCredentialPath),
61
61
  context7Configured: Boolean(env.CONTEXT7_API_KEY?.trim()),
62
- telegramConfigured: Boolean(env.PI_TERMINAL_BELL_TELEGRAM_BOT_TOKEN?.trim() && env.PI_TERMINAL_BELL_TELEGRAM_CHAT_ID?.trim()),
62
+ telegramConfigured: Boolean(env.PIX_TELEGRAM_BOT_TOKEN?.trim() && env.PIX_TELEGRAM_CHAT_ID?.trim()),
63
63
  };
64
64
  }
65
65
  export function ensurePixSetupFiles(homeDir = homedir()) {
@@ -106,7 +106,7 @@ export function formatPixInstallNextSteps(homeDir = homedir(), state = inspectPi
106
106
  "Optional integrations:",
107
107
  ` ${state.webCredentialsConfigured ? "✓ Web credentials detected." : "○ Web search: use local Ollama without a key, or run /web-credentials for Ollama Cloud/Tavily."}`,
108
108
  ` ${state.context7Configured ? "✓ Context7 API key detected." : "○ Context7 docs skill: export CONTEXT7_API_KEY."}`,
109
- ` ${state.telegramConfigured ? "✓ Telegram terminal-bell credentials detected in the environment." : "○ Telegram bell: set terminalBell.telegram in Pix config or PI_TERMINAL_BELL_TELEGRAM_* env vars."}`,
109
+ ` ${state.telegramConfigured ? "✓ Telegram connector credentials detected in the environment." : "○ Telegram connector: set telegramConnector in tools config or PIX_TELEGRAM_BOT_TOKEN plus PIX_TELEGRAM_CHAT_ID."}`,
110
110
  " ○ Model-backed helpers: review promptEnhancer, autocomplete, and sessionTitle model refs; each provider needs its own credentials.",
111
111
  " ○ Voice: set dictation.language in Pix config. LSP: configure and trust servers under lsp.servers in tools config.",
112
112
  "",
@@ -4,14 +4,18 @@ export declare class AppCommandController {
4
4
  private readonly host;
5
5
  readonly slashCommands: readonly SlashCommand[];
6
6
  private readonly modelActions;
7
+ private readonly gitActions;
7
8
  private readonly sessionActions;
8
9
  private readonly navigationActions;
9
10
  constructor(host: CommandControllerHost);
10
11
  runResumeCommand(queryOrOptions?: string | {
11
12
  preserveStatus?: boolean;
12
13
  placement?: PopupMenuPlacement;
14
+ draft?: boolean;
13
15
  }): Promise<void>;
16
+ runDraftSessionSelector(): Promise<void>;
14
17
  runModelCommand(model: SessionModel): Promise<void>;
18
+ runModelThinkingCommand(model: SessionModel, level: ThinkingLevel): Promise<void>;
15
19
  runThinkingCommand(level: ThinkingLevel): Promise<void>;
16
20
  private registryActions;
17
21
  }
@@ -1,3 +1,4 @@
1
+ import { GitCommandActions } from "./command-git-actions.js";
1
2
  import { ModelCommandActions } from "./command-model-actions.js";
2
3
  import { NavigationCommandActions } from "./command-navigation-actions.js";
3
4
  import { createSlashCommands } from "./command-registry.js";
@@ -6,11 +7,13 @@ export class AppCommandController {
6
7
  host;
7
8
  slashCommands;
8
9
  modelActions;
10
+ gitActions;
9
11
  sessionActions;
10
12
  navigationActions;
11
13
  constructor(host) {
12
14
  this.host = host;
13
15
  this.modelActions = new ModelCommandActions(host);
16
+ this.gitActions = new GitCommandActions(host);
14
17
  this.sessionActions = new SessionCommandActions(host);
15
18
  this.navigationActions = new NavigationCommandActions(host);
16
19
  this.slashCommands = createSlashCommands(this.registryActions(), host);
@@ -18,9 +21,15 @@ export class AppCommandController {
18
21
  async runResumeCommand(queryOrOptions = "") {
19
22
  await this.navigationActions.runResumeCommand(queryOrOptions);
20
23
  }
24
+ async runDraftSessionSelector() {
25
+ await this.navigationActions.runResumeCommand({ preserveStatus: true, placement: "draft-surface", draft: true });
26
+ }
21
27
  async runModelCommand(model) {
22
28
  await this.modelActions.runModelCommand(model);
23
29
  }
30
+ async runModelThinkingCommand(model, level) {
31
+ await this.modelActions.runModelThinkingCommand(model, level);
32
+ }
24
33
  async runThinkingCommand(level) {
25
34
  await this.modelActions.runThinkingCommand(level);
26
35
  }
@@ -35,6 +44,8 @@ export class AppCommandController {
35
44
  runThinkingSlashCommand: (argumentsText) => this.modelActions.runThinkingSlashCommand(argumentsText),
36
45
  runDefaultThinkingSlashCommand: (argumentsText) => this.modelActions.runDefaultThinkingSlashCommand(argumentsText),
37
46
  runEnhanceCommand: () => this.host.enhancePrompt(),
47
+ runCodeReviewCommand: () => this.gitActions.runCodeReviewCommand(),
48
+ runCommitMessageCommand: () => this.gitActions.runCommitMessageCommand(),
38
49
  runExportCommand: (argumentsText) => this.sessionActions.runExportCommand(argumentsText),
39
50
  runImportCommand: (argumentsText) => this.sessionActions.runImportCommand(argumentsText),
40
51
  runShareCommand: () => this.sessionActions.runShareCommand(),
@@ -0,0 +1,25 @@
1
+ import type { AgentSessionRuntime } from "@earendil-works/pi-coding-agent";
2
+ import { type AsyncProcessResult, type RunProcessOptions } from "../process.js";
3
+ import { type CommandControllerHost } from "./command-host.js";
4
+ export type GitAssistantKind = "review" | "commit-message";
5
+ type GitProcessRunner = (command: string, args?: readonly string[], options?: RunProcessOptions) => Promise<AsyncProcessResult>;
6
+ type GitAssistantRunner = (runtime: AgentSessionRuntime, kind: GitAssistantKind, diff: string) => Promise<string>;
7
+ export type GitCommandActionsDeps = {
8
+ runProcess?: GitProcessRunner;
9
+ generateGitAssistantText?: GitAssistantRunner;
10
+ };
11
+ export declare class GitCommandActions {
12
+ private readonly host;
13
+ private readonly runProcess;
14
+ private readonly generateGitAssistantText;
15
+ constructor(host: CommandControllerHost, deps?: GitCommandActionsDeps);
16
+ runCodeReviewCommand(): Promise<void>;
17
+ runCommitMessageCommand(): Promise<void>;
18
+ private gitRoot;
19
+ private trackedDiff;
20
+ private reviewInput;
21
+ private untrackedFiles;
22
+ }
23
+ export declare function generateGitAssistantText(runtime: AgentSessionRuntime, kind: GitAssistantKind, diff: string): Promise<string>;
24
+ export declare function loadGitAssistantModelRefs(cwd: string, kind: GitAssistantKind, homeDir?: string): string[];
25
+ export {};