@code-yeongyu/senpi 2026.8.13 → 2026.8.16

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 (514) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/README.md +1 -0
  3. package/dist/cli/args.d.ts +1 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +14 -0
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/auth-command.d.ts.map +1 -1
  8. package/dist/cli/auth-command.js +5 -4
  9. package/dist/cli/auth-command.js.map +1 -1
  10. package/dist/core/agent-session.d.ts +80 -8
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +206 -31
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/auth-storage.d.ts.map +1 -1
  15. package/dist/core/auth-storage.js +19 -8
  16. package/dist/core/auth-storage.js.map +1 -1
  17. package/dist/core/experimental.d.ts +4 -0
  18. package/dist/core/experimental.d.ts.map +1 -1
  19. package/dist/core/experimental.js +4 -0
  20. package/dist/core/experimental.js.map +1 -1
  21. package/dist/core/export-html/template.js +31 -9
  22. package/dist/core/extensions/builtin/claude-sdk-oauth/auth-environment.d.ts +4 -0
  23. package/dist/core/extensions/builtin/claude-sdk-oauth/auth-environment.d.ts.map +1 -0
  24. package/dist/core/extensions/builtin/claude-sdk-oauth/auth-environment.js +30 -0
  25. package/dist/core/extensions/builtin/claude-sdk-oauth/auth-environment.js.map +1 -0
  26. package/dist/core/extensions/builtin/claude-sdk-oauth/auth-lane.d.ts +2 -0
  27. package/dist/core/extensions/builtin/claude-sdk-oauth/auth-lane.d.ts.map +1 -1
  28. package/dist/core/extensions/builtin/claude-sdk-oauth/auth-lane.js +13 -47
  29. package/dist/core/extensions/builtin/claude-sdk-oauth/auth-lane.js.map +1 -1
  30. package/dist/core/extensions/builtin/claude-sdk-oauth/availability.d.ts +18 -1
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/availability.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/availability.js +64 -2
  33. package/dist/core/extensions/builtin/claude-sdk-oauth/availability.js.map +1 -1
  34. package/dist/core/extensions/builtin/claude-sdk-oauth/config-dir-credentials.d.ts +3 -0
  35. package/dist/core/extensions/builtin/claude-sdk-oauth/config-dir-credentials.d.ts.map +1 -0
  36. package/dist/core/extensions/builtin/claude-sdk-oauth/config-dir-credentials.js +27 -0
  37. package/dist/core/extensions/builtin/claude-sdk-oauth/config-dir-credentials.js.map +1 -0
  38. package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.d.ts +15 -2
  39. package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.d.ts.map +1 -1
  40. package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.js +45 -11
  41. package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.js.map +1 -1
  42. package/dist/core/extensions/builtin/claude-sdk-oauth/options.d.ts.map +1 -1
  43. package/dist/core/extensions/builtin/claude-sdk-oauth/options.js +1 -0
  44. package/dist/core/extensions/builtin/claude-sdk-oauth/options.js.map +1 -1
  45. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-wiring.d.ts.map +1 -1
  46. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-wiring.js +3 -1
  47. package/dist/core/extensions/builtin/claude-sdk-oauth/session-registry-wiring.js.map +1 -1
  48. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +1 -0
  49. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
  50. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.d.ts.map +1 -1
  51. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js +17 -1
  52. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js.map +1 -1
  53. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.d.ts.map +1 -1
  54. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js +1 -0
  55. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js.map +1 -1
  56. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/compaction/index.js +225 -75
  58. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  59. package/dist/core/extensions/builtin/compaction/lane-policy.d.ts +2 -3
  60. package/dist/core/extensions/builtin/compaction/lane-policy.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/compaction/lane-policy.js +2 -3
  62. package/dist/core/extensions/builtin/compaction/lane-policy.js.map +1 -1
  63. package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
  64. package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/compaction/log.js +2 -0
  66. package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
  67. package/dist/core/extensions/builtin/compaction/overflow-retry.d.ts.map +1 -1
  68. package/dist/core/extensions/builtin/compaction/overflow-retry.js +38 -4
  69. package/dist/core/extensions/builtin/compaction/overflow-retry.js.map +1 -1
  70. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  71. package/dist/core/extensions/builtin/compaction/speculative.js +2 -1
  72. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  73. package/dist/core/extensions/builtin/compaction/summarization-turn-order.d.ts +33 -0
  74. package/dist/core/extensions/builtin/compaction/summarization-turn-order.d.ts.map +1 -0
  75. package/dist/core/extensions/builtin/compaction/summarization-turn-order.js +67 -0
  76. package/dist/core/extensions/builtin/compaction/summarization-turn-order.js.map +1 -0
  77. package/dist/core/extensions/builtin/config-reload/index.d.ts.map +1 -1
  78. package/dist/core/extensions/builtin/config-reload/index.js +31 -8
  79. package/dist/core/extensions/builtin/config-reload/index.js.map +1 -1
  80. package/dist/core/extensions/builtin/config-reload/routine-settings.d.ts.map +1 -1
  81. package/dist/core/extensions/builtin/config-reload/routine-settings.js +11 -12
  82. package/dist/core/extensions/builtin/config-reload/routine-settings.js.map +1 -1
  83. package/dist/core/extensions/builtin/goal/terminal-provider-error.d.ts.map +1 -1
  84. package/dist/core/extensions/builtin/goal/terminal-provider-error.js +16 -0
  85. package/dist/core/extensions/builtin/goal/terminal-provider-error.js.map +1 -1
  86. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  87. package/dist/core/extensions/builtin/index.js +4 -0
  88. package/dist/core/extensions/builtin/index.js.map +1 -1
  89. package/dist/core/extensions/builtin/mcp/process-tree.js +6 -1
  90. package/dist/core/extensions/builtin/mcp/process-tree.js.map +1 -1
  91. package/dist/core/extensions/builtin/prompt-preset/glm-5-3.d.ts +3 -0
  92. package/dist/core/extensions/builtin/prompt-preset/glm-5-3.d.ts.map +1 -0
  93. package/dist/core/extensions/builtin/prompt-preset/glm-5-3.js +14 -0
  94. package/dist/core/extensions/builtin/prompt-preset/glm-5-3.js.map +1 -0
  95. package/dist/core/extensions/builtin/prompt-preset/grok-4.6.d.ts +3 -0
  96. package/dist/core/extensions/builtin/prompt-preset/grok-4.6.d.ts.map +1 -0
  97. package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js +91 -0
  98. package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js.map +1 -0
  99. package/dist/core/extensions/builtin/prompt-preset/index.d.ts.map +1 -1
  100. package/dist/core/extensions/builtin/prompt-preset/index.js +22 -2
  101. package/dist/core/extensions/builtin/prompt-preset/index.js.map +1 -1
  102. package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
  103. package/dist/core/extensions/builtin/prompt-preset/presets.js +25 -0
  104. package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
  105. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
  106. package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
  107. package/dist/core/extensions/builtin/prompt-preset/settings.js +2 -0
  108. package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
  109. package/dist/core/extensions/builtin/reasoning/index.d.ts +3 -0
  110. package/dist/core/extensions/builtin/reasoning/index.d.ts.map +1 -0
  111. package/dist/core/extensions/builtin/reasoning/index.js +211 -0
  112. package/dist/core/extensions/builtin/reasoning/index.js.map +1 -0
  113. package/dist/core/extensions/builtin/service-tier.d.ts +44 -1
  114. package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
  115. package/dist/core/extensions/builtin/service-tier.js +176 -24
  116. package/dist/core/extensions/builtin/service-tier.js.map +1 -1
  117. package/dist/core/extensions/runner.d.ts.map +1 -1
  118. package/dist/core/extensions/runner.js +4 -4
  119. package/dist/core/extensions/runner.js.map +1 -1
  120. package/dist/core/extensions/types.d.ts +13 -1
  121. package/dist/core/extensions/types.d.ts.map +1 -1
  122. package/dist/core/extensions/types.js.map +1 -1
  123. package/dist/core/keybindings.d.ts +24 -0
  124. package/dist/core/keybindings.d.ts.map +1 -1
  125. package/dist/core/lockfile-policy.d.ts +15 -0
  126. package/dist/core/lockfile-policy.d.ts.map +1 -0
  127. package/dist/core/lockfile-policy.js +15 -0
  128. package/dist/core/lockfile-policy.js.map +1 -0
  129. package/dist/core/model-config.d.ts.map +1 -1
  130. package/dist/core/model-config.js +1 -0
  131. package/dist/core/model-config.js.map +1 -1
  132. package/dist/core/model-resolver.d.ts +34 -7
  133. package/dist/core/model-resolver.d.ts.map +1 -1
  134. package/dist/core/model-resolver.js +115 -27
  135. package/dist/core/model-resolver.js.map +1 -1
  136. package/dist/core/model-runtime.d.ts.map +1 -1
  137. package/dist/core/model-runtime.js +5 -3
  138. package/dist/core/model-runtime.js.map +1 -1
  139. package/dist/core/provider-api-key-auth.d.ts.map +1 -1
  140. package/dist/core/provider-api-key-auth.js +45 -2
  141. package/dist/core/provider-api-key-auth.js.map +1 -1
  142. package/dist/core/provider-composer.d.ts +12 -1
  143. package/dist/core/provider-composer.d.ts.map +1 -1
  144. package/dist/core/provider-composer.js.map +1 -1
  145. package/dist/core/provider-display-names.d.ts.map +1 -1
  146. package/dist/core/provider-display-names.js +1 -0
  147. package/dist/core/provider-display-names.js.map +1 -1
  148. package/dist/core/remote-catalog-provider.d.ts +13 -0
  149. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  150. package/dist/core/remote-catalog-provider.js +15 -0
  151. package/dist/core/remote-catalog-provider.js.map +1 -1
  152. package/dist/core/retry-fallback/controller.d.ts +1 -1
  153. package/dist/core/retry-fallback/controller.d.ts.map +1 -1
  154. package/dist/core/retry-fallback/controller.js +5 -3
  155. package/dist/core/retry-fallback/controller.js.map +1 -1
  156. package/dist/core/retry-fallback/settings.d.ts.map +1 -1
  157. package/dist/core/retry-fallback/settings.js +4 -1
  158. package/dist/core/retry-fallback/settings.js.map +1 -1
  159. package/dist/core/sdk.d.ts +5 -3
  160. package/dist/core/sdk.d.ts.map +1 -1
  161. package/dist/core/sdk.js +14 -8
  162. package/dist/core/sdk.js.map +1 -1
  163. package/dist/core/session-manager.d.ts.map +1 -1
  164. package/dist/core/session-manager.js +20 -4
  165. package/dist/core/session-manager.js.map +1 -1
  166. package/dist/core/settings-manager.d.ts +50 -1
  167. package/dist/core/settings-manager.d.ts.map +1 -1
  168. package/dist/core/settings-manager.js +258 -15
  169. package/dist/core/settings-manager.js.map +1 -1
  170. package/dist/core/system-prompt.js +1 -1
  171. package/dist/core/system-prompt.js.map +1 -1
  172. package/dist/core/thinking-levels.d.ts +22 -0
  173. package/dist/core/thinking-levels.d.ts.map +1 -1
  174. package/dist/core/thinking-levels.js +29 -0
  175. package/dist/core/thinking-levels.js.map +1 -1
  176. package/dist/core/tools/bash.d.ts.map +1 -1
  177. package/dist/core/tools/bash.js +2 -0
  178. package/dist/core/tools/bash.js.map +1 -1
  179. package/dist/core/tools/edit.d.ts.map +1 -1
  180. package/dist/core/tools/edit.js +2 -0
  181. package/dist/core/tools/edit.js.map +1 -1
  182. package/dist/core/tools/find.js +1 -1
  183. package/dist/core/tools/find.js.map +1 -1
  184. package/dist/core/tools/grep.js +1 -1
  185. package/dist/core/tools/grep.js.map +1 -1
  186. package/dist/core/tools/read.d.ts.map +1 -1
  187. package/dist/core/tools/read.js +2 -0
  188. package/dist/core/tools/read.js.map +1 -1
  189. package/dist/core/tools/write.d.ts.map +1 -1
  190. package/dist/core/tools/write.js +2 -0
  191. package/dist/core/tools/write.js.map +1 -1
  192. package/dist/index.d.ts +1 -1
  193. package/dist/index.d.ts.map +1 -1
  194. package/dist/index.js.map +1 -1
  195. package/dist/main.d.ts.map +1 -1
  196. package/dist/main.js +18 -1
  197. package/dist/main.js.map +1 -1
  198. package/dist/modes/interactive/components/extension-selector.d.ts +2 -0
  199. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  200. package/dist/modes/interactive/components/extension-selector.js +17 -1
  201. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  202. package/dist/modes/interactive/components/model-favorites.d.ts +2 -1
  203. package/dist/modes/interactive/components/model-favorites.d.ts.map +1 -1
  204. package/dist/modes/interactive/components/model-favorites.js +72 -6
  205. package/dist/modes/interactive/components/model-favorites.js.map +1 -1
  206. package/dist/modes/interactive/components/model-selector.d.ts +5 -1
  207. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  208. package/dist/modes/interactive/components/model-selector.js +10 -6
  209. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  210. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  211. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  212. package/dist/modes/interactive/components/settings-selector.js +10 -0
  213. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  214. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  215. package/dist/modes/interactive/components/tool-execution.js +22 -1
  216. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  217. package/dist/modes/interactive/interactive-mode.d.ts +14 -4
  218. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  219. package/dist/modes/interactive/interactive-mode.js +109 -50
  220. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  221. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  222. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  223. package/dist/modes/interactive/model-catalog-refresh.js +36 -0
  224. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  225. package/dist/modes/interactive/theme/dark.json +2 -0
  226. package/dist/modes/interactive/theme/grok-day.json +2 -0
  227. package/dist/modes/interactive/theme/grok-night.json +2 -0
  228. package/dist/modes/interactive/theme/light.json +2 -0
  229. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  230. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  231. package/dist/modes/interactive/theme/theme-controller.js +23 -10
  232. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  233. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  234. package/dist/modes/interactive/theme/theme.d.ts +5 -3
  235. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  236. package/dist/modes/interactive/theme/theme.js +12 -2
  237. package/dist/modes/interactive/theme/theme.js.map +1 -1
  238. package/dist/modes/json-event.d.ts +4 -1
  239. package/dist/modes/json-event.d.ts.map +1 -1
  240. package/dist/modes/json-event.js +5 -2
  241. package/dist/modes/json-event.js.map +1 -1
  242. package/dist/modes/rpc/connection-handler.d.ts +10 -0
  243. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  244. package/dist/modes/rpc/connection-handler.js +87 -19
  245. package/dist/modes/rpc/connection-handler.js.map +1 -1
  246. package/dist/modes/rpc/multi-session-host.js +3 -3
  247. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  248. package/dist/modes/rpc/rpc-client.d.ts +26 -1
  249. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  250. package/dist/modes/rpc/rpc-client.js +25 -2
  251. package/dist/modes/rpc/rpc-client.js.map +1 -1
  252. package/dist/modes/rpc/rpc-types.d.ts +60 -0
  253. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  254. package/dist/modes/rpc/rpc-types.js.map +1 -1
  255. package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
  256. package/dist/modes/rpc/session-command-router.js +2 -14
  257. package/dist/modes/rpc/session-command-router.js.map +1 -1
  258. package/dist/modes/rpc/session-event-writer.d.ts +29 -10
  259. package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
  260. package/dist/modes/rpc/session-event-writer.js +229 -50
  261. package/dist/modes/rpc/session-event-writer.js.map +1 -1
  262. package/dist/server/create-harness.d.ts.map +1 -1
  263. package/dist/server/create-harness.js +2 -0
  264. package/dist/server/create-harness.js.map +1 -1
  265. package/dist/utils/shell.d.ts +27 -0
  266. package/dist/utils/shell.d.ts.map +1 -1
  267. package/dist/utils/shell.js +79 -13
  268. package/dist/utils/shell.js.map +1 -1
  269. package/dist/utils/tools-manager.d.ts +10 -1
  270. package/dist/utils/tools-manager.d.ts.map +1 -1
  271. package/dist/utils/tools-manager.js +14 -19
  272. package/dist/utils/tools-manager.js.map +1 -1
  273. package/docs/compaction.md +3 -3
  274. package/docs/custom-provider.md +1 -1
  275. package/docs/environment-variables.md +8 -2
  276. package/docs/extensions.md +13 -1
  277. package/docs/json.md +9 -5
  278. package/docs/keybindings.md +8 -2
  279. package/docs/providers.md +9 -1
  280. package/docs/rpc.md +128 -6
  281. package/docs/sdk.md +9 -0
  282. package/docs/settings.md +93 -1
  283. package/docs/terminal-setup.md +23 -2
  284. package/docs/themes.md +24 -3
  285. package/docs/tui.md +2 -2
  286. package/docs/usage.md +24 -4
  287. package/examples/extensions/subagent/agents.ts +40 -9
  288. package/examples/extensions/subagent/index.ts +26 -3
  289. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +38 -0
  290. package/node_modules/@code-yeongyu/senpi-codemode/README.md +5 -0
  291. package/node_modules/@code-yeongyu/senpi-codemode/package.json +5 -4
  292. package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +14 -1
  293. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/call-capture.ts +64 -0
  294. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-handler.ts +45 -61
  295. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-runtime.ts +6 -0
  296. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-execution-event.ts +201 -0
  297. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool-options.ts +2 -0
  298. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +22 -0
  299. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts +25 -0
  300. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  301. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +77 -12
  302. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  303. package/node_modules/@earendil-works/pi-agent-core/dist/harness/events.d.ts +43 -0
  304. package/node_modules/@earendil-works/pi-agent-core/dist/harness/events.d.ts.map +1 -0
  305. package/node_modules/@earendil-works/pi-agent-core/dist/harness/events.js +67 -0
  306. package/node_modules/@earendil-works/pi-agent-core/dist/harness/events.js.map +1 -0
  307. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.d.ts +4 -2
  308. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.d.ts.map +1 -1
  309. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js +127 -88
  310. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js.map +1 -1
  311. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/errors.d.ts +5 -1
  312. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/errors.d.ts.map +1 -1
  313. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/errors.js +9 -2
  314. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/errors.js.map +1 -1
  315. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/repo.d.ts +12 -3
  316. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/repo.d.ts.map +1 -1
  317. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/repo.js +88 -45
  318. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/repo.js.map +1 -1
  319. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/storage.d.ts +1 -1
  320. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/storage.d.ts.map +1 -1
  321. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/storage.js +28 -19
  322. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/storage.js.map +1 -1
  323. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/types.d.ts +1 -1
  324. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/types.d.ts.map +1 -1
  325. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/types.js.map +1 -1
  326. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory.d.ts +1 -1
  327. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory.d.ts.map +1 -1
  328. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory.js.map +1 -1
  329. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts +6 -1
  330. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts.map +1 -1
  331. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +7 -2
  332. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js.map +1 -1
  333. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/state.d.ts +1 -1
  334. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/state.d.ts.map +1 -1
  335. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/state.js.map +1 -1
  336. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/testing/conformance.d.ts.map +1 -1
  337. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/testing/conformance.js +19 -0
  338. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/testing/conformance.js.map +1 -1
  339. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts +18 -5
  340. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts.map +1 -1
  341. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.js.map +1 -1
  342. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +1 -1
  343. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
  344. package/node_modules/@earendil-works/pi-agent-core/dist/index.js +1 -1
  345. package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
  346. package/node_modules/@earendil-works/pi-agent-core/dist/proxy.d.ts +4 -1
  347. package/node_modules/@earendil-works/pi-agent-core/dist/proxy.d.ts.map +1 -1
  348. package/node_modules/@earendil-works/pi-agent-core/dist/proxy.js.map +1 -1
  349. package/node_modules/@earendil-works/pi-agent-core/dist/search/index.d.ts +21 -0
  350. package/node_modules/@earendil-works/pi-agent-core/dist/search/index.d.ts.map +1 -0
  351. package/node_modules/@earendil-works/pi-agent-core/dist/search/index.js +2 -0
  352. package/node_modules/@earendil-works/pi-agent-core/dist/search/index.js.map +1 -0
  353. package/node_modules/@earendil-works/pi-agent-core/dist/search/scanning.d.ts +29 -0
  354. package/node_modules/@earendil-works/pi-agent-core/dist/search/scanning.d.ts.map +1 -0
  355. package/node_modules/@earendil-works/pi-agent-core/dist/search/scanning.js +103 -0
  356. package/node_modules/@earendil-works/pi-agent-core/dist/search/scanning.js.map +1 -0
  357. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  358. package/node_modules/@earendil-works/pi-ai/README.md +2 -0
  359. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  360. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +40 -14
  361. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  362. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  363. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +16 -4
  364. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
  365. package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare-gateway-binding.d.ts +69 -0
  366. package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare-gateway-binding.d.ts.map +1 -0
  367. package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare-gateway-binding.js +159 -0
  368. package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare-gateway-binding.js.map +1 -0
  369. package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.d.ts +3 -0
  370. package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.d.ts.map +1 -1
  371. package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.js +125 -5
  372. package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.js.map +1 -1
  373. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +7 -3
  374. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js.map +1 -1
  375. package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.d.ts +1 -1
  376. package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.d.ts.map +1 -1
  377. package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js +13 -9
  378. package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js.map +1 -1
  379. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +7 -3
  380. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js.map +1 -1
  381. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.d.ts +1 -1
  382. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.d.ts.map +1 -1
  383. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +208 -42
  384. package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js.map +1 -1
  385. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
  386. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +13 -4
  387. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  388. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  389. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +10 -7
  390. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  391. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts +1 -0
  392. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
  393. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +31 -7
  394. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
  395. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  396. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +8 -1
  397. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  398. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/cursor.d.ts +16 -0
  399. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/cursor.d.ts.map +1 -0
  400. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/cursor.js +234 -0
  401. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/cursor.js.map +1 -0
  402. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.d.ts.map +1 -1
  403. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.js +6 -3
  404. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.js.map +1 -1
  405. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.d.ts +2 -0
  406. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.d.ts.map +1 -1
  407. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.js +5 -0
  408. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.js.map +1 -1
  409. package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +44 -9
  410. package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js.map +1 -1
  411. package/node_modules/@earendil-works/pi-ai/dist/auth/types.d.ts +2 -0
  412. package/node_modules/@earendil-works/pi-ai/dist/auth/types.d.ts.map +1 -1
  413. package/node_modules/@earendil-works/pi-ai/dist/auth/types.js.map +1 -1
  414. package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.d.ts.map +1 -1
  415. package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.js +2 -0
  416. package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.js.map +1 -1
  417. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts +15 -0
  418. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  419. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +15 -0
  420. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
  421. package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts.map +1 -1
  422. package/node_modules/@earendil-works/pi-ai/dist/providers/all.js +2 -0
  423. package/node_modules/@earendil-works/pi-ai/dist/providers/all.js.map +1 -1
  424. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts +15 -0
  425. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts.map +1 -0
  426. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js +32 -0
  427. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js.map +1 -0
  428. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  429. package/node_modules/@earendil-works/pi-ai/dist/providers/data/alibaba-token-plan.json +1 -1
  430. package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
  431. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  432. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  433. package/node_modules/@earendil-works/pi-ai/dist/providers/data/deepseek.json +1 -1
  434. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  435. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  436. package/node_modules/@earendil-works/pi-ai/dist/providers/data/google-vertex.json +1 -1
  437. package/node_modules/@earendil-works/pi-ai/dist/providers/data/google.json +1 -1
  438. package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  439. package/node_modules/@earendil-works/pi-ai/dist/providers/data/kimi-coding.json +1 -1
  440. package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
  441. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
  442. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
  443. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  444. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  445. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  446. package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan-cn.json +1 -1
  447. package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan.json +1 -1
  448. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  449. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
  450. package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
  451. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +14 -3
  452. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  453. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  454. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  455. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +2 -0
  456. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  457. package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.d.ts +2 -0
  458. package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.d.ts.map +1 -0
  459. package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.js +12 -0
  460. package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.js.map +1 -0
  461. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
  462. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js +3 -2
  463. package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js.map +1 -1
  464. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  465. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +1 -0
  466. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  467. package/node_modules/@earendil-works/pi-ai/dist/utils/validation.d.ts.map +1 -1
  468. package/node_modules/@earendil-works/pi-ai/dist/utils/validation.js +34 -0
  469. package/node_modules/@earendil-works/pi-ai/dist/utils/validation.js.map +1 -1
  470. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  471. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  472. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  473. package/node_modules/@earendil-works/pi-tui/README.md +1 -1
  474. package/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.d.ts +26 -0
  475. package/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.d.ts.map +1 -0
  476. package/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.js +121 -0
  477. package/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.js.map +1 -0
  478. package/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.d.ts +6 -1
  479. package/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.d.ts.map +1 -1
  480. package/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.js +23 -7
  481. package/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.js.map +1 -1
  482. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +4 -1
  483. package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
  484. package/node_modules/@earendil-works/pi-tui/dist/index.js +7 -1
  485. package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
  486. package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts +30 -0
  487. package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts.map +1 -1
  488. package/node_modules/@earendil-works/pi-tui/dist/keybindings.js +24 -0
  489. package/node_modules/@earendil-works/pi-tui/dist/keybindings.js.map +1 -1
  490. package/node_modules/@earendil-works/pi-tui/dist/layout.d.ts.map +1 -1
  491. package/node_modules/@earendil-works/pi-tui/dist/layout.js +9 -2
  492. package/node_modules/@earendil-works/pi-tui/dist/layout.js.map +1 -1
  493. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts +8 -2
  494. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  495. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js +6 -2
  496. package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js.map +1 -1
  497. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +6 -0
  498. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  499. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +18 -1
  500. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  501. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.d.ts +21 -0
  502. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.d.ts.map +1 -1
  503. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js +243 -5
  504. package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js.map +1 -1
  505. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +2 -0
  506. package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
  507. package/node_modules/@earendil-works/pi-tui/dist/tui.js +33 -8
  508. package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
  509. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  510. package/package.json +7 -7
  511. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/search.d.ts +0 -19
  512. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/search.d.ts.map +0 -1
  513. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/search.js +0 -41
  514. package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/search.js.map +0 -1
