@oh-my-pi/pi-coding-agent 17.0.9 → 17.1.1

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 (349) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +1 -1
  3. package/dist/cli.js +4685 -4614
  4. package/dist/types/async/job-manager.d.ts +37 -1
  5. package/dist/types/cli/args.d.ts +2 -0
  6. package/dist/types/cli/bench-cli.d.ts +41 -0
  7. package/dist/types/commands/bench.d.ts +15 -3
  8. package/dist/types/commands/launch.d.ts +4 -0
  9. package/dist/types/config/model-registry.d.ts +3 -1
  10. package/dist/types/config/models-config-schema.d.ts +18 -6
  11. package/dist/types/config/models-config.d.ts +14 -5
  12. package/dist/types/config/provider-globals.d.ts +2 -2
  13. package/dist/types/config/service-tier.d.ts +5 -1
  14. package/dist/types/config/settings-schema.d.ts +374 -47
  15. package/dist/types/config/settings.d.ts +7 -0
  16. package/dist/types/dap/client.d.ts +20 -0
  17. package/dist/types/exec/bash-executor.d.ts +38 -0
  18. package/dist/types/exec/direnv.d.ts +33 -0
  19. package/dist/types/export/html/args.d.ts +15 -0
  20. package/dist/types/export/html/index.d.ts +11 -26
  21. package/dist/types/export/html/web-palette.d.ts +55 -116
  22. package/dist/types/extensibility/extensions/loader.d.ts +3 -0
  23. package/dist/types/extensibility/extensions/types.d.ts +17 -2
  24. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +3 -0
  25. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +13 -0
  26. package/dist/types/live/attestation.d.ts +2 -0
  27. package/dist/types/live/controller.d.ts +48 -0
  28. package/dist/types/live/protocol.d.ts +85 -0
  29. package/dist/types/live/protocol.test.d.ts +1 -0
  30. package/dist/types/live/transport.d.ts +35 -0
  31. package/dist/types/live/visualizer.d.ts +30 -0
  32. package/dist/types/lsp/client.d.ts +16 -1
  33. package/dist/types/mcp/config-writer.test.d.ts +1 -0
  34. package/dist/types/mcp/oauth-discovery.d.ts +2 -0
  35. package/dist/types/mcp/smithery-auth.d.ts +1 -1
  36. package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
  37. package/dist/types/memory-backend/tool-names.d.ts +2 -0
  38. package/dist/types/modes/components/assistant-message.d.ts +1 -0
  39. package/dist/types/modes/components/custom-message.d.ts +1 -1
  40. package/dist/types/modes/components/login-dialog.d.ts +2 -2
  41. package/dist/types/modes/components/message-frame.d.ts +8 -4
  42. package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
  43. package/dist/types/modes/components/session-account-selector.d.ts +11 -0
  44. package/dist/types/modes/components/settings-defs.d.ts +7 -1
  45. package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
  46. package/dist/types/modes/components/tool-execution.d.ts +9 -1
  47. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  48. package/dist/types/modes/controllers/event-controller.d.ts +6 -1
  49. package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
  50. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  51. package/dist/types/modes/interactive-mode.d.ts +6 -0
  52. package/dist/types/modes/theme/theme.d.ts +1 -1
  53. package/dist/types/modes/types.d.ts +4 -1
  54. package/dist/types/modes/utils/context-usage.d.ts +12 -2
  55. package/dist/types/plan-mode/plan-files.d.ts +10 -0
  56. package/dist/types/registry/agent-lifecycle.d.ts +21 -9
  57. package/dist/types/registry/agent-registry.d.ts +11 -4
  58. package/dist/types/sdk.d.ts +15 -1
  59. package/dist/types/secrets/index.d.ts +15 -2
  60. package/dist/types/secrets/obfuscator.d.ts +113 -15
  61. package/dist/types/session/acp-permission-gate.d.ts +19 -0
  62. package/dist/types/session/agent-session-events.d.ts +84 -0
  63. package/dist/types/session/agent-session-types.d.ts +321 -0
  64. package/dist/types/session/agent-session.d.ts +135 -753
  65. package/dist/types/session/async-job-delivery.d.ts +28 -0
  66. package/dist/types/session/auth-storage.d.ts +1 -1
  67. package/dist/types/session/bash-runner.d.ts +75 -0
  68. package/dist/types/session/blob-store.d.ts +11 -1
  69. package/dist/types/session/blob-store.test.d.ts +1 -0
  70. package/dist/types/session/checkpoint-entries.d.ts +28 -0
  71. package/dist/types/session/eval-runner.d.ts +51 -0
  72. package/dist/types/session/irc-bridge.d.ts +46 -0
  73. package/dist/types/session/messages.d.ts +24 -0
  74. package/dist/types/session/model-controls.d.ts +180 -0
  75. package/dist/types/session/prewalk.d.ts +46 -0
  76. package/dist/types/session/queued-messages.d.ts +22 -0
  77. package/dist/types/session/redis-session-storage.d.ts +1 -0
  78. package/dist/types/session/retry-fallback-chains.d.ts +74 -0
  79. package/dist/types/session/role-models.d.ts +15 -0
  80. package/dist/types/session/session-advisors.d.ts +228 -0
  81. package/dist/types/session/session-entries.d.ts +8 -0
  82. package/dist/types/session/session-handoff.d.ts +70 -0
  83. package/dist/types/session/session-history-format.d.ts +6 -1
  84. package/dist/types/session/session-maintenance.d.ts +246 -0
  85. package/dist/types/session/session-manager.d.ts +41 -0
  86. package/dist/types/session/session-memory.d.ts +56 -0
  87. package/dist/types/session/session-provider-boundary.d.ts +62 -0
  88. package/dist/types/session/session-stats.d.ts +48 -0
  89. package/dist/types/session/session-tools.d.ts +158 -0
  90. package/dist/types/session/session-workspace.d.ts +32 -0
  91. package/dist/types/session/stream-guards.d.ts +44 -0
  92. package/dist/types/session/streaming-output.d.ts +8 -0
  93. package/dist/types/session/todo-tracker.d.ts +56 -0
  94. package/dist/types/session/ttsr-coordinator.d.ts +51 -0
  95. package/dist/types/session/turn-recovery.d.ts +225 -0
  96. package/dist/types/slash-commands/helpers/session-pin.d.ts +9 -0
  97. package/dist/types/stt/index.d.ts +0 -2
  98. package/dist/types/stt/stt-controller.d.ts +7 -0
  99. package/dist/types/system-prompt.d.ts +2 -0
  100. package/dist/types/task/executor.d.ts +4 -1
  101. package/dist/types/task/output-manager.d.ts +3 -1
  102. package/dist/types/tiny/title-client.d.ts +10 -0
  103. package/dist/types/tools/approval.d.ts +1 -0
  104. package/dist/types/tools/browser/launch.d.ts +5 -0
  105. package/dist/types/tools/builtin-names.d.ts +1 -1
  106. package/dist/types/tools/computer/protocol.d.ts +43 -0
  107. package/dist/types/tools/computer/supervisor.d.ts +32 -0
  108. package/dist/types/tools/computer/worker-entry.d.ts +1 -0
  109. package/dist/types/tools/computer/worker.d.ts +15 -0
  110. package/dist/types/tools/computer-renderer.d.ts +22 -0
  111. package/dist/types/tools/computer.d.ts +71 -0
  112. package/dist/types/tools/context.d.ts +2 -0
  113. package/dist/types/tools/default-renderer.d.ts +21 -0
  114. package/dist/types/tools/image-gen.d.ts +4 -4
  115. package/dist/types/tools/image-providers.d.ts +38 -0
  116. package/dist/types/tools/index.d.ts +7 -0
  117. package/dist/types/tools/path-utils.d.ts +8 -0
  118. package/dist/types/tools/todo.d.ts +10 -4
  119. package/dist/types/tools/xdev.d.ts +5 -1
  120. package/dist/types/tts/streaming-player.d.ts +10 -29
  121. package/dist/types/tts/vocalizer.d.ts +5 -0
  122. package/dist/types/utils/jj.d.ts +29 -0
  123. package/dist/types/utils/lang-from-path.d.ts +1 -0
  124. package/dist/types/utils/title-generator.d.ts +30 -0
  125. package/dist/types/utils/tools-manager.d.ts +1 -2
  126. package/dist/types/vibe/runtime.d.ts +36 -8
  127. package/dist/types/web/search/index.d.ts +1 -1
  128. package/dist/types/web/search/provider.d.ts +15 -5
  129. package/dist/types/web/search/types.d.ts +94 -0
  130. package/package.json +12 -12
  131. package/scripts/generate-share-viewer.ts +4 -6
  132. package/src/advisor/__tests__/advisor.test.ts +643 -0
  133. package/src/advisor/runtime.ts +144 -43
  134. package/src/async/job-manager.ts +90 -2
  135. package/src/cli/args.ts +3 -0
  136. package/src/cli/auth-gateway-cli.ts +18 -3
  137. package/src/cli/bench-cli.ts +395 -29
  138. package/src/cli/flag-tables.ts +3 -0
  139. package/src/cli/gc-cli.ts +2 -2
  140. package/src/cli/setup-cli.ts +2 -14
  141. package/src/cli/usage-cli.ts +10 -1
  142. package/src/cli.ts +8 -0
  143. package/src/collab/host.ts +3 -2
  144. package/src/commands/bench.ts +18 -4
  145. package/src/commands/launch.ts +4 -0
  146. package/src/config/model-registry.ts +16 -2
  147. package/src/config/models-config-schema.ts +15 -4
  148. package/src/config/provider-globals.ts +9 -9
  149. package/src/config/service-tier.ts +26 -1
  150. package/src/config/settings-schema.ts +270 -51
  151. package/src/config/settings.ts +81 -2
  152. package/src/dap/client.ts +37 -4
  153. package/src/edit/index.ts +49 -12
  154. package/src/eval/executor-base.ts +1 -0
  155. package/src/eval/js/executor.ts +2 -0
  156. package/src/eval/py/executor.ts +103 -33
  157. package/src/exec/bash-executor.ts +90 -6
  158. package/src/exec/direnv.ts +145 -0
  159. package/src/export/html/args.ts +20 -0
  160. package/src/export/html/index.ts +45 -47
  161. package/src/export/html/template.css +19 -1
  162. package/src/export/html/template.html +6 -0
  163. package/src/export/html/template.js +21 -0
  164. package/src/export/html/tool-views.generated.js +31 -31
  165. package/src/export/html/web-palette.ts +116 -132
  166. package/src/export/share.ts +249 -49
  167. package/src/extensibility/extensions/loader.ts +29 -1
  168. package/src/extensibility/extensions/runner.ts +6 -0
  169. package/src/extensibility/extensions/types.ts +33 -2
  170. package/src/extensibility/extensions/wrapper.ts +68 -12
  171. package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -1
  172. package/src/extensibility/plugins/legacy-pi-compat.ts +19 -1
  173. package/src/hindsight/state.ts +64 -6
  174. package/src/internal-urls/local-protocol.ts +2 -1
  175. package/src/internal-urls/memory-protocol.ts +2 -2
  176. package/src/internal-urls/skill-protocol.ts +2 -2
  177. package/src/internal-urls/ssh-protocol.ts +2 -1
  178. package/src/internal-urls/vault-protocol.ts +2 -1
  179. package/src/live/attestation.ts +91 -0
  180. package/src/live/controller.ts +517 -0
  181. package/src/live/prompts/agent-final-message.md +3 -0
  182. package/src/live/prompts/live-instructions.md +23 -0
  183. package/src/live/protocol.test.ts +140 -0
  184. package/src/live/protocol.ts +233 -0
  185. package/src/live/transport.ts +422 -0
  186. package/src/live/visualizer.ts +214 -0
  187. package/src/lsp/client.ts +29 -9
  188. package/src/lsp/index.ts +19 -4
  189. package/src/main.ts +8 -1
  190. package/src/mcp/config-writer.test.ts +43 -0
  191. package/src/mcp/config-writer.ts +109 -82
  192. package/src/mcp/oauth-discovery.ts +10 -2
  193. package/src/mcp/smithery-auth.test.ts +29 -0
  194. package/src/mcp/smithery-auth.ts +3 -2
  195. package/src/memories/index.ts +40 -20
  196. package/src/memory-backend/tool-names.ts +2 -0
  197. package/src/mnemopi/backend.ts +24 -7
  198. package/src/modes/acp/acp-agent.ts +56 -10
  199. package/src/modes/acp/acp-event-mapper.ts +8 -1
  200. package/src/modes/components/agent-hub.ts +1 -1
  201. package/src/modes/components/assistant-message.ts +7 -2
  202. package/src/modes/components/custom-message.ts +4 -1
  203. package/src/modes/components/login-dialog.ts +14 -4
  204. package/src/modes/components/message-frame.ts +14 -8
  205. package/src/modes/components/plan-review-overlay.ts +350 -35
  206. package/src/modes/components/session-account-selector.ts +62 -0
  207. package/src/modes/components/settings-defs.ts +23 -1
  208. package/src/modes/components/settings-selector.ts +169 -1
  209. package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
  210. package/src/modes/components/status-line/component.ts +123 -2
  211. package/src/modes/components/tool-execution.ts +28 -112
  212. package/src/modes/controllers/command-controller.ts +56 -15
  213. package/src/modes/controllers/event-controller.ts +111 -16
  214. package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
  215. package/src/modes/controllers/extension-ui-controller.ts +20 -4
  216. package/src/modes/controllers/input-controller.ts +47 -39
  217. package/src/modes/controllers/live-command-controller.ts +255 -0
  218. package/src/modes/controllers/mcp-command-controller.ts +10 -1
  219. package/src/modes/controllers/omfg-controller.ts +44 -40
  220. package/src/modes/controllers/selector-controller.ts +80 -10
  221. package/src/modes/controllers/session-focus-controller.ts +6 -1
  222. package/src/modes/interactive-mode.ts +176 -38
  223. package/src/modes/runtime-init.ts +2 -0
  224. package/src/modes/setup-wizard/scenes/web-search.ts +17 -9
  225. package/src/modes/theme/theme.ts +1 -1
  226. package/src/modes/types.ts +4 -1
  227. package/src/modes/utils/context-usage.ts +23 -7
  228. package/src/modes/utils/ui-helpers.ts +10 -5
  229. package/src/plan-mode/plan-files.ts +40 -0
  230. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  231. package/src/prompts/bench/cache-prefix.md +3 -0
  232. package/src/prompts/bench/cache-suffix.md +1 -0
  233. package/src/prompts/system/computer-safety.md +14 -0
  234. package/src/prompts/system/custom-system-prompt.md +1 -1
  235. package/src/prompts/system/project-prompt.md +8 -1
  236. package/src/prompts/system/subagent-async-pending.md +6 -0
  237. package/src/prompts/system/system-prompt.md +1 -1
  238. package/src/prompts/system/workflow-notice.md +48 -40
  239. package/src/prompts/system/xdev-mount-notice.md +4 -0
  240. package/src/prompts/tools/computer.md +26 -0
  241. package/src/prompts/tools/todo.md +3 -1
  242. package/src/registry/agent-lifecycle.ts +116 -46
  243. package/src/registry/agent-registry.ts +35 -8
  244. package/src/registry/persisted-agents.ts +26 -2
  245. package/src/sdk.ts +215 -136
  246. package/src/secrets/index.ts +127 -4
  247. package/src/secrets/obfuscator.ts +2328 -105
  248. package/src/session/acp-permission-gate.ts +165 -0
  249. package/src/session/agent-session-error-log.test.ts +1 -1
  250. package/src/session/agent-session-events.ts +66 -0
  251. package/src/session/agent-session-types.ts +343 -0
  252. package/src/session/agent-session.ts +1940 -12218
  253. package/src/session/artifacts.ts +6 -5
  254. package/src/session/async-job-delivery.ts +74 -0
  255. package/src/session/auth-storage.ts +1 -0
  256. package/src/session/bash-runner.ts +326 -0
  257. package/src/session/blob-store.test.ts +56 -0
  258. package/src/session/blob-store.ts +18 -2
  259. package/src/session/checkpoint-entries.ts +81 -0
  260. package/src/session/eval-runner.ts +217 -0
  261. package/src/session/indexed-session-storage.ts +9 -2
  262. package/src/session/irc-bridge.ts +203 -0
  263. package/src/session/messages.ts +266 -1
  264. package/src/session/model-controls.ts +714 -0
  265. package/src/session/prewalk.ts +252 -0
  266. package/src/session/queued-messages.ts +93 -0
  267. package/src/session/redis-session-storage.ts +51 -11
  268. package/src/session/retry-fallback-chains.ts +455 -0
  269. package/src/session/role-models.ts +85 -0
  270. package/src/session/session-advisors.ts +1679 -0
  271. package/src/session/session-context.ts +6 -3
  272. package/src/session/session-entries.ts +8 -0
  273. package/src/session/session-handoff.ts +308 -0
  274. package/src/session/session-history-format.ts +20 -9
  275. package/src/session/session-loader.ts +0 -46
  276. package/src/session/session-maintenance.ts +2983 -0
  277. package/src/session/session-manager.ts +390 -30
  278. package/src/session/session-memory.ts +222 -0
  279. package/src/session/session-provider-boundary.ts +307 -0
  280. package/src/session/session-stats.ts +293 -0
  281. package/src/session/session-tools.ts +980 -0
  282. package/src/session/session-workspace.ts +53 -0
  283. package/src/session/stream-guards.ts +417 -0
  284. package/src/session/streaming-output.ts +52 -5
  285. package/src/session/todo-tracker.ts +380 -0
  286. package/src/session/ttsr-coordinator.ts +496 -0
  287. package/src/session/turn-recovery.ts +1629 -0
  288. package/src/slash-commands/builtin-registry.ts +257 -20
  289. package/src/slash-commands/helpers/session-pin.ts +44 -0
  290. package/src/slash-commands/helpers/usage-report.ts +22 -3
  291. package/src/stt/downloader.ts +0 -2
  292. package/src/stt/index.ts +0 -2
  293. package/src/stt/stt-controller.ts +57 -146
  294. package/src/system-prompt.ts +45 -17
  295. package/src/task/executor.ts +285 -70
  296. package/src/task/index.ts +98 -105
  297. package/src/task/output-manager.ts +25 -3
  298. package/src/task/persisted-revive.ts +4 -3
  299. package/src/task/structured-subagent.ts +1 -0
  300. package/src/tiny/title-client.ts +22 -0
  301. package/src/tools/approval.ts +55 -10
  302. package/src/tools/bash-interactive.ts +116 -86
  303. package/src/tools/bash-skill-urls.ts +28 -2
  304. package/src/tools/bash.ts +339 -123
  305. package/src/tools/browser/launch.ts +9 -1
  306. package/src/tools/browser/tab-supervisor.ts +8 -5
  307. package/src/tools/browser.ts +94 -47
  308. package/src/tools/builtin-names.ts +1 -0
  309. package/src/tools/computer/protocol.ts +28 -0
  310. package/src/tools/computer/supervisor.ts +258 -0
  311. package/src/tools/computer/worker-entry.ts +25 -0
  312. package/src/tools/computer/worker.ts +135 -0
  313. package/src/tools/computer-renderer.ts +108 -0
  314. package/src/tools/computer.ts +433 -0
  315. package/src/tools/context.ts +2 -0
  316. package/src/tools/default-renderer.ts +139 -0
  317. package/src/tools/essential-tools.ts +1 -0
  318. package/src/tools/image-gen.ts +20 -28
  319. package/src/tools/image-providers.ts +50 -0
  320. package/src/tools/index.ts +14 -0
  321. package/src/tools/path-utils.ts +49 -2
  322. package/src/tools/read.ts +156 -88
  323. package/src/tools/renderers.ts +9 -1
  324. package/src/tools/todo.ts +101 -11
  325. package/src/tools/vibe.ts +1 -2
  326. package/src/tools/write.ts +51 -1
  327. package/src/tools/xdev.ts +121 -29
  328. package/src/tts/streaming-player.ts +82 -283
  329. package/src/tts/vocalizer.ts +18 -1
  330. package/src/utils/clipboard.ts +1 -30
  331. package/src/utils/jj.ts +125 -4
  332. package/src/utils/lang-from-path.ts +7 -0
  333. package/src/utils/mac-file-urls.applescript +37 -0
  334. package/src/utils/title-generator.ts +137 -1
  335. package/src/utils/tool-choice.ts +14 -0
  336. package/src/utils/tools-manager.ts +1 -19
  337. package/src/vibe/runtime.ts +852 -72
  338. package/src/web/search/index.ts +3 -6
  339. package/src/web/search/provider.ts +31 -16
  340. package/src/web/search/providers/xai.ts +7 -1
  341. package/src/web/search/types.ts +3 -0
  342. package/dist/types/stt/recorder.d.ts +0 -30
  343. package/dist/types/stt/transcriber.d.ts +0 -14
  344. package/dist/types/stt/wav.d.ts +0 -29
  345. package/dist/types/tts/player.d.ts +0 -32
  346. package/src/stt/recorder.ts +0 -551
  347. package/src/stt/transcriber.ts +0 -60
  348. package/src/stt/wav.ts +0 -173
  349. package/src/tts/player.ts +0 -137
@@ -16,6 +16,7 @@ import {
16
16
  resolveActiveProjectRegistryPath,
17
17
  resolveOrDefaultProjectRegistryPath,
18
18
  } from "../discovery/helpers.js";
19
+ import { parseExportArgs } from "../export/html/args";
19
20
  import { shareSession } from "../export/share";
20
21
  import { PluginManager } from "../extensibility/plugins";
21
22
  import {
@@ -32,6 +33,7 @@ import { theme } from "../modes/theme/theme";
32
33
  import type { InteractiveModeContext } from "../modes/types";
33
34
  import { extractLastCodeBlock, extractLastCommand } from "../modes/utils/copy-targets";
34
35
  import type { AgentSession, FreshSessionResult } from "../session/agent-session";
36
+ import type { SessionOAuthAccountList } from "../session/agent-session-types";
35
37
  import { COMPACT_MODES, parseCompactArgs } from "../session/compact-modes";
36
38
  import { resolveResumableSession } from "../session/session-listing";
37
39
  import { formatShakeSummary, type ShakeMode } from "../session/shake-types";
@@ -51,6 +53,7 @@ import { createMarketplaceManager } from "./helpers/marketplace-manager";
51
53
  import { handleMcpAcp } from "./helpers/mcp";
52
54
  import { commandConsumed, errorMessage, parseSlashCommand, parseSubcommand, usage } from "./helpers/parse";
53
55
  import { describeRedeemOutcome, type ResetUsageAccount, toResetUsageAccounts } from "./helpers/reset-usage";
56
+ import { matchSessionPinAccounts, toSessionPinAccounts } from "./helpers/session-pin";
54
57
  import { handleSshAcp } from "./helpers/ssh";
55
58
  import { launchStatsDashboard, parseStatsDashboardArgs } from "./helpers/stats-dashboard";
56
59
  import { handleTodoAcp } from "./helpers/todo";
@@ -87,6 +90,26 @@ function formatFastModeStatus(session: AgentSession): string {
87
90
  return session.isFastModeEnabled() ? "on" : "off";
88
91
  }
89
92
 
93
+ /** `/computer status` label for the session-effective `computer.enabled` value. */
94
+ function formatComputerUseStatus(session: AgentSession): string {
95
+ return session.settings.get("computer.enabled") ? "on" : "off";
96
+ }
97
+
98
+ /**
99
+ * Apply a session-scoped computer-use toggle: flip the active tool slate first
100
+ * (so a failed enable never leaves a stale settings override), then record the
101
+ * runtime override — never `settings.set`, which would persist to settings.json.
102
+ * Returns the operator feedback line.
103
+ */
104
+ async function applyComputerUseToggle(session: AgentSession, enable: boolean): Promise<string> {
105
+ const applied = await session.setComputerToolEnabled(enable);
106
+ if (enable && !applied) {
107
+ return "Computer use is unavailable in this session.";
108
+ }
109
+ session.settings.override("computer.enabled", enable);
110
+ return `Computer use ${enable ? "enabled" : "disabled"} for this session.`;
111
+ }
112
+
90
113
  const AUTOCOMPLETE_DETAIL_LIMIT = 48;
91
114
 
92
115
  function shortDetail(value: string, limit = AUTOCOMPLETE_DETAIL_LIMIT): string {
@@ -195,6 +218,74 @@ async function handleUsageResetCommand(
195
218
  await output(describeRedeemOutcome(outcome, target.label));
196
219
  }
197
220
 
221
+ async function handleSessionPinCommand(
222
+ arg: string,
223
+ session: AgentSession,
224
+ output: SlashCommandRuntime["output"],
225
+ ): Promise<void> {
226
+ if (session.isStreaming) {
227
+ await output("Cannot pin an account while the session is streaming.");
228
+ return;
229
+ }
230
+ let accountList: SessionOAuthAccountList | undefined;
231
+ try {
232
+ accountList = await session.listCurrentProviderOAuthAccounts();
233
+ } catch (error) {
234
+ await output(`Could not load provider accounts: ${errorMessage(error)}`);
235
+ return;
236
+ }
237
+ if (!accountList) {
238
+ await output("Select a model before pinning a provider account.");
239
+ return;
240
+ }
241
+ const provider = getOAuthProviders().find(candidate => candidate.id === accountList.provider);
242
+ const providerName = provider?.name ?? accountList.provider;
243
+ const accounts = toSessionPinAccounts(accountList.accounts);
244
+ if (accounts.length === 0) {
245
+ const source = session.modelRegistry.authStorage.describeCredentialSource(
246
+ accountList.provider,
247
+ session.sessionId,
248
+ );
249
+ await output(
250
+ source
251
+ ? `No stored OAuth accounts for ${providerName}. Current auth comes from ${source}.`
252
+ : `No stored OAuth accounts for ${providerName}. Use /login to add one.`,
253
+ );
254
+ return;
255
+ }
256
+
257
+ const selector = arg.trim();
258
+ if (!selector) {
259
+ const lines = [`OAuth accounts for ${providerName}:`];
260
+ for (const account of accounts) {
261
+ lines.push(`${account.position + 1}. ${account.label}${account.active ? " (active)" : ""}`);
262
+ }
263
+ lines.push("", "Pin one with `/session pin <number|email|account id>`.");
264
+ await output(lines.join("\n"));
265
+ return;
266
+ }
267
+
268
+ const matches = matchSessionPinAccounts(accounts, selector);
269
+ if (matches.length === 0) {
270
+ await output(`No ${providerName} account matches "${selector}".`);
271
+ return;
272
+ }
273
+ if (matches.length > 1) {
274
+ await output(
275
+ `"${selector}" matches multiple ${providerName} accounts: ${matches
276
+ .map(account => `${account.position + 1}. ${account.label}`)
277
+ .join(", ")}. Use the account number.`,
278
+ );
279
+ return;
280
+ }
281
+ const account = matches[0];
282
+ if (!account || !session.pinCurrentProviderOAuthAccount(account.credentialId)) {
283
+ await output(`${account?.label ?? selector} is no longer available to pin.`);
284
+ return;
285
+ }
286
+ await output(`Pinned ${account.label} to this session for ${providerName}.`);
287
+ }
288
+
198
289
  /** Parse the `/shake` subcommand into a {@link ShakeMode}; empty defaults to elide. */
199
290
  function parseShakeMode(args: string): ShakeMode | { error: string } {
200
291
  const verb = args.trim().toLowerCase();
@@ -203,6 +294,14 @@ function parseShakeMode(args: string): ShakeMode | { error: string } {
203
294
  return { error: `Unknown /shake mode "${verb}". Use elide or images.` };
204
295
  }
205
296
 
297
+ /** Format the session's workspace directories (cwd + additional) for display. */
298
+ function formatWorkspaceDirectories(runtime: SlashCommandRuntime, note?: string): string {
299
+ const cwd = runtime.sessionManager.getCwd();
300
+ const additional = runtime.sessionManager.getAdditionalDirectories();
301
+ const lines = ["Workspace directories:", ` ${cwd} (working directory)`, ...additional.map(d => ` ${d}`)];
302
+ return note ? `${note}\n${lines.join("\n")}` : lines.join("\n");
303
+ }
304
+
206
305
  const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
207
306
  {
208
307
  name: "settings",
@@ -463,6 +562,49 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
463
562
  runtime.ctx.editor.setText("");
464
563
  },
465
564
  },
565
+ {
566
+ name: "computer",
567
+ description: "Toggle the native computer-use tool for this session",
568
+ acpDescription: "Toggle computer use",
569
+ acpInputHint: "[on|off|status]",
570
+ subcommands: [
571
+ { name: "on", description: "Enable computer use for this session" },
572
+ { name: "off", description: "Disable computer use for this session" },
573
+ { name: "status", description: "Show computer use status" },
574
+ ],
575
+ allowArgs: true,
576
+ getTuiAutocompleteDescription: runtime => `Computer: ${formatComputerUseStatus(runtime.ctx.session)}`,
577
+ handle: async (command, runtime) => {
578
+ const arg = command.args.trim().toLowerCase();
579
+ if (arg === "status") {
580
+ await runtime.output(`Computer use is ${formatComputerUseStatus(runtime.session)}.`);
581
+ return commandConsumed();
582
+ }
583
+ if (!arg || arg === "toggle" || arg === "on" || arg === "off") {
584
+ const enable = arg === "off" ? false : arg === "on" || !runtime.session.settings.get("computer.enabled");
585
+ await runtime.output(await applyComputerUseToggle(runtime.session, enable));
586
+ return commandConsumed();
587
+ }
588
+ return usage("Usage: /computer [on|off|status]", runtime);
589
+ },
590
+ handleTui: async (command, runtime) => {
591
+ const arg = command.args.trim().toLowerCase();
592
+ if (arg === "status") {
593
+ runtime.ctx.showStatus(`Computer use is ${formatComputerUseStatus(runtime.ctx.session)}.`);
594
+ runtime.ctx.editor.setText("");
595
+ return;
596
+ }
597
+ if (!arg || arg === "toggle" || arg === "on" || arg === "off") {
598
+ const enable =
599
+ arg === "off" ? false : arg === "on" || !runtime.ctx.session.settings.get("computer.enabled");
600
+ runtime.ctx.showStatus(await applyComputerUseToggle(runtime.ctx.session, enable));
601
+ runtime.ctx.editor.setText("");
602
+ return;
603
+ }
604
+ runtime.ctx.showStatus("Usage: /computer [on|off|status]");
605
+ runtime.ctx.editor.setText("");
606
+ },
607
+ },
466
608
  {
467
609
  name: "prewalk",
468
610
  description: "Switch to a fast/cheap model at the next action (works even without --prewalk)",
@@ -606,19 +748,15 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
606
748
  {
607
749
  name: "export",
608
750
  description: "Export session to HTML file",
609
- inlineHint: "[path]",
751
+ inlineHint: "[--themes] [path]",
610
752
  allowArgs: true,
611
753
  handle: async (command, runtime) => {
612
- const arg = command.args.trim();
613
- // Match the interactive `/export` behavior: clipboard aliases are not a
614
- // valid export target. Without this, the literal value (`copy`,
615
- // `--copy`, `clipboard`) is passed to `exportToHtml` and becomes the
616
- // output filename.
617
- if (arg === "--copy" || arg === "clipboard" || arg === "copy") {
618
- return usage("Use /dump to copy the session to clipboard.", runtime);
619
- }
620
754
  try {
621
- const filePath = await runtime.session.exportToHtml(arg || undefined);
755
+ const { outputPath, useUserThemes } = parseExportArgs(command.args);
756
+ if (outputPath === "--copy" || outputPath === "clipboard" || outputPath === "copy") {
757
+ return usage("Use /dump to copy the session to clipboard.", runtime);
758
+ }
759
+ const filePath = await runtime.session.exportToHtml(outputPath, useUserThemes);
622
760
  await runtime.output(`Session exported to: ${filePath}`);
623
761
  return commandConsumed();
624
762
  } catch (err) {
@@ -976,15 +1114,21 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
976
1114
  {
977
1115
  name: "session",
978
1116
  description: "Session management commands",
979
- acpDescription: "Show session information",
980
- acpInputHint: "info|delete",
1117
+ acpDescription: "Show or configure the current session",
1118
+ acpInputHint: "[info|delete|pin [account]]",
981
1119
  subcommands: [
982
1120
  { name: "info", description: "Show session info and stats" },
983
1121
  { name: "delete", description: "Delete current session and return to selector" },
1122
+ {
1123
+ name: "pin",
1124
+ description: "Pin the current provider to a stored OAuth account",
1125
+ usage: "[account]",
1126
+ },
984
1127
  ],
985
1128
  allowArgs: true,
986
1129
  handle: async (command, runtime) => {
987
- if (!command.args || command.args === "info") {
1130
+ const { verb, rest } = parseSubcommand(command.args);
1131
+ if (!verb || (verb === "info" && !rest)) {
988
1132
  await runtime.output(
989
1133
  [
990
1134
  `Session: ${runtime.session.sessionId}`,
@@ -994,7 +1138,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
994
1138
  );
995
1139
  return commandConsumed();
996
1140
  }
997
- if (command.args === "delete") {
1141
+ if (verb === "delete" && !rest) {
998
1142
  if (runtime.session.isStreaming) return usage("Cannot delete the session while streaming.", runtime);
999
1143
  const sessionFile = runtime.sessionManager.getSessionFile();
1000
1144
  if (!sessionFile) return usage("No session file to delete (in-memory session).", runtime);
@@ -1013,17 +1157,34 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1013
1157
  );
1014
1158
  return commandConsumed();
1015
1159
  }
1016
- return usage("Usage: /session [info|delete]", runtime);
1160
+ if (verb === "pin") {
1161
+ await handleSessionPinCommand(rest, runtime.session, runtime.output);
1162
+ return commandConsumed();
1163
+ }
1164
+ return usage("Usage: /session [info|delete|pin [account]]", runtime);
1017
1165
  },
1018
1166
  handleTui: async (command, runtime) => {
1019
- const sub = command.args.trim().toLowerCase() || "info";
1020
- if (sub === "delete") {
1167
+ const { verb, rest } = parseSubcommand(command.args);
1168
+ if (verb === "delete" && !rest) {
1021
1169
  runtime.ctx.editor.setText("");
1022
1170
  await runtime.ctx.handleSessionDeleteCommand();
1023
1171
  return;
1024
1172
  }
1025
- // Default: show session info
1026
- await runtime.ctx.handleSessionCommand();
1173
+ if (verb === "pin") {
1174
+ if (rest) {
1175
+ await handleSessionPinCommand(rest, runtime.ctx.session, text => runtime.ctx.showStatus(text));
1176
+ refreshStatusLine(runtime.ctx);
1177
+ } else {
1178
+ await runtime.ctx.showSessionPinSelector();
1179
+ }
1180
+ runtime.ctx.editor.setText("");
1181
+ return;
1182
+ }
1183
+ if (!verb || (verb === "info" && !rest)) {
1184
+ await runtime.ctx.handleSessionCommand();
1185
+ } else {
1186
+ runtime.ctx.showStatus("Usage: /session [info|delete|pin [account]]");
1187
+ }
1027
1188
  runtime.ctx.editor.setText("");
1028
1189
  },
1029
1190
  },
@@ -1712,7 +1873,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1712
1873
  return usage(`Failed to save pending settings: ${errorMessage(err)}`, runtime);
1713
1874
  }
1714
1875
  try {
1715
- await runtime.sessionManager.moveTo(resolvedPath);
1876
+ await runtime.session.moveSession(resolvedPath);
1716
1877
  } catch (err) {
1717
1878
  return usage(`Move failed: ${errorMessage(err)}`, runtime);
1718
1879
  }
@@ -1733,6 +1894,74 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
1733
1894
  await runtime.ctx.handleMoveCommand(command.args || undefined);
1734
1895
  },
1735
1896
  },
1897
+ {
1898
+ name: "add-dir",
1899
+ description: "Add a workspace directory to this session (multi-root)",
1900
+ acpDescription: "Add a workspace directory to this session",
1901
+ inlineHint: "<path>",
1902
+ allowArgs: true,
1903
+ handle: async (command, runtime) => {
1904
+ if (runtime.session.isStreaming) return usage("Cannot add a directory while streaming.", runtime);
1905
+ if (!command.args) return usage(formatWorkspaceDirectories(runtime, "Usage: /add-dir <path>"), runtime);
1906
+ const resolved = resolveToCwd(command.args, runtime.cwd);
1907
+ try {
1908
+ const stat = await fs.stat(resolved);
1909
+ if (!stat.isDirectory()) return usage(`Not a directory: ${resolved}`, runtime);
1910
+ } catch {
1911
+ return usage(`Directory does not exist: ${resolved}`, runtime);
1912
+ }
1913
+ let added: string | null;
1914
+ try {
1915
+ added = await runtime.sessionManager.addWorkspaceDirectory(resolved);
1916
+ } catch (err) {
1917
+ return usage(errorMessage(err), runtime);
1918
+ }
1919
+ if (added === null) {
1920
+ await runtime.output(`Already in the workspace: ${resolved}`);
1921
+ return commandConsumed();
1922
+ }
1923
+ await runtime.session.refreshBaseSystemPrompt();
1924
+ await runtime.output(formatWorkspaceDirectories(runtime, `Added ${added}.`));
1925
+ return commandConsumed();
1926
+ },
1927
+ },
1928
+ {
1929
+ name: "remove-dir",
1930
+ description: "Remove a workspace directory from this session",
1931
+ acpDescription: "Remove a workspace directory from this session",
1932
+ inlineHint: "<path>",
1933
+ allowArgs: true,
1934
+ handle: async (command, runtime) => {
1935
+ if (runtime.session.isStreaming) return usage("Cannot remove a directory while streaming.", runtime);
1936
+ if (!command.args) return usage("Usage: /remove-dir <path>", runtime);
1937
+ const resolved = resolveToCwd(command.args, runtime.cwd);
1938
+ if (resolved === path.resolve(runtime.cwd)) {
1939
+ return usage("Cannot remove the working directory; use /move to change it.", runtime);
1940
+ }
1941
+ let removed: string | null;
1942
+ try {
1943
+ removed = await runtime.sessionManager.removeWorkspaceDirectory(resolved);
1944
+ } catch (err) {
1945
+ return usage(errorMessage(err), runtime);
1946
+ }
1947
+ if (removed === null) {
1948
+ await runtime.output(`Not a workspace directory: ${resolved}`);
1949
+ return commandConsumed();
1950
+ }
1951
+ await runtime.session.refreshBaseSystemPrompt();
1952
+ await runtime.output(formatWorkspaceDirectories(runtime, `Removed ${removed}.`));
1953
+ return commandConsumed();
1954
+ },
1955
+ },
1956
+ {
1957
+ name: "dirs",
1958
+ description: "List this session's workspace directories",
1959
+ acpDescription: "List this session's workspace directories",
1960
+ handle: async (_command, runtime) => {
1961
+ await runtime.output(formatWorkspaceDirectories(runtime));
1962
+ return commandConsumed();
1963
+ },
1964
+ },
1736
1965
  {
1737
1966
  name: "exit",
1738
1967
  description: "Exit the application",
@@ -2317,6 +2546,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
2317
2546
  if (prompt) return { prompt };
2318
2547
  },
2319
2548
  },
2549
+ {
2550
+ name: "live",
2551
+ description: "Start Codex-backed realtime voice mode",
2552
+ handleTui: async (_command, runtime) => {
2553
+ runtime.ctx.editor.setText("");
2554
+ await runtime.ctx.handleLiveCommand();
2555
+ },
2556
+ },
2320
2557
  {
2321
2558
  name: "pause",
2322
2559
  description: "Freeze all agents (main, subagents, advisor) until resumed",
@@ -0,0 +1,44 @@
1
+ import type { OAuthAccountSummary } from "../../session/auth-storage";
2
+ import { formatActiveAccountLabel } from "./active-oauth-account";
3
+
4
+ /** Stored OAuth account rendered and matched by `/session pin`. */
5
+ export interface SessionPinAccount extends OAuthAccountSummary {
6
+ label: string;
7
+ }
8
+
9
+ /** Add stable user-facing labels to provider account summaries. */
10
+ export function toSessionPinAccounts(accounts: readonly OAuthAccountSummary[]): SessionPinAccount[] {
11
+ return accounts.map(account => {
12
+ const enterpriseUrl = account.enterpriseUrl?.trim();
13
+ return {
14
+ ...account,
15
+ label: (formatActiveAccountLabel(account) ?? enterpriseUrl) || `OAuth credential #${account.credentialId}`,
16
+ };
17
+ });
18
+ }
19
+
20
+ /** Match a `/session pin` selector by 1-based position or exact account identity. */
21
+ export function matchSessionPinAccounts(accounts: readonly SessionPinAccount[], selector: string): SessionPinAccount[] {
22
+ const wanted = selector.trim().toLowerCase();
23
+ if (!wanted) return [];
24
+ if (wanted === "active") return accounts.filter(account => account.active);
25
+
26
+ if (/^\d+$/.test(wanted)) {
27
+ const position = Number(wanted) - 1;
28
+ const positioned = accounts.find(account => account.position === position);
29
+ if (positioned) return [positioned];
30
+ }
31
+
32
+ return accounts.filter(account =>
33
+ [
34
+ account.label,
35
+ account.email,
36
+ account.accountId,
37
+ account.projectId,
38
+ account.enterpriseUrl,
39
+ account.orgId,
40
+ account.orgName,
41
+ `OAuth credential #${account.credentialId}`,
42
+ ].some(value => value?.trim().toLowerCase() === wanted),
43
+ );
44
+ }
@@ -12,6 +12,14 @@ function formatProviderName(provider: string): string {
12
12
  .join(" ");
13
13
  }
14
14
 
15
+ function formatWindowSuffix(label: string, windowLabel: string | undefined): string {
16
+ if (!windowLabel) return "";
17
+ const normalizedLabel = label.toLowerCase();
18
+ const normalizedWindow = windowLabel.toLowerCase();
19
+ if (normalizedWindow === "quota window" || normalizedLabel.includes(normalizedWindow)) return "";
20
+ return ` — ${windowLabel}`;
21
+ }
22
+
15
23
  function formatUsageAmount(limit: UsageLimit): string {
16
24
  const amount = limit.amount;
17
25
  const used = amount.used ?? (amount.usedFraction !== undefined ? amount.usedFraction * 100 : undefined);
@@ -55,6 +63,7 @@ function renderUsageReports(
55
63
  reports: UsageReport[],
56
64
  nowMs: number,
57
65
  resolveActiveAccount?: (provider: string) => OAuthAccountIdentity | undefined,
66
+ usageModelSelectors: readonly string[] = [],
58
67
  ): string {
59
68
  const latestFetchedAt = Math.max(...reports.map(report => report.fetchedAt ?? 0));
60
69
  const lines = [`Usage${latestFetchedAt ? ` (${formatDuration(nowMs - latestFetchedAt)} ago)` : ""}`];
@@ -69,6 +78,11 @@ function renderUsageReports(
69
78
  left.localeCompare(right),
70
79
  )) {
71
80
  lines.push("", formatProviderName(provider));
81
+ const reportingModels = usageModelSelectors.filter(selector => selector.startsWith(`${provider}/`));
82
+ if (reportingModels.length > 0) {
83
+ lines.push(" Models with usage data");
84
+ for (const selector of reportingModels) lines.push(` ${sanitizeText(selector)}`);
85
+ }
72
86
  const activeAccount = resolveActiveAccount?.(provider);
73
87
  // Provider-wide disclaimers render once per provider, not per limit.
74
88
  const providerNotes = [...new Set(providerReports.flatMap(report => report.notes ?? []))];
@@ -118,7 +132,7 @@ function renderUsageReports(
118
132
  limit.scope.tier && !limit.label.toLowerCase().includes(limit.scope.tier.toLowerCase())
119
133
  ? ` (${limit.scope.tier})`
120
134
  : "";
121
- lines.push(`- ${limit.label}${tier}${window ? ` — ${window}` : ""}`);
135
+ lines.push(`- ${limit.label}${tier}${formatWindowSuffix(limit.label, window)}`);
122
136
  lines.push(
123
137
  ` ${formatUsageReportAccount(report, limit, index)}: ${formatUsageAmount(limit)}${inUse ? " ← in use by this session" : ""}`,
124
138
  );
@@ -146,6 +160,7 @@ function renderUsageReports(
146
160
  export async function buildUsageReportText(runtime: SlashCommandRuntime): Promise<string> {
147
161
  const provider = runtime.session as SlashCommandRuntime["session"] & {
148
162
  fetchUsageReports?: () => Promise<UsageReport[] | null>;
163
+ getUsageReportingModelSelectors?: (reports: readonly UsageReport[]) => string[];
149
164
  };
150
165
  if (provider.fetchUsageReports) {
151
166
  const reports = await provider.fetchUsageReports();
@@ -157,8 +172,12 @@ export async function buildUsageReportText(runtime: SlashCommandRuntime): Promis
157
172
  runtime.session.sessionId,
158
173
  )
159
174
  : undefined;
160
- return renderUsageReports(reports, Date.now(), providerId =>
161
- providerId === currentProvider ? activeAccount : undefined,
175
+ const usageModelSelectors = provider.getUsageReportingModelSelectors?.(reports) ?? [];
176
+ return renderUsageReports(
177
+ reports,
178
+ Date.now(),
179
+ providerId => (providerId === currentProvider ? activeAccount : undefined),
180
+ usageModelSelectors,
162
181
  );
163
182
  }
164
183
  }
@@ -4,7 +4,6 @@ import { getTinyModelsCacheDir } from "@oh-my-pi/pi-utils";
4
4
  import { sttClient } from "./asr-client";
5
5
  import type { SttProgressStatus } from "./asr-protocol";
6
6
  import { resolveSttModelSpec } from "./models";
7
- import { ensureRecorder } from "./recorder";
8
7
 
9
8
  export interface DownloadProgress {
10
9
  stage: string;
@@ -129,7 +128,6 @@ export async function downloadSttModel(
129
128
  // ── Public API ─────────────────────────────────────────────────────
130
129
 
131
130
  export async function ensureSTTDependencies(options?: EnsureOptions): Promise<void> {
132
- await ensureRecorder(progress => options?.onProgress?.(progress), options?.signal);
133
131
  await downloadSttModel(
134
132
  resolveSttModelSpec(options?.modelName).key,
135
133
  progress => {
package/src/stt/index.ts CHANGED
@@ -4,5 +4,3 @@ export * from "./downloader";
4
4
  export * from "./models";
5
5
  export * from "./stt-controller";
6
6
  export * from "./submit-trigger";
7
- export * from "./transcriber";
8
- export * from "./wav";