@@ -0,0 +1,211 @@
1
+ import { SettingsManager } from "../../../settings-manager.js";
2
+ import { classifyReasoningCapability } from "../../../thinking-levels.js";
3
+ /**
4
+ * Capability-aware `/reasoning` (the on/off axis) and `/efforts` (the effort ladder).
5
+ *
6
+ * Both commands classify the CURRENT model on every invocation through
7
+ * `classifyReasoningCapability`, so a mid-session model switch is honored immediately and no
8
+ * capability is ever cached. Neither command opens a selector: status is notified as text so both
9
+ * work headless and over RPC.
10
+ */
11
+ /** Effort ladder, ascending. `off` is deliberately absent: it is the /reasoning axis. */
12
+ const EFFORT_LEVELS = ["minimal", "low", "medium", "high", "xhigh", "max"];
13
+ /** Tokens `/efforts` recognizes grammatically; support is then checked per model. */
14
+ const EFFORT_TOKENS = ["off", ...EFFORT_LEVELS];
15
+ const REASONING_ARGUMENTS = ["on", "off"];
16
+ const REASONING_USAGE = "Usage: /reasoning [on|off]";
17
+ const EFFORTS_USAGE = "Usage: /efforts [minimal|low|medium|high|xhigh|max]";
18
+ const DEFAULT_ON_LEVEL = "medium";
19
+ function modelKey(model) {
20
+ return `${model.provider}/${model.id}`;
21
+ }
22
+ function noReasoningMessage(model) {
23
+ return `Model ${modelKey(model)} does not support reasoning.`;
24
+ }
25
+ function onOffOnlyMessage(model) {
26
+ return `Reasoning effort is not configurable for ${modelKey(model)}; this model supports on/off only. Use /reasoning on or /reasoning off.`;
27
+ }
28
+ function reasoningStatus(level) {
29
+ return level === "off" ? "Reasoning: off." : `Reasoning: on (${level}).`;
30
+ }
31
+ function effortsStatus(level, capability) {
32
+ return `Reasoning effort: ${level}. Available: ${capability.nonOffLevels.join(", ")}.`;
33
+ }
34
+ /** The single argument token, `""` for the no-arg form, or `null` when more than one token was given. */
35
+ function parseSingleArgument(args) {
36
+ const trimmed = args.trim();
37
+ if (trimmed.length === 0)
38
+ return "";
39
+ const tokens = trimmed.split(/\s+/);
40
+ return tokens.length === 1 ? (tokens[0] ?? "") : null;
41
+ }
42
+ /**
43
+ * Clamp to the nearest supported non-off level, searching upward first and then downward — the
44
+ * same direction policy the session clamp uses, so a remembered level below a model's floor lands
45
+ * on that floor instead of silently disabling reasoning.
46
+ */
47
+ function clampToNonOff(level, capability) {
48
+ const supported = new Set(capability.nonOffLevels);
49
+ if (supported.has(level))
50
+ return level;
51
+ const index = EFFORT_LEVELS.indexOf(level);
52
+ if (index === -1)
53
+ return capability.nonOffLevels[0];
54
+ for (let i = index; i < EFFORT_LEVELS.length; i++) {
55
+ const candidate = EFFORT_LEVELS[i];
56
+ if (candidate && supported.has(candidate))
57
+ return candidate;
58
+ }
59
+ for (let i = index - 1; i >= 0; i--) {
60
+ const candidate = EFFORT_LEVELS[i];
61
+ if (candidate && supported.has(candidate))
62
+ return candidate;
63
+ }
64
+ return capability.nonOffLevels[0];
65
+ }
66
+ /**
67
+ * Level to restore when reasoning is switched back on: this model's durable last non-off level,
68
+ * else a legacy non-off effective memory, else the global default when that is not "off", else
69
+ * medium — always clamped to what the model supports.
70
+ */
71
+ function resolvePreferredOnLevel(ctx, model, capability) {
72
+ const settingsManager = SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });
73
+ const lastOnLevel = settingsManager.getModelLastOnThinkingLevel(model.provider, model.id);
74
+ const remembered = settingsManager.getModelThinkingLevel(model.provider, model.id);
75
+ const globalDefault = settingsManager.getDefaultThinkingLevel();
76
+ const preferred = lastOnLevel ?? nonOff(remembered) ?? nonOff(globalDefault) ?? DEFAULT_ON_LEVEL;
77
+ return clampToNonOff(preferred, capability);
78
+ }
79
+ function createSettingsManager(ctx) {
80
+ return SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });
81
+ }
82
+ function nonOff(level) {
83
+ return level !== undefined && level !== "off" ? level : undefined;
84
+ }
85
+ function toCompletions(values, prefix) {
86
+ const normalizedPrefix = prefix.trim();
87
+ const matches = values.filter((value) => value.startsWith(normalizedPrefix));
88
+ return matches.length > 0 ? matches.map((value) => ({ value, label: value })) : null;
89
+ }
90
+ export default function reasoningExtension(pi) {
91
+ // Completions run outside a command context, which carries no model, so the live model is
92
+ // tracked from the events that change it. Handlers always read ctx.model instead.
93
+ let currentModel;
94
+ pi.on("session_start", (_event, ctx) => {
95
+ currentModel = ctx.model;
96
+ });
97
+ pi.on("model_select", (event) => {
98
+ currentModel = event.model;
99
+ });
100
+ pi.registerCommand("reasoning", {
101
+ description: "Show or toggle reasoning for the current model",
102
+ argumentHint: "[on|off]",
103
+ getArgumentCompletions: (prefix) => toCompletions(REASONING_ARGUMENTS, prefix),
104
+ handler: async (args, ctx) => {
105
+ const model = ctx.model;
106
+ if (!model) {
107
+ ctx.ui.notify("No model is active.", "error");
108
+ return;
109
+ }
110
+ currentModel = model;
111
+ const argument = parseSingleArgument(args);
112
+ if (argument === null || (argument !== "" && !REASONING_ARGUMENTS.includes(argument))) {
113
+ ctx.ui.notify(REASONING_USAGE, "error");
114
+ return;
115
+ }
116
+ const capability = classifyReasoningCapability(model);
117
+ const currentLevel = pi.getThinkingLevel();
118
+ if (argument === "") {
119
+ ctx.ui.notify(reasoningStatus(currentLevel), "info");
120
+ return;
121
+ }
122
+ if (argument === "off") {
123
+ if (capability.kind === "none") {
124
+ // Nothing to disable; the model is already, permanently, off.
125
+ ctx.ui.notify(reasoningStatus("off"), "info");
126
+ return;
127
+ }
128
+ if (capability.kind === "always-on") {
129
+ ctx.ui.notify(`Reasoning cannot be disabled for ${modelKey(model)}.`, "error");
130
+ return;
131
+ }
132
+ if (currentLevel !== "off") {
133
+ const settingsManager = createSettingsManager(ctx);
134
+ settingsManager.setModelLastOnThinkingLevel(model.provider, model.id, currentLevel);
135
+ await settingsManager.flush();
136
+ }
137
+ pi.setThinkingLevel("off");
138
+ ctx.ui.notify(reasoningStatus(pi.getThinkingLevel()), "info");
139
+ return;
140
+ }
141
+ if (capability.kind === "none") {
142
+ ctx.ui.notify(noReasoningMessage(model), "error");
143
+ return;
144
+ }
145
+ // Already reasoning: re-persist the current level instead of overriding the user's choice.
146
+ if (currentLevel !== "off") {
147
+ pi.setThinkingLevel(currentLevel);
148
+ ctx.ui.notify(reasoningStatus(pi.getThinkingLevel()), "info");
149
+ return;
150
+ }
151
+ const target = resolvePreferredOnLevel(ctx, model, capability);
152
+ if (target === undefined) {
153
+ ctx.ui.notify(noReasoningMessage(model), "error");
154
+ return;
155
+ }
156
+ // Apply the clamped level to the session, but persist it without rewriting a stale raw
157
+ // last-on preference. If catalog support returns later, the original preference still wins.
158
+ pi.setSessionThinkingLevel(target);
159
+ const settingsManager = createSettingsManager(ctx);
160
+ settingsManager.setModelThinkingLevel(model.provider, model.id, target, { preserveLastOn: true });
161
+ settingsManager.setDefaultThinkingLevel(target);
162
+ await settingsManager.flush();
163
+ ctx.ui.notify(reasoningStatus(pi.getThinkingLevel()), "info");
164
+ },
165
+ });
166
+ pi.registerCommand("efforts", {
167
+ description: "Show or set the reasoning effort for the current model",
168
+ argumentHint: "[minimal|low|medium|high|xhigh|max]",
169
+ getArgumentCompletions: (prefix) => {
170
+ // Only a graded model has an effort ladder worth completing.
171
+ const capability = currentModel ? classifyReasoningCapability(currentModel) : undefined;
172
+ if (capability?.kind !== "graded")
173
+ return null;
174
+ return toCompletions(capability.nonOffLevels, prefix);
175
+ },
176
+ handler: async (args, ctx) => {
177
+ const model = ctx.model;
178
+ if (!model) {
179
+ ctx.ui.notify("No model is active.", "error");
180
+ return;
181
+ }
182
+ currentModel = model;
183
+ const argument = parseSingleArgument(args);
184
+ if (argument === null || (argument !== "" && !EFFORT_TOKENS.includes(argument))) {
185
+ ctx.ui.notify(EFFORTS_USAGE, "error");
186
+ return;
187
+ }
188
+ const capability = classifyReasoningCapability(model);
189
+ if (capability.kind === "none") {
190
+ ctx.ui.notify(noReasoningMessage(model), "error");
191
+ return;
192
+ }
193
+ if (capability.kind === "on-off") {
194
+ ctx.ui.notify(onOffOnlyMessage(model), "error");
195
+ return;
196
+ }
197
+ if (argument === "") {
198
+ ctx.ui.notify(effortsStatus(pi.getThinkingLevel(), capability), "info");
199
+ return;
200
+ }
201
+ const level = argument;
202
+ if (!capability.nonOffLevels.includes(level)) {
203
+ ctx.ui.notify(`Reasoning effort "${level}" is not supported by ${modelKey(model)}. Available: ${capability.nonOffLevels.join(", ")}.`, "error");
204
+ return;
205
+ }
206
+ pi.setThinkingLevel(level);
207
+ ctx.ui.notify(effortsStatus(pi.getThinkingLevel(), capability), "info");
208
+ },
209
+ });
210
+ }
211
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/reasoning/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport type { AutocompleteItem } from \"@earendil-works/pi-tui\";\nimport { SettingsManager } from \"../../../settings-manager.ts\";\nimport { classifyReasoningCapability, type ReasoningCapability } from \"../../../thinking-levels.ts\";\nimport type { ExtensionAPI, ExtensionCommandContext } from \"../../types.ts\";\n\n/**\n * Capability-aware `/reasoning` (the on/off axis) and `/efforts` (the effort ladder).\n *\n * Both commands classify the CURRENT model on every invocation through\n * `classifyReasoningCapability`, so a mid-session model switch is honored immediately and no\n * capability is ever cached. Neither command opens a selector: status is notified as text so both\n * work headless and over RPC.\n */\n\n/** Effort ladder, ascending. `off` is deliberately absent: it is the /reasoning axis. */\nconst EFFORT_LEVELS: readonly ThinkingLevel[] = [\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n/** Tokens `/efforts` recognizes grammatically; support is then checked per model. */\nconst EFFORT_TOKENS: readonly string[] = [\"off\", ...EFFORT_LEVELS];\nconst REASONING_ARGUMENTS = [\"on\", \"off\"] as const;\nconst REASONING_USAGE = \"Usage: /reasoning [on|off]\";\nconst EFFORTS_USAGE = \"Usage: /efforts [minimal|low|medium|high|xhigh|max]\";\nconst DEFAULT_ON_LEVEL: ThinkingLevel = \"medium\";\n\ntype ReasoningArgument = (typeof REASONING_ARGUMENTS)[number];\n\nfunction modelKey(model: Model<Api>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction noReasoningMessage(model: Model<Api>): string {\n\treturn `Model ${modelKey(model)} does not support reasoning.`;\n}\n\nfunction onOffOnlyMessage(model: Model<Api>): string {\n\treturn `Reasoning effort is not configurable for ${modelKey(model)}; this model supports on/off only. Use /reasoning on or /reasoning off.`;\n}\n\nfunction reasoningStatus(level: ThinkingLevel): string {\n\treturn level === \"off\" ? \"Reasoning: off.\" : `Reasoning: on (${level}).`;\n}\n\nfunction effortsStatus(level: ThinkingLevel, capability: ReasoningCapability): string {\n\treturn `Reasoning effort: ${level}. Available: ${capability.nonOffLevels.join(\", \")}.`;\n}\n\n/** The single argument token, `\"\"` for the no-arg form, or `null` when more than one token was given. */\nfunction parseSingleArgument(args: string): string | null {\n\tconst trimmed = args.trim();\n\tif (trimmed.length === 0) return \"\";\n\tconst tokens = trimmed.split(/\\s+/);\n\treturn tokens.length === 1 ? (tokens[0] ?? \"\") : null;\n}\n\n/**\n * Clamp to the nearest supported non-off level, searching upward first and then downward — the\n * same direction policy the session clamp uses, so a remembered level below a model's floor lands\n * on that floor instead of silently disabling reasoning.\n */\nfunction clampToNonOff(level: ThinkingLevel, capability: ReasoningCapability): ThinkingLevel | undefined {\n\tconst supported = new Set(capability.nonOffLevels);\n\tif (supported.has(level)) return level;\n\tconst index = EFFORT_LEVELS.indexOf(level);\n\tif (index === -1) return capability.nonOffLevels[0];\n\tfor (let i = index; i < EFFORT_LEVELS.length; i++) {\n\t\tconst candidate = EFFORT_LEVELS[i];\n\t\tif (candidate && supported.has(candidate)) return candidate;\n\t}\n\tfor (let i = index - 1; i >= 0; i--) {\n\t\tconst candidate = EFFORT_LEVELS[i];\n\t\tif (candidate && supported.has(candidate)) return candidate;\n\t}\n\treturn capability.nonOffLevels[0];\n}\n\n/**\n * Level to restore when reasoning is switched back on: this model's durable last non-off level,\n * else a legacy non-off effective memory, else the global default when that is not \"off\", else\n * medium — always clamped to what the model supports.\n */\nfunction resolvePreferredOnLevel(\n\tctx: ExtensionCommandContext,\n\tmodel: Model<Api>,\n\tcapability: ReasoningCapability,\n): ThinkingLevel | undefined {\n\tconst settingsManager = SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });\n\tconst lastOnLevel = settingsManager.getModelLastOnThinkingLevel(model.provider, model.id);\n\tconst remembered = settingsManager.getModelThinkingLevel(model.provider, model.id);\n\tconst globalDefault = settingsManager.getDefaultThinkingLevel();\n\tconst preferred = lastOnLevel ?? nonOff(remembered) ?? nonOff(globalDefault) ?? DEFAULT_ON_LEVEL;\n\treturn clampToNonOff(preferred, capability);\n}\n\nfunction createSettingsManager(ctx: ExtensionCommandContext): SettingsManager {\n\treturn SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });\n}\n\nfunction nonOff(level: ThinkingLevel | undefined): ThinkingLevel | undefined {\n\treturn level !== undefined && level !== \"off\" ? level : undefined;\n}\n\nfunction toCompletions(values: readonly string[], prefix: string): AutocompleteItem[] | null {\n\tconst normalizedPrefix = prefix.trim();\n\tconst matches = values.filter((value) => value.startsWith(normalizedPrefix));\n\treturn matches.length > 0 ? matches.map((value) => ({ value, label: value })) : null;\n}\n\nexport default function reasoningExtension(pi: ExtensionAPI): void {\n\t// Completions run outside a command context, which carries no model, so the live model is\n\t// tracked from the events that change it. Handlers always read ctx.model instead.\n\tlet currentModel: Model<Api> | undefined;\n\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tcurrentModel = ctx.model;\n\t});\n\tpi.on(\"model_select\", (event) => {\n\t\tcurrentModel = event.model;\n\t});\n\n\tpi.registerCommand(\"reasoning\", {\n\t\tdescription: \"Show or toggle reasoning for the current model\",\n\t\targumentHint: \"[on|off]\",\n\t\tgetArgumentCompletions: (prefix) => toCompletions(REASONING_ARGUMENTS, prefix),\n\t\thandler: async (args, ctx) => {\n\t\t\tconst model = ctx.model;\n\t\t\tif (!model) {\n\t\t\t\tctx.ui.notify(\"No model is active.\", \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcurrentModel = model;\n\n\t\t\tconst argument = parseSingleArgument(args);\n\t\t\tif (argument === null || (argument !== \"\" && !REASONING_ARGUMENTS.includes(argument as ReasoningArgument))) {\n\t\t\t\tctx.ui.notify(REASONING_USAGE, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst capability = classifyReasoningCapability(model);\n\t\t\tconst currentLevel = pi.getThinkingLevel();\n\n\t\t\tif (argument === \"\") {\n\t\t\t\tctx.ui.notify(reasoningStatus(currentLevel), \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (argument === \"off\") {\n\t\t\t\tif (capability.kind === \"none\") {\n\t\t\t\t\t// Nothing to disable; the model is already, permanently, off.\n\t\t\t\t\tctx.ui.notify(reasoningStatus(\"off\"), \"info\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (capability.kind === \"always-on\") {\n\t\t\t\t\tctx.ui.notify(`Reasoning cannot be disabled for ${modelKey(model)}.`, \"error\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (currentLevel !== \"off\") {\n\t\t\t\t\tconst settingsManager = createSettingsManager(ctx);\n\t\t\t\t\tsettingsManager.setModelLastOnThinkingLevel(model.provider, model.id, currentLevel);\n\t\t\t\t\tawait settingsManager.flush();\n\t\t\t\t}\n\t\t\t\tpi.setThinkingLevel(\"off\");\n\t\t\t\tctx.ui.notify(reasoningStatus(pi.getThinkingLevel()), \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (capability.kind === \"none\") {\n\t\t\t\tctx.ui.notify(noReasoningMessage(model), \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Already reasoning: re-persist the current level instead of overriding the user's choice.\n\t\t\tif (currentLevel !== \"off\") {\n\t\t\t\tpi.setThinkingLevel(currentLevel);\n\t\t\t\tctx.ui.notify(reasoningStatus(pi.getThinkingLevel()), \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst target = resolvePreferredOnLevel(ctx, model, capability);\n\t\t\tif (target === undefined) {\n\t\t\t\tctx.ui.notify(noReasoningMessage(model), \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Apply the clamped level to the session, but persist it without rewriting a stale raw\n\t\t\t// last-on preference. If catalog support returns later, the original preference still wins.\n\t\t\tpi.setSessionThinkingLevel(target);\n\t\t\tconst settingsManager = createSettingsManager(ctx);\n\t\t\tsettingsManager.setModelThinkingLevel(model.provider, model.id, target, { preserveLastOn: true });\n\t\t\tsettingsManager.setDefaultThinkingLevel(target);\n\t\t\tawait settingsManager.flush();\n\t\t\tctx.ui.notify(reasoningStatus(pi.getThinkingLevel()), \"info\");\n\t\t},\n\t});\n\n\tpi.registerCommand(\"efforts\", {\n\t\tdescription: \"Show or set the reasoning effort for the current model\",\n\t\targumentHint: \"[minimal|low|medium|high|xhigh|max]\",\n\t\tgetArgumentCompletions: (prefix) => {\n\t\t\t// Only a graded model has an effort ladder worth completing.\n\t\t\tconst capability = currentModel ? classifyReasoningCapability(currentModel) : undefined;\n\t\t\tif (capability?.kind !== \"graded\") return null;\n\t\t\treturn toCompletions(capability.nonOffLevels, prefix);\n\t\t},\n\t\thandler: async (args, ctx) => {\n\t\t\tconst model = ctx.model;\n\t\t\tif (!model) {\n\t\t\t\tctx.ui.notify(\"No model is active.\", \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tcurrentModel = model;\n\n\t\t\tconst argument = parseSingleArgument(args);\n\t\t\tif (argument === null || (argument !== \"\" && !EFFORT_TOKENS.includes(argument))) {\n\t\t\t\tctx.ui.notify(EFFORTS_USAGE, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst capability = classifyReasoningCapability(model);\n\t\t\tif (capability.kind === \"none\") {\n\t\t\t\tctx.ui.notify(noReasoningMessage(model), \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (capability.kind === \"on-off\") {\n\t\t\t\tctx.ui.notify(onOffOnlyMessage(model), \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (argument === \"\") {\n\t\t\t\tctx.ui.notify(effortsStatus(pi.getThinkingLevel(), capability), \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst level = argument as ThinkingLevel;\n\t\t\tif (!capability.nonOffLevels.includes(level)) {\n\t\t\t\tctx.ui.notify(\n\t\t\t\t\t`Reasoning effort \"${level}\" is not supported by ${modelKey(model)}. Available: ${capability.nonOffLevels.join(\", \")}.`,\n\t\t\t\t\t\"error\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tpi.setThinkingLevel(level);\n\t\t\tctx.ui.notify(effortsStatus(pi.getThinkingLevel(), capability), \"info\");\n\t\t},\n\t});\n}\n"]}
@@ -1,6 +1,49 @@
1
- import type { Api } from "@earendil-works/pi-ai";
1
+ import type { Api, Model } from "@earendil-works/pi-ai";
2
+ import type { ModelRegistry } from "../../model-registry.ts";
3
+ import { SettingsManager } from "../../settings-manager.ts";
2
4
  import type { ExtensionAPI, ServiceTier } from "../types.ts";
3
5
  export type { ServiceTier };
6
+ export declare const CODEX_RESPONSES_API = "openai-codex-responses";
4
7
  export declare function addServiceTierToPayload(api: Api | undefined, payload: unknown, serviceTier?: ServiceTier): unknown;
8
+ /**
9
+ * The model that owns this model's persisted service-tier preference.
10
+ *
11
+ * A `-fast` catalog variant and its base model are one model to the user (`/fast` just swaps
12
+ * between them), so both must read and write ONE key. Anything else can hold contradictory
13
+ * preferences for the same choice.
14
+ */
15
+ export declare function resolveServiceTierMemoryModel(modelRegistry: ModelRegistry, model: Model<Api>): Model<Api>;
16
+ /** Remembered service tier for this model, normalized onto its base-model key. */
17
+ export declare function getRememberedServiceTier(settingsManager: SettingsManager, modelRegistry: ModelRegistry, model: Model<Api>): ServiceTier | undefined;
18
+ /** Host capabilities `applyFastMode` needs; satisfied by an extension context plus `pi`. */
19
+ export interface FastModeContext {
20
+ cwd: string;
21
+ agentDir: string;
22
+ model: Model<Api> | undefined;
23
+ modelRegistry: ModelRegistry;
24
+ /** Tier the host resolved for the active model (scoped/favorite pin or catalog). */
25
+ serviceTier: ServiceTier | undefined;
26
+ isProjectTrusted(): boolean;
27
+ notify(message: string, level: "info" | "warning" | "error"): void;
28
+ setSessionModel(model: Model<Api>): Promise<boolean>;
29
+ setSessionFastMode(enabled: boolean): void;
30
+ }
31
+ export interface FastModeResult {
32
+ /** Fast mode state after the call; unchanged when the request was refused. */
33
+ enabled: boolean;
34
+ /** False when the request was refused (non-Codex model, active pin, failed model switch). */
35
+ applied: boolean;
36
+ /** The just-persisted tier (or the unchanged one when refused); `"priority"` on, `"auto"` off. */
37
+ recordedTier: ServiceTier;
38
+ message: string;
39
+ }
40
+ /**
41
+ * Single entry point for turning fast mode on/off: the `/fast` command and any other surface
42
+ * (RPC) must go through this so persistence and `-fast` key normalization exist exactly once.
43
+ *
44
+ * `off` persists an explicit `"auto"` rather than deleting the key: deletion re-inherits the
45
+ * catalog/global tier, which is what the user just asked to turn off.
46
+ */
47
+ export declare function applyFastMode(ctx: FastModeContext, enabled: boolean): Promise<FastModeResult>;
5
48
  export default function serviceTierExtension(pi: ExtensionAPI): void;
6
49
  //# sourceMappingURL=service-tier.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"service-tier.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/builtin/service-tier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAS,MAAM,uBAAuB,CAAC;AAGxD,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE7D,YAAY,EAAE,WAAW,EAAE,CAAC;AAa5B,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAalH;AAkCD,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAqEnE"}
1
+ {"version":3,"file":"service-tier.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/builtin/service-tier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAA2B,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtF,YAAY,EAAE,WAAW,EAAE,CAAC;AAK5B,eAAO,MAAM,mBAAmB,2BAA6B,CAAC;AAa9D,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAalH;AAkCD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,aAAa,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAEzG;AAED,kFAAkF;AAClF,wBAAgB,wBAAwB,CACvC,eAAe,EAAE,eAAe,EAChC,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GACf,WAAW,GAAG,SAAS,CAGzB;AAED,4FAA4F;AAC5F,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,oFAAoF;IACpF,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;IACrC,gBAAgB,IAAI,OAAO,CAAC;IAC5B,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,CAAC;IACnE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,cAAc;IAC9B,8EAA8E;IAC9E,OAAO,EAAE,OAAO,CAAC;IACjB,6FAA6F;IAC7F,OAAO,EAAE,OAAO,CAAC;IACjB,kGAAkG;IAClG,YAAY,EAAE,WAAW,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CAChB;AAeD;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAsCnG;AAqBD,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAmInE"}
@@ -1,8 +1,11 @@
1
1
  import { SettingsManager } from "../../settings-manager.js";
2
2
  const OPENAI_CODEX_RESPONSES_API = "openai-codex-responses";
3
+ export const CODEX_RESPONSES_API = OPENAI_CODEX_RESPONSES_API;
3
4
  const FAST_MODEL_SUFFIX = "-fast";
4
5
  const PRIORITY_TIER = "priority";
5
6
  const SERVICE_TIER_APIS = new Set(["openai-responses", OPENAI_CODEX_RESPONSES_API]);
7
+ const FAST_ARGUMENTS = ["on", "off"];
8
+ const FAST_USAGE = "Usage: /fast [on|off]";
6
9
  function isRecord(value) {
7
10
  return typeof value === "object" && value !== null;
8
11
  }
@@ -42,6 +45,93 @@ function findFastModel(modelRegistry, baseModel) {
42
45
  const fastModel = modelRegistry.find(baseModel.provider, `${baseModel.id}${FAST_MODEL_SUFFIX}`);
43
46
  return fastModel && isCompatibleFastVariant(modelRegistry, baseModel, fastModel) ? fastModel : undefined;
44
47
  }
48
+ /**
49
+ * The model that owns this model's persisted service-tier preference.
50
+ *
51
+ * A `-fast` catalog variant and its base model are one model to the user (`/fast` just swaps
52
+ * between them), so both must read and write ONE key. Anything else can hold contradictory
53
+ * preferences for the same choice.
54
+ */
55
+ export function resolveServiceTierMemoryModel(modelRegistry, model) {
56
+ return findBaseModel(modelRegistry, model) ?? model;
57
+ }
58
+ /** Remembered service tier for this model, normalized onto its base-model key. */
59
+ export function getRememberedServiceTier(settingsManager, modelRegistry, model) {
60
+ const memoryModel = resolveServiceTierMemoryModel(modelRegistry, model);
61
+ return settingsManager.getModelServiceTier(memoryModel.provider, memoryModel.id);
62
+ }
63
+ /**
64
+ * Whether the active model's priority tier is PINNED by the user's model selection (a scoped
65
+ * or favorite `provider/id:priority` pattern) rather than inherited from the catalog.
66
+ *
67
+ * `ctx.serviceTier` merges both sources, so a priority the catalog does not explain must have
68
+ * come from a `:priority` decorator; checking `scopedModels` alone would miss favorite pins,
69
+ * which the session resolves the same way.
70
+ */
71
+ function hasPriorityPin(ctx, model) {
72
+ if (ctx.serviceTier !== PRIORITY_TIER)
73
+ return false;
74
+ return ctx.modelRegistry.getServiceTier(model) !== PRIORITY_TIER;
75
+ }
76
+ /**
77
+ * Single entry point for turning fast mode on/off: the `/fast` command and any other surface
78
+ * (RPC) must go through this so persistence and `-fast` key normalization exist exactly once.
79
+ *
80
+ * `off` persists an explicit `"auto"` rather than deleting the key: deletion re-inherits the
81
+ * catalog/global tier, which is what the user just asked to turn off.
82
+ */
83
+ export async function applyFastMode(ctx, enabled) {
84
+ const model = ctx.model;
85
+ if (model?.api !== OPENAI_CODEX_RESPONSES_API) {
86
+ const message = "Fast mode is only available for OpenAI Codex models.";
87
+ ctx.notify(message, "warning");
88
+ return { enabled: false, applied: false, recordedTier: "auto", message };
89
+ }
90
+ if (!enabled && hasPriorityPin(ctx, model)) {
91
+ // The pin outranks the memory, so writing "auto" here would be a silent no-op on the wire.
92
+ const message = "Fast mode is fixed by the active model selection's priority tier.";
93
+ ctx.notify(message, "info");
94
+ // The pin keeps the model on priority no matter what the memory says, so that is the honest
95
+ // tier to report; nothing was written, and no caller reads this field when `applied` is false.
96
+ return { enabled: true, applied: false, recordedTier: PRIORITY_TIER, message };
97
+ }
98
+ const settingsManager = SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });
99
+ const memoryModel = resolveServiceTierMemoryModel(ctx.modelRegistry, model);
100
+ const tier = enabled ? PRIORITY_TIER : "auto";
101
+ settingsManager.setModelServiceTier(memoryModel.provider, memoryModel.id, tier);
102
+ await settingsManager.flush();
103
+ const baseModel = findBaseModel(ctx.modelRegistry, model);
104
+ const targetModel = enabled ? findFastModel(ctx.modelRegistry, model) : baseModel;
105
+ if (targetModel) {
106
+ if (!(await ctx.setSessionModel(targetModel))) {
107
+ const message = `Could not switch to ${targetModel.provider}/${targetModel.id}.`;
108
+ ctx.notify(message, "error");
109
+ return { enabled: baseModel === undefined, applied: false, recordedTier: "auto", message };
110
+ }
111
+ }
112
+ ctx.setSessionFastMode(enabled);
113
+ const labelModel = targetModel ?? model;
114
+ const message = `Fast mode ${enabled ? "enabled" : "disabled"}: ${labelModel.id}`;
115
+ ctx.notify(message, "info");
116
+ return { enabled, applied: true, recordedTier: tier, message };
117
+ }
118
+ function toCompletions(values, prefix) {
119
+ const matches = values.filter((value) => value.startsWith(prefix.trim()));
120
+ return matches.length > 0 ? matches.map((value) => ({ value, label: value })) : null;
121
+ }
122
+ function toFastModeContext(pi, ctx) {
123
+ return {
124
+ cwd: ctx.cwd,
125
+ agentDir: ctx.agentDir,
126
+ model: ctx.model,
127
+ modelRegistry: ctx.modelRegistry,
128
+ serviceTier: ctx.serviceTier,
129
+ isProjectTrusted: () => ctx.isProjectTrusted(),
130
+ notify: (message, level) => ctx.ui.notify(message, level),
131
+ setSessionModel: (model) => pi.setSessionModel(model),
132
+ setSessionFastMode: (enabled) => pi.setSessionFastMode(enabled),
133
+ };
134
+ }
45
135
  export default function serviceTierExtension(pi) {
46
136
  let settingsServiceTier;
47
137
  /**
@@ -58,48 +148,110 @@ export default function serviceTierExtension(pi) {
58
148
  * sent — so this toggle drives the request field directly. See issue #545.
59
149
  */
60
150
  let sessionFastMode = false;
151
+ /**
152
+ * The per-model memory tier `/fast` last wrote or read for the active base model this
153
+ * session. Lives only to suppress an inherited priority when the user turns fast off in
154
+ * the SAME session: the host's resolved `ctx.serviceTier` is recomputed on model switch,
155
+ * not on a memory write, so without this an in-session `/fast off` (no model swap) would
156
+ * keep sending `priority` until a restart. Scoped to the active base key (re-derived on
157
+ * model_select for the incoming model) so it never leaks across models.
158
+ */
159
+ let liveMemoryTier;
160
+ let liveMemoryKey;
61
161
  pi.on("session_start", async (_event, ctx) => {
62
- const settingsManager = SettingsManager.create(ctx.cwd);
162
+ const settingsManager = SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });
63
163
  settingsServiceTier = settingsManager.getOpenAIServiceTier();
64
- sessionFastMode = false;
65
- pi.setSessionFastMode(false);
66
164
  const model = ctx.model;
67
165
  if (model?.api !== OPENAI_CODEX_RESPONSES_API) {
166
+ sessionFastMode = false;
167
+ pi.setSessionFastMode(false);
68
168
  return;
69
169
  }
170
+ // Fast mode is remembered per model, so a new session inherits the user's last choice for
171
+ // THIS model instead of silently starting off. A model whose catalog/scoped tier is already
172
+ // `priority` (a models.json priority entry, or a `-fast`-less codex model) is treated as
173
+ // fast-active when nothing is remembered; an explicit remembered `"auto"` (`/fast off`)
174
+ // wins over that inheritance. Malformed values read back as undefined.
175
+ //
176
+ // The flag is derived from the POST-swap model: a `-fast` catalog variant swaps down to
177
+ // its base before this reads `ctx.serviceTier`, so inheritance is judged on the model the
178
+ // user actually ends up on.
179
+ const remembered = getRememberedServiceTier(settingsManager, ctx.modelRegistry, model);
70
180
  const baseModel = findBaseModel(ctx.modelRegistry, model);
71
181
  if (baseModel) {
72
182
  await pi.setSessionModel(baseModel);
73
183
  }
184
+ sessionFastMode = remembered === PRIORITY_TIER || (remembered === undefined && ctx.serviceTier === PRIORITY_TIER);
185
+ pi.setSessionFastMode(sessionFastMode);
186
+ const memoryModel = resolveServiceTierMemoryModel(ctx.modelRegistry, model);
187
+ liveMemoryKey = `${memoryModel.provider}/${memoryModel.id}`;
188
+ liveMemoryTier = remembered;
189
+ });
190
+ pi.on("model_select", (event, ctx) => {
191
+ // A model switch changes the base key the memory lives under, so the live tier is RE-DERIVED
192
+ // for the incoming model instead of merely dropped: dropping it would leave a remembered
193
+ // "auto" unable to suppress a catalog-inherited priority after switching away and back in one
194
+ // session, silently re-sending the tier `/fast off` turned off. Per-key scoping is preserved —
195
+ // each model reads ITS OWN memory, never the previous model's.
196
+ const settingsManager = SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });
197
+ const memoryModel = resolveServiceTierMemoryModel(ctx.modelRegistry, event.model);
198
+ liveMemoryKey = `${memoryModel.provider}/${memoryModel.id}`;
199
+ liveMemoryTier = getRememberedServiceTier(settingsManager, ctx.modelRegistry, event.model);
74
200
  });
75
201
  pi.registerCommand("fast", {
76
- description: "Toggle OpenAI Codex fast mode for this session",
77
- handler: async (_args, ctx) => {
78
- const model = ctx.model;
79
- if (model?.api !== OPENAI_CODEX_RESPONSES_API) {
80
- ctx.ui.notify("Fast mode is only available for OpenAI Codex models.", "warning");
202
+ description: "Turn OpenAI Codex fast mode on or off for the current model",
203
+ argumentHint: "[on|off]",
204
+ getArgumentCompletions: (prefix) => toCompletions(FAST_ARGUMENTS, prefix),
205
+ handler: async (args, ctx) => {
206
+ const argument = args.trim().toLowerCase();
207
+ if (argument !== "" && !FAST_ARGUMENTS.includes(argument)) {
208
+ ctx.ui.notify(FAST_USAGE, "error");
81
209
  return;
82
210
  }
83
- const baseModel = findBaseModel(ctx.modelRegistry, model);
84
- const targetModel = baseModel ?? findFastModel(ctx.modelRegistry, model);
85
- if (!targetModel) {
86
- sessionFastMode = !sessionFastMode;
87
- pi.setSessionFastMode(sessionFastMode);
88
- ctx.ui.notify(`Fast mode ${sessionFastMode ? "enabled" : "disabled"}: ${model.id}`, "info");
89
- return;
211
+ // No argument keeps the established toggle UX.
212
+ const active = sessionFastMode || ctx.serviceTier === PRIORITY_TIER;
213
+ const enabled = argument === "" ? !active : argument === "on";
214
+ const result = await applyFastMode(toFastModeContext(pi, ctx), enabled);
215
+ sessionFastMode = result.enabled;
216
+ // Track this session's own write AFTER applyFastMode's internal model switch (which fires
217
+ // model_select and clears the live tier). Keyed to the active base model so a later switch
218
+ // cannot carry it across models.
219
+ if (result.applied && ctx.model) {
220
+ const memoryModel = resolveServiceTierMemoryModel(ctx.modelRegistry, ctx.model);
221
+ liveMemoryKey = `${memoryModel.provider}/${memoryModel.id}`;
222
+ liveMemoryTier = result.recordedTier;
90
223
  }
91
- if (!(await pi.setSessionModel(targetModel))) {
92
- ctx.ui.notify(`Could not switch to ${targetModel.provider}/${targetModel.id}.`, "error");
93
- return;
94
- }
95
- const enabled = baseModel === undefined;
96
- ctx.ui.notify(`Fast mode ${enabled ? "enabled" : "disabled"}: ${targetModel.id}`, "info");
97
224
  },
98
225
  });
99
226
  pi.on("before_provider_request", (event, ctx) => {
100
- const effectiveServiceTier = ctx.model?.api === OPENAI_CODEX_RESPONSES_API
101
- ? (ctx.serviceTier ?? (sessionFastMode ? PRIORITY_TIER : undefined))
102
- : (ctx.serviceTier ?? settingsServiceTier);
227
+ let effectiveServiceTier;
228
+ if (ctx.model?.api === OPENAI_CODEX_RESPONSES_API) {
229
+ if (sessionFastMode) {
230
+ effectiveServiceTier = PRIORITY_TIER;
231
+ }
232
+ else {
233
+ const memoryModel = ctx.model ? resolveServiceTierMemoryModel(ctx.modelRegistry, ctx.model) : undefined;
234
+ const activeBaseKey = memoryModel ? `${memoryModel.provider}/${memoryModel.id}` : undefined;
235
+ // Same-session /fast off: the host's resolved ctx.serviceTier is only recomputed on a
236
+ // model switch, so an inherited priority would otherwise keep emitting until a restart.
237
+ // Only a CATALOG-explained priority may be suppressed: a priority the catalog does not
238
+ // explain is an explicit scoped/favorite `:priority` pin, which outranks the memory
239
+ // (same discriminator `applyFastMode` uses to refuse `/fast off` under a pin).
240
+ const catalogExplainsPriority = ctx.model !== undefined && ctx.modelRegistry.getServiceTier(ctx.model) === PRIORITY_TIER;
241
+ if (liveMemoryTier === "auto" &&
242
+ activeBaseKey === liveMemoryKey &&
243
+ ctx.serviceTier === PRIORITY_TIER &&
244
+ catalogExplainsPriority) {
245
+ effectiveServiceTier = undefined;
246
+ }
247
+ else {
248
+ effectiveServiceTier = ctx.serviceTier;
249
+ }
250
+ }
251
+ }
252
+ else {
253
+ effectiveServiceTier = ctx.serviceTier ?? settingsServiceTier;
254
+ }
103
255
  return addServiceTierToPayload(ctx.model?.api, event.payload, effectiveServiceTier);
104
256
  });
105
257
  }
@@ -1 +1 @@
1
- {"version":3,"file":"service-tier.js","sourceRoot":"","sources":["../../../../src/core/extensions/builtin/service-tier.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model } from \"@earendil-works/pi-ai\";\nimport type { ModelRegistry } from \"../../model-registry.ts\";\nimport { SettingsManager } from \"../../settings-manager.ts\";\nimport type { ExtensionAPI, ServiceTier } from \"../types.ts\";\n\nexport type { ServiceTier };\n\ntype ProviderPayload = Record<string, unknown>;\n\nconst OPENAI_CODEX_RESPONSES_API = \"openai-codex-responses\";\nconst FAST_MODEL_SUFFIX = \"-fast\";\nconst PRIORITY_TIER: ServiceTier = \"priority\";\nconst SERVICE_TIER_APIS: ReadonlySet<Api> = new Set([\"openai-responses\", OPENAI_CODEX_RESPONSES_API]);\n\nfunction isRecord(value: unknown): value is ProviderPayload {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nexport function addServiceTierToPayload(api: Api | undefined, payload: unknown, serviceTier?: ServiceTier): unknown {\n\tif (!api || !SERVICE_TIER_APIS.has(api) || !serviceTier) {\n\t\treturn payload;\n\t}\n\n\tif (!isRecord(payload) || payload.service_tier !== undefined) {\n\t\treturn payload;\n\t}\n\n\treturn {\n\t\t...payload,\n\t\tservice_tier: serviceTier,\n\t};\n}\n\nfunction getRequestModelId(modelRegistry: ModelRegistry, model: Model<Api>): string {\n\treturn modelRegistry.getUpstreamModelId(model) ?? model.id;\n}\n\nfunction isCompatibleFastVariant(modelRegistry: ModelRegistry, baseModel: Model<Api>, fastModel: Model<Api>): boolean {\n\treturn (\n\t\tfastModel.provider === baseModel.provider &&\n\t\tfastModel.api === baseModel.api &&\n\t\tmodelRegistry.getServiceTier(fastModel) === \"priority\" &&\n\t\tgetRequestModelId(modelRegistry, fastModel) === getRequestModelId(modelRegistry, baseModel)\n\t);\n}\n\nfunction findBaseModel(modelRegistry: ModelRegistry, fastModel: Model<Api>): Model<Api> | undefined {\n\tif (!fastModel.id.endsWith(FAST_MODEL_SUFFIX)) {\n\t\treturn undefined;\n\t}\n\n\tconst baseModelId = fastModel.id.slice(0, -FAST_MODEL_SUFFIX.length);\n\tconst baseModel = modelRegistry.find(fastModel.provider, baseModelId);\n\treturn baseModel && isCompatibleFastVariant(modelRegistry, baseModel, fastModel) ? baseModel : undefined;\n}\n\nfunction findFastModel(modelRegistry: ModelRegistry, baseModel: Model<Api>): Model<Api> | undefined {\n\tif (baseModel.id.endsWith(FAST_MODEL_SUFFIX)) {\n\t\treturn undefined;\n\t}\n\n\tconst fastModel = modelRegistry.find(baseModel.provider, `${baseModel.id}${FAST_MODEL_SUFFIX}`);\n\treturn fastModel && isCompatibleFastVariant(modelRegistry, baseModel, fastModel) ? fastModel : undefined;\n}\n\nexport default function serviceTierExtension(pi: ExtensionAPI): void {\n\tlet settingsServiceTier: ServiceTier | undefined;\n\t/**\n\t * Session-level fast mode for Codex models that have no `-fast` catalog sibling.\n\t *\n\t * The catalog generator emits `-fast` priority variants only for the direct\n\t * `openai` provider, so `openai-codex` models never have a switch target. The\n\t * ChatGPT backend still offers the tier to subscriptions:\n\t * `chatgpt.com/backend-api/codex/models` advertises a `priority` service tier\n\t * labelled \"Fast\" (\"1.5x speed, increased usage\")\n\t * for gpt-5.4/5.5/5.6-*, and the first-party Codex CLI sends\n\t * `service_tier: \"priority\"` on subscription OAuth. The response echo is not a\n\t * confirmation channel — it reads `auto`/`default` whether or not a tier was\n\t * sent — so this toggle drives the request field directly. See issue #545.\n\t */\n\tlet sessionFastMode = false;\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tconst settingsManager = SettingsManager.create(ctx.cwd);\n\t\tsettingsServiceTier = settingsManager.getOpenAIServiceTier();\n\t\tsessionFastMode = false;\n\t\tpi.setSessionFastMode(false);\n\n\t\tconst model = ctx.model;\n\t\tif (model?.api !== OPENAI_CODEX_RESPONSES_API) {\n\t\t\treturn;\n\t\t}\n\n\t\tconst baseModel = findBaseModel(ctx.modelRegistry, model);\n\t\tif (baseModel) {\n\t\t\tawait pi.setSessionModel(baseModel);\n\t\t}\n\t});\n\n\tpi.registerCommand(\"fast\", {\n\t\tdescription: \"Toggle OpenAI Codex fast mode for this session\",\n\t\thandler: async (_args, ctx) => {\n\t\t\tconst model = ctx.model;\n\t\t\tif (model?.api !== OPENAI_CODEX_RESPONSES_API) {\n\t\t\t\tctx.ui.notify(\"Fast mode is only available for OpenAI Codex models.\", \"warning\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst baseModel = findBaseModel(ctx.modelRegistry, model);\n\t\t\tconst targetModel = baseModel ?? findFastModel(ctx.modelRegistry, model);\n\t\t\tif (!targetModel) {\n\t\t\t\tsessionFastMode = !sessionFastMode;\n\t\t\t\tpi.setSessionFastMode(sessionFastMode);\n\t\t\t\tctx.ui.notify(`Fast mode ${sessionFastMode ? \"enabled\" : \"disabled\"}: ${model.id}`, \"info\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!(await pi.setSessionModel(targetModel))) {\n\t\t\t\tctx.ui.notify(`Could not switch to ${targetModel.provider}/${targetModel.id}.`, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst enabled = baseModel === undefined;\n\t\t\tctx.ui.notify(`Fast mode ${enabled ? \"enabled\" : \"disabled\"}: ${targetModel.id}`, \"info\");\n\t\t},\n\t});\n\n\tpi.on(\"before_provider_request\", (event, ctx) => {\n\t\tconst effectiveServiceTier =\n\t\t\tctx.model?.api === OPENAI_CODEX_RESPONSES_API\n\t\t\t\t? (ctx.serviceTier ?? (sessionFastMode ? PRIORITY_TIER : undefined))\n\t\t\t\t: (ctx.serviceTier ?? settingsServiceTier);\n\t\treturn addServiceTierToPayload(ctx.model?.api, event.payload, effectiveServiceTier);\n\t});\n}\n"]}
1
+ {"version":3,"file":"service-tier.js","sourceRoot":"","sources":["../../../../src/core/extensions/builtin/service-tier.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model } from \"@earendil-works/pi-ai\";\nimport type { AutocompleteItem } from \"@earendil-works/pi-tui\";\nimport type { ModelRegistry } from \"../../model-registry.ts\";\nimport { SettingsManager } from \"../../settings-manager.ts\";\nimport type { ExtensionAPI, ExtensionCommandContext, ServiceTier } from \"../types.ts\";\n\nexport type { ServiceTier };\n\ntype ProviderPayload = Record<string, unknown>;\n\nconst OPENAI_CODEX_RESPONSES_API = \"openai-codex-responses\";\nexport const CODEX_RESPONSES_API = OPENAI_CODEX_RESPONSES_API;\nconst FAST_MODEL_SUFFIX = \"-fast\";\nconst PRIORITY_TIER: ServiceTier = \"priority\";\nconst SERVICE_TIER_APIS: ReadonlySet<Api> = new Set([\"openai-responses\", OPENAI_CODEX_RESPONSES_API]);\nconst FAST_ARGUMENTS = [\"on\", \"off\"] as const;\nconst FAST_USAGE = \"Usage: /fast [on|off]\";\n\ntype FastArgument = (typeof FAST_ARGUMENTS)[number];\n\nfunction isRecord(value: unknown): value is ProviderPayload {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nexport function addServiceTierToPayload(api: Api | undefined, payload: unknown, serviceTier?: ServiceTier): unknown {\n\tif (!api || !SERVICE_TIER_APIS.has(api) || !serviceTier) {\n\t\treturn payload;\n\t}\n\n\tif (!isRecord(payload) || payload.service_tier !== undefined) {\n\t\treturn payload;\n\t}\n\n\treturn {\n\t\t...payload,\n\t\tservice_tier: serviceTier,\n\t};\n}\n\nfunction getRequestModelId(modelRegistry: ModelRegistry, model: Model<Api>): string {\n\treturn modelRegistry.getUpstreamModelId(model) ?? model.id;\n}\n\nfunction isCompatibleFastVariant(modelRegistry: ModelRegistry, baseModel: Model<Api>, fastModel: Model<Api>): boolean {\n\treturn (\n\t\tfastModel.provider === baseModel.provider &&\n\t\tfastModel.api === baseModel.api &&\n\t\tmodelRegistry.getServiceTier(fastModel) === \"priority\" &&\n\t\tgetRequestModelId(modelRegistry, fastModel) === getRequestModelId(modelRegistry, baseModel)\n\t);\n}\n\nfunction findBaseModel(modelRegistry: ModelRegistry, fastModel: Model<Api>): Model<Api> | undefined {\n\tif (!fastModel.id.endsWith(FAST_MODEL_SUFFIX)) {\n\t\treturn undefined;\n\t}\n\n\tconst baseModelId = fastModel.id.slice(0, -FAST_MODEL_SUFFIX.length);\n\tconst baseModel = modelRegistry.find(fastModel.provider, baseModelId);\n\treturn baseModel && isCompatibleFastVariant(modelRegistry, baseModel, fastModel) ? baseModel : undefined;\n}\n\nfunction findFastModel(modelRegistry: ModelRegistry, baseModel: Model<Api>): Model<Api> | undefined {\n\tif (baseModel.id.endsWith(FAST_MODEL_SUFFIX)) {\n\t\treturn undefined;\n\t}\n\n\tconst fastModel = modelRegistry.find(baseModel.provider, `${baseModel.id}${FAST_MODEL_SUFFIX}`);\n\treturn fastModel && isCompatibleFastVariant(modelRegistry, baseModel, fastModel) ? fastModel : undefined;\n}\n\n/**\n * The model that owns this model's persisted service-tier preference.\n *\n * A `-fast` catalog variant and its base model are one model to the user (`/fast` just swaps\n * between them), so both must read and write ONE key. Anything else can hold contradictory\n * preferences for the same choice.\n */\nexport function resolveServiceTierMemoryModel(modelRegistry: ModelRegistry, model: Model<Api>): Model<Api> {\n\treturn findBaseModel(modelRegistry, model) ?? model;\n}\n\n/** Remembered service tier for this model, normalized onto its base-model key. */\nexport function getRememberedServiceTier(\n\tsettingsManager: SettingsManager,\n\tmodelRegistry: ModelRegistry,\n\tmodel: Model<Api>,\n): ServiceTier | undefined {\n\tconst memoryModel = resolveServiceTierMemoryModel(modelRegistry, model);\n\treturn settingsManager.getModelServiceTier(memoryModel.provider, memoryModel.id);\n}\n\n/** Host capabilities `applyFastMode` needs; satisfied by an extension context plus `pi`. */\nexport interface FastModeContext {\n\tcwd: string;\n\tagentDir: string;\n\tmodel: Model<Api> | undefined;\n\tmodelRegistry: ModelRegistry;\n\t/** Tier the host resolved for the active model (scoped/favorite pin or catalog). */\n\tserviceTier: ServiceTier | undefined;\n\tisProjectTrusted(): boolean;\n\tnotify(message: string, level: \"info\" | \"warning\" | \"error\"): void;\n\tsetSessionModel(model: Model<Api>): Promise<boolean>;\n\tsetSessionFastMode(enabled: boolean): void;\n}\n\nexport interface FastModeResult {\n\t/** Fast mode state after the call; unchanged when the request was refused. */\n\tenabled: boolean;\n\t/** False when the request was refused (non-Codex model, active pin, failed model switch). */\n\tapplied: boolean;\n\t/** The just-persisted tier (or the unchanged one when refused); `\"priority\"` on, `\"auto\"` off. */\n\trecordedTier: ServiceTier;\n\tmessage: string;\n}\n\n/**\n * Whether the active model's priority tier is PINNED by the user's model selection (a scoped\n * or favorite `provider/id:priority` pattern) rather than inherited from the catalog.\n *\n * `ctx.serviceTier` merges both sources, so a priority the catalog does not explain must have\n * come from a `:priority` decorator; checking `scopedModels` alone would miss favorite pins,\n * which the session resolves the same way.\n */\nfunction hasPriorityPin(ctx: FastModeContext, model: Model<Api>): boolean {\n\tif (ctx.serviceTier !== PRIORITY_TIER) return false;\n\treturn ctx.modelRegistry.getServiceTier(model) !== PRIORITY_TIER;\n}\n\n/**\n * Single entry point for turning fast mode on/off: the `/fast` command and any other surface\n * (RPC) must go through this so persistence and `-fast` key normalization exist exactly once.\n *\n * `off` persists an explicit `\"auto\"` rather than deleting the key: deletion re-inherits the\n * catalog/global tier, which is what the user just asked to turn off.\n */\nexport async function applyFastMode(ctx: FastModeContext, enabled: boolean): Promise<FastModeResult> {\n\tconst model = ctx.model;\n\tif (model?.api !== OPENAI_CODEX_RESPONSES_API) {\n\t\tconst message = \"Fast mode is only available for OpenAI Codex models.\";\n\t\tctx.notify(message, \"warning\");\n\t\treturn { enabled: false, applied: false, recordedTier: \"auto\", message };\n\t}\n\n\tif (!enabled && hasPriorityPin(ctx, model)) {\n\t\t// The pin outranks the memory, so writing \"auto\" here would be a silent no-op on the wire.\n\t\tconst message = \"Fast mode is fixed by the active model selection's priority tier.\";\n\t\tctx.notify(message, \"info\");\n\t\t// The pin keeps the model on priority no matter what the memory says, so that is the honest\n\t\t// tier to report; nothing was written, and no caller reads this field when `applied` is false.\n\t\treturn { enabled: true, applied: false, recordedTier: PRIORITY_TIER, message };\n\t}\n\n\tconst settingsManager = SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });\n\tconst memoryModel = resolveServiceTierMemoryModel(ctx.modelRegistry, model);\n\tconst tier: ServiceTier = enabled ? PRIORITY_TIER : \"auto\";\n\tsettingsManager.setModelServiceTier(memoryModel.provider, memoryModel.id, tier);\n\tawait settingsManager.flush();\n\n\tconst baseModel = findBaseModel(ctx.modelRegistry, model);\n\tconst targetModel = enabled ? findFastModel(ctx.modelRegistry, model) : baseModel;\n\tif (targetModel) {\n\t\tif (!(await ctx.setSessionModel(targetModel))) {\n\t\t\tconst message = `Could not switch to ${targetModel.provider}/${targetModel.id}.`;\n\t\t\tctx.notify(message, \"error\");\n\t\t\treturn { enabled: baseModel === undefined, applied: false, recordedTier: \"auto\", message };\n\t\t}\n\t}\n\n\tctx.setSessionFastMode(enabled);\n\tconst labelModel = targetModel ?? model;\n\tconst message = `Fast mode ${enabled ? \"enabled\" : \"disabled\"}: ${labelModel.id}`;\n\tctx.notify(message, \"info\");\n\treturn { enabled, applied: true, recordedTier: tier, message };\n}\n\nfunction toCompletions(values: readonly string[], prefix: string): AutocompleteItem[] | null {\n\tconst matches = values.filter((value) => value.startsWith(prefix.trim()));\n\treturn matches.length > 0 ? matches.map((value) => ({ value, label: value })) : null;\n}\n\nfunction toFastModeContext(pi: ExtensionAPI, ctx: ExtensionCommandContext): FastModeContext {\n\treturn {\n\t\tcwd: ctx.cwd,\n\t\tagentDir: ctx.agentDir,\n\t\tmodel: ctx.model,\n\t\tmodelRegistry: ctx.modelRegistry,\n\t\tserviceTier: ctx.serviceTier,\n\t\tisProjectTrusted: () => ctx.isProjectTrusted(),\n\t\tnotify: (message, level) => ctx.ui.notify(message, level),\n\t\tsetSessionModel: (model) => pi.setSessionModel(model),\n\t\tsetSessionFastMode: (enabled) => pi.setSessionFastMode(enabled),\n\t};\n}\n\nexport default function serviceTierExtension(pi: ExtensionAPI): void {\n\tlet settingsServiceTier: ServiceTier | undefined;\n\t/**\n\t * Session-level fast mode for Codex models that have no `-fast` catalog sibling.\n\t *\n\t * The catalog generator emits `-fast` priority variants only for the direct\n\t * `openai` provider, so `openai-codex` models never have a switch target. The\n\t * ChatGPT backend still offers the tier to subscriptions:\n\t * `chatgpt.com/backend-api/codex/models` advertises a `priority` service tier\n\t * labelled \"Fast\" (\"1.5x speed, increased usage\")\n\t * for gpt-5.4/5.5/5.6-*, and the first-party Codex CLI sends\n\t * `service_tier: \"priority\"` on subscription OAuth. The response echo is not a\n\t * confirmation channel — it reads `auto`/`default` whether or not a tier was\n\t * sent — so this toggle drives the request field directly. See issue #545.\n\t */\n\tlet sessionFastMode = false;\n\t/**\n\t * The per-model memory tier `/fast` last wrote or read for the active base model this\n\t * session. Lives only to suppress an inherited priority when the user turns fast off in\n\t * the SAME session: the host's resolved `ctx.serviceTier` is recomputed on model switch,\n\t * not on a memory write, so without this an in-session `/fast off` (no model swap) would\n\t * keep sending `priority` until a restart. Scoped to the active base key (re-derived on\n\t * model_select for the incoming model) so it never leaks across models.\n\t */\n\tlet liveMemoryTier: ServiceTier | undefined;\n\tlet liveMemoryKey: string | undefined;\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tconst settingsManager = SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });\n\t\tsettingsServiceTier = settingsManager.getOpenAIServiceTier();\n\n\t\tconst model = ctx.model;\n\t\tif (model?.api !== OPENAI_CODEX_RESPONSES_API) {\n\t\t\tsessionFastMode = false;\n\t\t\tpi.setSessionFastMode(false);\n\t\t\treturn;\n\t\t}\n\n\t\t// Fast mode is remembered per model, so a new session inherits the user's last choice for\n\t\t// THIS model instead of silently starting off. A model whose catalog/scoped tier is already\n\t\t// `priority` (a models.json priority entry, or a `-fast`-less codex model) is treated as\n\t\t// fast-active when nothing is remembered; an explicit remembered `\"auto\"` (`/fast off`)\n\t\t// wins over that inheritance. Malformed values read back as undefined.\n\t\t//\n\t\t// The flag is derived from the POST-swap model: a `-fast` catalog variant swaps down to\n\t\t// its base before this reads `ctx.serviceTier`, so inheritance is judged on the model the\n\t\t// user actually ends up on.\n\t\tconst remembered = getRememberedServiceTier(settingsManager, ctx.modelRegistry, model);\n\n\t\tconst baseModel = findBaseModel(ctx.modelRegistry, model);\n\t\tif (baseModel) {\n\t\t\tawait pi.setSessionModel(baseModel);\n\t\t}\n\n\t\tsessionFastMode = remembered === PRIORITY_TIER || (remembered === undefined && ctx.serviceTier === PRIORITY_TIER);\n\t\tpi.setSessionFastMode(sessionFastMode);\n\t\tconst memoryModel = resolveServiceTierMemoryModel(ctx.modelRegistry, model);\n\t\tliveMemoryKey = `${memoryModel.provider}/${memoryModel.id}`;\n\t\tliveMemoryTier = remembered;\n\t});\n\n\tpi.on(\"model_select\", (event, ctx) => {\n\t\t// A model switch changes the base key the memory lives under, so the live tier is RE-DERIVED\n\t\t// for the incoming model instead of merely dropped: dropping it would leave a remembered\n\t\t// \"auto\" unable to suppress a catalog-inherited priority after switching away and back in one\n\t\t// session, silently re-sending the tier `/fast off` turned off. Per-key scoping is preserved —\n\t\t// each model reads ITS OWN memory, never the previous model's.\n\t\tconst settingsManager = SettingsManager.create(ctx.cwd, ctx.agentDir, { projectTrusted: ctx.isProjectTrusted() });\n\t\tconst memoryModel = resolveServiceTierMemoryModel(ctx.modelRegistry, event.model);\n\t\tliveMemoryKey = `${memoryModel.provider}/${memoryModel.id}`;\n\t\tliveMemoryTier = getRememberedServiceTier(settingsManager, ctx.modelRegistry, event.model);\n\t});\n\n\tpi.registerCommand(\"fast\", {\n\t\tdescription: \"Turn OpenAI Codex fast mode on or off for the current model\",\n\t\targumentHint: \"[on|off]\",\n\t\tgetArgumentCompletions: (prefix) => toCompletions(FAST_ARGUMENTS, prefix),\n\t\thandler: async (args, ctx) => {\n\t\t\tconst argument = args.trim().toLowerCase();\n\t\t\tif (argument !== \"\" && !FAST_ARGUMENTS.includes(argument as FastArgument)) {\n\t\t\t\tctx.ui.notify(FAST_USAGE, \"error\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// No argument keeps the established toggle UX.\n\t\t\tconst active = sessionFastMode || ctx.serviceTier === PRIORITY_TIER;\n\t\t\tconst enabled = argument === \"\" ? !active : argument === \"on\";\n\t\t\tconst result = await applyFastMode(toFastModeContext(pi, ctx), enabled);\n\t\t\tsessionFastMode = result.enabled;\n\t\t\t// Track this session's own write AFTER applyFastMode's internal model switch (which fires\n\t\t\t// model_select and clears the live tier). Keyed to the active base model so a later switch\n\t\t\t// cannot carry it across models.\n\t\t\tif (result.applied && ctx.model) {\n\t\t\t\tconst memoryModel = resolveServiceTierMemoryModel(ctx.modelRegistry, ctx.model);\n\t\t\t\tliveMemoryKey = `${memoryModel.provider}/${memoryModel.id}`;\n\t\t\t\tliveMemoryTier = result.recordedTier;\n\t\t\t}\n\t\t},\n\t});\n\n\tpi.on(\"before_provider_request\", (event, ctx) => {\n\t\tlet effectiveServiceTier: ServiceTier | undefined;\n\t\tif (ctx.model?.api === OPENAI_CODEX_RESPONSES_API) {\n\t\t\tif (sessionFastMode) {\n\t\t\t\teffectiveServiceTier = PRIORITY_TIER;\n\t\t\t} else {\n\t\t\t\tconst memoryModel = ctx.model ? resolveServiceTierMemoryModel(ctx.modelRegistry, ctx.model) : undefined;\n\t\t\t\tconst activeBaseKey = memoryModel ? `${memoryModel.provider}/${memoryModel.id}` : undefined;\n\t\t\t\t// Same-session /fast off: the host's resolved ctx.serviceTier is only recomputed on a\n\t\t\t\t// model switch, so an inherited priority would otherwise keep emitting until a restart.\n\t\t\t\t// Only a CATALOG-explained priority may be suppressed: a priority the catalog does not\n\t\t\t\t// explain is an explicit scoped/favorite `:priority` pin, which outranks the memory\n\t\t\t\t// (same discriminator `applyFastMode` uses to refuse `/fast off` under a pin).\n\t\t\t\tconst catalogExplainsPriority =\n\t\t\t\t\tctx.model !== undefined && ctx.modelRegistry.getServiceTier(ctx.model) === PRIORITY_TIER;\n\t\t\t\tif (\n\t\t\t\t\tliveMemoryTier === \"auto\" &&\n\t\t\t\t\tactiveBaseKey === liveMemoryKey &&\n\t\t\t\t\tctx.serviceTier === PRIORITY_TIER &&\n\t\t\t\t\tcatalogExplainsPriority\n\t\t\t\t) {\n\t\t\t\t\teffectiveServiceTier = undefined;\n\t\t\t\t} else {\n\t\t\t\t\teffectiveServiceTier = ctx.serviceTier;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\teffectiveServiceTier = ctx.serviceTier ?? settingsServiceTier;\n\t\t}\n\t\treturn addServiceTierToPayload(ctx.model?.api, event.payload, effectiveServiceTier);\n\t});\n}\n"]}