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

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 (285) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +1 -1
  3. package/dist/cli.js +4659 -4261
  4. package/dist/types/async/job-manager.d.ts +37 -1
  5. package/dist/types/cli/args.d.ts +2 -0
  6. package/dist/types/cli/bench-cli.d.ts +41 -0
  7. package/dist/types/commands/bench.d.ts +15 -3
  8. package/dist/types/commands/launch.d.ts +4 -0
  9. package/dist/types/config/model-registry.d.ts +3 -1
  10. package/dist/types/config/models-config-schema.d.ts +18 -6
  11. package/dist/types/config/models-config.d.ts +14 -5
  12. package/dist/types/config/provider-globals.d.ts +2 -2
  13. package/dist/types/config/service-tier.d.ts +5 -1
  14. package/dist/types/config/settings-schema.d.ts +316 -47
  15. package/dist/types/config/settings.d.ts +7 -0
  16. package/dist/types/dap/client.d.ts +20 -0
  17. package/dist/types/exec/bash-executor.d.ts +38 -0
  18. package/dist/types/exec/direnv.d.ts +33 -0
  19. package/dist/types/export/html/args.d.ts +15 -0
  20. package/dist/types/export/html/index.d.ts +11 -26
  21. package/dist/types/export/html/web-palette.d.ts +55 -116
  22. package/dist/types/extensibility/extensions/loader.d.ts +3 -0
  23. package/dist/types/extensibility/extensions/types.d.ts +17 -2
  24. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +13 -0
  25. package/dist/types/live/controller.d.ts +40 -0
  26. package/dist/types/live/protocol.d.ts +85 -0
  27. package/dist/types/live/protocol.test.d.ts +1 -0
  28. package/dist/types/live/transport.d.ts +48 -0
  29. package/dist/types/live/visualizer.d.ts +33 -0
  30. package/dist/types/lsp/client.d.ts +16 -1
  31. package/dist/types/mcp/config-writer.test.d.ts +1 -0
  32. package/dist/types/mcp/oauth-discovery.d.ts +2 -0
  33. package/dist/types/mcp/smithery-auth.d.ts +1 -1
  34. package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
  35. package/dist/types/memory-backend/tool-names.d.ts +2 -0
  36. package/dist/types/modes/components/login-dialog.d.ts +2 -2
  37. package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
  38. package/dist/types/modes/components/settings-defs.d.ts +7 -1
  39. package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
  40. package/dist/types/modes/components/tool-execution.d.ts +9 -1
  41. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  42. package/dist/types/modes/controllers/event-controller.d.ts +6 -1
  43. package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
  44. package/dist/types/modes/interactive-mode.d.ts +5 -0
  45. package/dist/types/modes/theme/theme.d.ts +1 -1
  46. package/dist/types/modes/types.d.ts +3 -1
  47. package/dist/types/modes/utils/context-usage.d.ts +12 -2
  48. package/dist/types/plan-mode/plan-files.d.ts +10 -0
  49. package/dist/types/registry/agent-lifecycle.d.ts +21 -9
  50. package/dist/types/registry/agent-registry.d.ts +11 -4
  51. package/dist/types/sdk.d.ts +15 -1
  52. package/dist/types/secrets/index.d.ts +15 -2
  53. package/dist/types/secrets/obfuscator.d.ts +113 -15
  54. package/dist/types/session/acp-permission-gate.d.ts +19 -0
  55. package/dist/types/session/agent-session-events.d.ts +84 -0
  56. package/dist/types/session/agent-session-types.d.ts +314 -0
  57. package/dist/types/session/agent-session.d.ts +118 -755
  58. package/dist/types/session/async-job-delivery.d.ts +28 -0
  59. package/dist/types/session/bash-runner.d.ts +75 -0
  60. package/dist/types/session/blob-store.d.ts +11 -1
  61. package/dist/types/session/blob-store.test.d.ts +1 -0
  62. package/dist/types/session/checkpoint-entries.d.ts +28 -0
  63. package/dist/types/session/eval-runner.d.ts +49 -0
  64. package/dist/types/session/irc-bridge.d.ts +46 -0
  65. package/dist/types/session/messages.d.ts +22 -0
  66. package/dist/types/session/model-controls.d.ts +180 -0
  67. package/dist/types/session/prewalk.d.ts +46 -0
  68. package/dist/types/session/queued-messages.d.ts +22 -0
  69. package/dist/types/session/redis-session-storage.d.ts +1 -0
  70. package/dist/types/session/retry-fallback-chains.d.ts +74 -0
  71. package/dist/types/session/role-models.d.ts +15 -0
  72. package/dist/types/session/session-advisors.d.ts +228 -0
  73. package/dist/types/session/session-entries.d.ts +8 -0
  74. package/dist/types/session/session-handoff.d.ts +70 -0
  75. package/dist/types/session/session-history-format.d.ts +6 -1
  76. package/dist/types/session/session-maintenance.d.ts +246 -0
  77. package/dist/types/session/session-manager.d.ts +41 -0
  78. package/dist/types/session/session-memory.d.ts +56 -0
  79. package/dist/types/session/session-provider-boundary.d.ts +62 -0
  80. package/dist/types/session/session-stats.d.ts +48 -0
  81. package/dist/types/session/session-tools.d.ts +143 -0
  82. package/dist/types/session/session-workspace.d.ts +32 -0
  83. package/dist/types/session/stream-guards.d.ts +44 -0
  84. package/dist/types/session/todo-tracker.d.ts +56 -0
  85. package/dist/types/session/ttsr-coordinator.d.ts +51 -0
  86. package/dist/types/session/turn-recovery.d.ts +225 -0
  87. package/dist/types/system-prompt.d.ts +2 -0
  88. package/dist/types/task/executor.d.ts +4 -1
  89. package/dist/types/task/output-manager.d.ts +3 -1
  90. package/dist/types/tools/approval.d.ts +1 -0
  91. package/dist/types/tools/browser/launch.d.ts +5 -0
  92. package/dist/types/tools/image-gen.d.ts +4 -4
  93. package/dist/types/tools/image-providers.d.ts +38 -0
  94. package/dist/types/tools/index.d.ts +5 -0
  95. package/dist/types/tools/path-utils.d.ts +8 -0
  96. package/dist/types/tools/todo.d.ts +10 -4
  97. package/dist/types/tools/xdev.d.ts +5 -1
  98. package/dist/types/tts/streaming-player.d.ts +14 -0
  99. package/dist/types/tts/vocalizer.d.ts +5 -0
  100. package/dist/types/utils/jj.d.ts +29 -0
  101. package/dist/types/utils/lang-from-path.d.ts +1 -0
  102. package/dist/types/utils/title-generator.d.ts +30 -0
  103. package/dist/types/vibe/runtime.d.ts +36 -8
  104. package/dist/types/web/search/index.d.ts +1 -1
  105. package/dist/types/web/search/provider.d.ts +15 -5
  106. package/dist/types/web/search/types.d.ts +94 -0
  107. package/package.json +12 -12
  108. package/scripts/generate-share-viewer.ts +4 -6
  109. package/src/advisor/__tests__/advisor.test.ts +643 -0
  110. package/src/advisor/runtime.ts +144 -43
  111. package/src/async/job-manager.ts +90 -2
  112. package/src/cli/args.ts +2 -0
  113. package/src/cli/auth-gateway-cli.ts +18 -3
  114. package/src/cli/bench-cli.ts +395 -29
  115. package/src/cli/flag-tables.ts +3 -0
  116. package/src/cli/gc-cli.ts +2 -2
  117. package/src/cli/usage-cli.ts +10 -1
  118. package/src/collab/host.ts +3 -2
  119. package/src/commands/bench.ts +18 -4
  120. package/src/commands/launch.ts +4 -0
  121. package/src/config/model-registry.ts +10 -2
  122. package/src/config/models-config-schema.ts +15 -4
  123. package/src/config/provider-globals.ts +9 -9
  124. package/src/config/service-tier.ts +26 -1
  125. package/src/config/settings-schema.ts +209 -51
  126. package/src/config/settings.ts +81 -2
  127. package/src/dap/client.ts +37 -4
  128. package/src/edit/index.ts +49 -12
  129. package/src/eval/py/executor.ts +103 -33
  130. package/src/exec/bash-executor.ts +89 -6
  131. package/src/exec/direnv.ts +145 -0
  132. package/src/export/html/args.ts +20 -0
  133. package/src/export/html/index.ts +45 -47
  134. package/src/export/html/template.css +19 -1
  135. package/src/export/html/template.html +6 -0
  136. package/src/export/html/template.js +21 -0
  137. package/src/export/html/tool-views.generated.js +31 -31
  138. package/src/export/html/web-palette.ts +116 -132
  139. package/src/export/share.ts +249 -49
  140. package/src/extensibility/extensions/loader.ts +29 -1
  141. package/src/extensibility/extensions/runner.ts +6 -0
  142. package/src/extensibility/extensions/types.ts +33 -2
  143. package/src/extensibility/plugins/legacy-pi-compat.ts +19 -1
  144. package/src/hindsight/state.ts +64 -6
  145. package/src/internal-urls/local-protocol.ts +2 -1
  146. package/src/internal-urls/memory-protocol.ts +2 -2
  147. package/src/internal-urls/skill-protocol.ts +2 -2
  148. package/src/internal-urls/ssh-protocol.ts +2 -1
  149. package/src/internal-urls/vault-protocol.ts +2 -1
  150. package/src/live/audio-worklet.txt +59 -0
  151. package/src/live/browser-runtime.txt +221 -0
  152. package/src/live/controller.ts +464 -0
  153. package/src/live/prompts/agent-final-message.md +3 -0
  154. package/src/live/prompts/live-instructions.md +23 -0
  155. package/src/live/protocol.test.ts +140 -0
  156. package/src/live/protocol.ts +233 -0
  157. package/src/live/transport.ts +490 -0
  158. package/src/live/visualizer.ts +234 -0
  159. package/src/lsp/client.ts +29 -9
  160. package/src/lsp/index.ts +19 -4
  161. package/src/main.ts +8 -1
  162. package/src/mcp/config-writer.test.ts +43 -0
  163. package/src/mcp/config-writer.ts +109 -82
  164. package/src/mcp/oauth-discovery.ts +10 -2
  165. package/src/mcp/smithery-auth.test.ts +29 -0
  166. package/src/mcp/smithery-auth.ts +3 -2
  167. package/src/memories/index.ts +40 -20
  168. package/src/memory-backend/tool-names.ts +2 -0
  169. package/src/mnemopi/backend.ts +24 -7
  170. package/src/modes/acp/acp-agent.ts +56 -10
  171. package/src/modes/acp/acp-event-mapper.ts +8 -1
  172. package/src/modes/components/agent-hub.ts +1 -1
  173. package/src/modes/components/login-dialog.ts +14 -4
  174. package/src/modes/components/plan-review-overlay.ts +350 -35
  175. package/src/modes/components/settings-defs.ts +23 -1
  176. package/src/modes/components/settings-selector.ts +169 -1
  177. package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
  178. package/src/modes/components/status-line/component.ts +123 -2
  179. package/src/modes/components/tool-execution.ts +11 -2
  180. package/src/modes/controllers/command-controller.ts +56 -15
  181. package/src/modes/controllers/event-controller.ts +111 -16
  182. package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
  183. package/src/modes/controllers/extension-ui-controller.ts +20 -4
  184. package/src/modes/controllers/live-command-controller.ts +178 -0
  185. package/src/modes/controllers/mcp-command-controller.ts +10 -1
  186. package/src/modes/controllers/omfg-controller.ts +44 -40
  187. package/src/modes/controllers/selector-controller.ts +18 -10
  188. package/src/modes/controllers/session-focus-controller.ts +6 -1
  189. package/src/modes/interactive-mode.ts +157 -38
  190. package/src/modes/runtime-init.ts +2 -0
  191. package/src/modes/setup-wizard/scenes/web-search.ts +17 -9
  192. package/src/modes/theme/theme.ts +1 -1
  193. package/src/modes/types.ts +3 -1
  194. package/src/modes/utils/context-usage.ts +23 -7
  195. package/src/modes/utils/ui-helpers.ts +10 -5
  196. package/src/plan-mode/plan-files.ts +40 -0
  197. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  198. package/src/prompts/bench/cache-prefix.md +3 -0
  199. package/src/prompts/bench/cache-suffix.md +1 -0
  200. package/src/prompts/system/custom-system-prompt.md +1 -1
  201. package/src/prompts/system/project-prompt.md +8 -1
  202. package/src/prompts/system/subagent-async-pending.md +6 -0
  203. package/src/prompts/system/system-prompt.md +1 -1
  204. package/src/prompts/system/workflow-notice.md +48 -40
  205. package/src/prompts/system/xdev-mount-notice.md +4 -0
  206. package/src/prompts/tools/todo.md +3 -1
  207. package/src/registry/agent-lifecycle.ts +116 -46
  208. package/src/registry/agent-registry.ts +35 -8
  209. package/src/registry/persisted-agents.ts +26 -2
  210. package/src/sdk.ts +210 -136
  211. package/src/secrets/index.ts +127 -4
  212. package/src/secrets/obfuscator.ts +2328 -105
  213. package/src/session/acp-permission-gate.ts +165 -0
  214. package/src/session/agent-session-error-log.test.ts +1 -1
  215. package/src/session/agent-session-events.ts +66 -0
  216. package/src/session/agent-session-types.ts +334 -0
  217. package/src/session/agent-session.ts +1892 -12226
  218. package/src/session/artifacts.ts +6 -5
  219. package/src/session/async-job-delivery.ts +74 -0
  220. package/src/session/bash-runner.ts +326 -0
  221. package/src/session/blob-store.test.ts +56 -0
  222. package/src/session/blob-store.ts +18 -2
  223. package/src/session/checkpoint-entries.ts +81 -0
  224. package/src/session/eval-runner.ts +212 -0
  225. package/src/session/indexed-session-storage.ts +9 -2
  226. package/src/session/irc-bridge.ts +203 -0
  227. package/src/session/messages.ts +263 -1
  228. package/src/session/model-controls.ts +714 -0
  229. package/src/session/prewalk.ts +252 -0
  230. package/src/session/queued-messages.ts +93 -0
  231. package/src/session/redis-session-storage.ts +51 -11
  232. package/src/session/retry-fallback-chains.ts +455 -0
  233. package/src/session/role-models.ts +85 -0
  234. package/src/session/session-advisors.ts +1679 -0
  235. package/src/session/session-context.ts +6 -3
  236. package/src/session/session-entries.ts +8 -0
  237. package/src/session/session-handoff.ts +308 -0
  238. package/src/session/session-history-format.ts +20 -9
  239. package/src/session/session-loader.ts +0 -46
  240. package/src/session/session-maintenance.ts +2983 -0
  241. package/src/session/session-manager.ts +390 -30
  242. package/src/session/session-memory.ts +222 -0
  243. package/src/session/session-provider-boundary.ts +307 -0
  244. package/src/session/session-stats.ts +293 -0
  245. package/src/session/session-tools.ts +943 -0
  246. package/src/session/session-workspace.ts +53 -0
  247. package/src/session/stream-guards.ts +417 -0
  248. package/src/session/todo-tracker.ts +380 -0
  249. package/src/session/ttsr-coordinator.ts +496 -0
  250. package/src/session/turn-recovery.ts +1629 -0
  251. package/src/slash-commands/builtin-registry.ts +92 -11
  252. package/src/slash-commands/helpers/usage-report.ts +22 -3
  253. package/src/system-prompt.ts +41 -17
  254. package/src/task/executor.ts +285 -70
  255. package/src/task/index.ts +98 -105
  256. package/src/task/output-manager.ts +25 -3
  257. package/src/task/persisted-revive.ts +4 -3
  258. package/src/task/structured-subagent.ts +1 -0
  259. package/src/tools/approval.ts +55 -10
  260. package/src/tools/bash-interactive.ts +26 -0
  261. package/src/tools/bash-skill-urls.ts +28 -2
  262. package/src/tools/bash.ts +339 -123
  263. package/src/tools/browser/launch.ts +9 -1
  264. package/src/tools/browser/tab-supervisor.ts +8 -5
  265. package/src/tools/browser.ts +94 -47
  266. package/src/tools/image-gen.ts +20 -28
  267. package/src/tools/image-providers.ts +50 -0
  268. package/src/tools/index.ts +9 -0
  269. package/src/tools/path-utils.ts +49 -2
  270. package/src/tools/read.ts +156 -88
  271. package/src/tools/renderers.ts +7 -1
  272. package/src/tools/todo.ts +101 -11
  273. package/src/tools/vibe.ts +1 -2
  274. package/src/tools/write.ts +51 -1
  275. package/src/tools/xdev.ts +67 -3
  276. package/src/tts/streaming-player.ts +62 -4
  277. package/src/tts/vocalizer.ts +18 -1
  278. package/src/utils/jj.ts +125 -4
  279. package/src/utils/lang-from-path.ts +7 -0
  280. package/src/utils/title-generator.ts +137 -1
  281. package/src/vibe/runtime.ts +852 -72
  282. package/src/web/search/index.ts +3 -6
  283. package/src/web/search/provider.ts +31 -16
  284. package/src/web/search/providers/xai.ts +7 -1
  285. package/src/web/search/types.ts +3 -0
@@ -25,7 +25,7 @@ import { $which, logger } from "@oh-my-pi/pi-utils";
25
25
  import { DEFAULT_SHARE_URL } from "@oh-my-pi/pi-wire";
26
26
  import { $ } from "bun";
27
27
  import { obfuscateToolArguments, type SecretObfuscator } from "../secrets/obfuscator";
28
- import type { SessionEntry, SessionHeader } from "../session/session-entries";
28
+ import { type SessionEntry, type SessionHeader, TITLE_CHANGE_ENTRY_TYPE } from "../session/session-entries";
29
29
  import type { SessionManager } from "../session/session-manager";
30
30
  import type { OutputMeta } from "../tools/output-meta";
31
31
  import { buildSessionData, type SessionData, type SubSession } from "./html";
@@ -103,54 +103,212 @@ export function buildShareSnapshot(sm: SessionManager, options?: ShareSessionOpt
103
103
  * extension `details`/`data`, `mode_change.data`, structured output schemas)
104
104
  * are dropped so they cannot leak.
105
105
  */
106
- function redactShareHeader(o: SecretObfuscator, header: SessionHeader | null): SessionHeader | null {
106
+ function collectShareRegexSecretValues(o: SecretObfuscator, data: SessionData): Set<string> {
107
+ const values = new Set<string>();
108
+ const add = (value: string | undefined): void => {
109
+ if (value === undefined) return;
110
+ for (const secretValue of o.collectRegexSecretValuesForObfuscation(value)) {
111
+ values.add(secretValue);
112
+ }
113
+ };
114
+ const addJsonStrings = (value: unknown): void => {
115
+ if (typeof value === "string") {
116
+ add(value);
117
+ return;
118
+ }
119
+ if (Array.isArray(value)) {
120
+ for (const item of value) addJsonStrings(item);
121
+ return;
122
+ }
123
+ if (!isRecord(value)) return;
124
+ for (const item of Object.values(value)) addJsonStrings(item);
125
+ };
126
+ const addContent = (content: string | (TextContent | ImageContent)[]): void => {
127
+ if (typeof content === "string") {
128
+ add(content);
129
+ return;
130
+ }
131
+ for (const block of content) {
132
+ if (block.type === "text") add(block.text);
133
+ }
134
+ };
135
+ const addOutputMeta = (meta: OutputMeta | undefined): void => {
136
+ if (!meta) return;
137
+ add(meta.source?.value);
138
+ if (!meta.diagnostics) return;
139
+ add(meta.diagnostics.summary);
140
+ for (const message of meta.diagnostics.messages) add(message);
141
+ };
142
+ const addMessage = (message: AgentMessage): void => {
143
+ switch (message.role) {
144
+ case "user":
145
+ case "developer":
146
+ case "custom":
147
+ case "hookMessage":
148
+ case "toolResult":
149
+ addContent(message.content as string | (TextContent | ImageContent)[]);
150
+ return;
151
+ case "assistant":
152
+ add(message.errorMessage);
153
+ for (const block of message.content) {
154
+ if (block.type === "text") add(block.text);
155
+ else if (block.type === "thinking") add(block.thinking);
156
+ else if (block.type === "toolCall") {
157
+ addJsonStrings(block.arguments);
158
+ add(block.intent);
159
+ add(block.rawBlock);
160
+ }
161
+ }
162
+ return;
163
+ case "bashExecution":
164
+ add(message.command);
165
+ add(message.output);
166
+ addOutputMeta(message.meta);
167
+ return;
168
+ case "pythonExecution":
169
+ add(message.code);
170
+ add(message.output);
171
+ addOutputMeta(message.meta);
172
+ return;
173
+ case "branchSummary":
174
+ add(message.summary);
175
+ return;
176
+ case "compactionSummary":
177
+ add(message.summary);
178
+ add(message.shortSummary);
179
+ if (message.blocks) addContent(message.blocks);
180
+ return;
181
+ case "fileMention":
182
+ for (const file of message.files) {
183
+ add(file.path);
184
+ add(file.content);
185
+ }
186
+ return;
187
+ default:
188
+ return;
189
+ }
190
+ };
191
+ const addEntry = (entry: SessionEntry): void => {
192
+ switch (entry.type) {
193
+ case "message":
194
+ addMessage(entry.message);
195
+ return;
196
+ case "compaction":
197
+ add(entry.summary);
198
+ add(entry.shortSummary);
199
+ return;
200
+ case "branch_summary":
201
+ add(entry.summary);
202
+ return;
203
+ case "custom_message":
204
+ addContent(entry.content);
205
+ return;
206
+ case "session_init":
207
+ add(entry.systemPrompt);
208
+ add(entry.task);
209
+ return;
210
+ case "label":
211
+ add(entry.label);
212
+ return;
213
+ case TITLE_CHANGE_ENTRY_TYPE:
214
+ add(entry.title);
215
+ add(entry.previousTitle);
216
+ add(entry.trigger);
217
+ return;
218
+ default:
219
+ return;
220
+ }
221
+ };
222
+ const addHeader = (header: SessionHeader | null): void => {
223
+ if (!header) return;
224
+ add(header.title);
225
+ add(header.cwd);
226
+ };
227
+
228
+ addHeader(data.header);
229
+ add(data.systemPrompt);
230
+ for (const tool of data.tools ?? []) add(tool.description);
231
+ for (const entry of data.entries) addEntry(entry);
232
+ for (const sub of Object.values(data.subSessions ?? {})) {
233
+ addHeader(sub.header);
234
+ for (const entry of sub.entries) addEntry(entry);
235
+ }
236
+ return values;
237
+ }
238
+
239
+ function redactShareHeader(
240
+ o: SecretObfuscator,
241
+ header: SessionHeader | null,
242
+ sharedRegexSecretValues: ReadonlySet<string>,
243
+ ): SessionHeader | null {
107
244
  if (!header) return header;
108
245
  return {
109
246
  ...header,
110
- title: header.title === undefined ? undefined : o.obfuscate(header.title),
111
- cwd: o.obfuscate(header.cwd),
247
+ title: header.title === undefined ? undefined : o.obfuscate(header.title, sharedRegexSecretValues),
248
+ cwd: o.obfuscate(header.cwd, sharedRegexSecretValues),
112
249
  };
113
250
  }
114
251
 
115
252
  function redactSessionDataForShare(o: SecretObfuscator, data: SessionData): SessionData {
253
+ const sharedRegexSecretValues = collectShareRegexSecretValues(o, data);
116
254
  return {
117
255
  ...data,
118
- header: redactShareHeader(o, data.header),
119
- systemPrompt: data.systemPrompt === undefined ? undefined : o.obfuscate(data.systemPrompt),
120
- tools: data.tools?.map(tool => ({ ...tool, description: o.obfuscate(tool.description) })),
121
- entries: data.entries.map(entry => redactShareEntry(o, entry)),
256
+ header: redactShareHeader(o, data.header, sharedRegexSecretValues),
257
+ systemPrompt:
258
+ data.systemPrompt === undefined ? undefined : o.obfuscate(data.systemPrompt, sharedRegexSecretValues),
259
+ tools: data.tools?.map(tool => ({
260
+ ...tool,
261
+ description: o.obfuscate(tool.description, sharedRegexSecretValues),
262
+ })),
263
+ entries: data.entries.map(entry => redactShareEntry(o, entry, sharedRegexSecretValues)),
122
264
  subSessions: data.subSessions
123
265
  ? Object.fromEntries(
124
- Object.entries(data.subSessions).map(([key, sub]) => [key, redactShareSubSession(o, sub)]),
266
+ Object.entries(data.subSessions).map(([key, sub]) => [
267
+ key,
268
+ redactShareSubSession(o, sub, sharedRegexSecretValues),
269
+ ]),
125
270
  )
126
271
  : data.subSessions,
127
272
  };
128
273
  }
129
274
 
130
- function redactShareSubSession(o: SecretObfuscator, sub: SubSession): SubSession {
275
+ function redactShareSubSession(
276
+ o: SecretObfuscator,
277
+ sub: SubSession,
278
+ sharedRegexSecretValues: ReadonlySet<string>,
279
+ ): SubSession {
131
280
  return {
132
281
  ...sub,
133
- header: redactShareHeader(o, sub.header),
134
- entries: sub.entries.map(entry => redactShareEntry(o, entry)),
282
+ header: redactShareHeader(o, sub.header, sharedRegexSecretValues),
283
+ entries: sub.entries.map(entry => redactShareEntry(o, entry, sharedRegexSecretValues)),
135
284
  };
136
285
  }
137
286
 
138
- function redactShareEntry(o: SecretObfuscator, entry: SessionEntry): SessionEntry {
287
+ function redactShareEntry(
288
+ o: SecretObfuscator,
289
+ entry: SessionEntry,
290
+ sharedRegexSecretValues: ReadonlySet<string>,
291
+ ): SessionEntry {
139
292
  switch (entry.type) {
140
293
  case "message":
141
- return { ...entry, message: redactShareMessage(o, entry.message) };
294
+ return { ...entry, message: redactShareMessage(o, entry.message, sharedRegexSecretValues) };
142
295
  case "compaction":
143
296
  return {
144
297
  ...entry,
145
- summary: o.obfuscate(entry.summary),
146
- shortSummary: entry.shortSummary === undefined ? undefined : o.obfuscate(entry.shortSummary),
298
+ summary: o.obfuscate(entry.summary, sharedRegexSecretValues),
299
+ shortSummary:
300
+ entry.shortSummary === undefined ? undefined : o.obfuscate(entry.shortSummary, sharedRegexSecretValues),
147
301
  details: undefined,
148
302
  preserveData: undefined,
149
303
  };
150
304
  case "branch_summary":
151
- return { ...entry, summary: o.obfuscate(entry.summary), details: undefined };
305
+ return { ...entry, summary: o.obfuscate(entry.summary, sharedRegexSecretValues), details: undefined };
152
306
  case "custom_message":
153
- return { ...entry, content: redactShareContent(o, entry.content), details: undefined };
307
+ return {
308
+ ...entry,
309
+ content: redactShareContent(o, entry.content, sharedRegexSecretValues),
310
+ details: undefined,
311
+ };
154
312
  case "custom":
155
313
  return { ...entry, data: undefined };
156
314
  case "mode_change":
@@ -158,12 +316,25 @@ function redactShareEntry(o: SecretObfuscator, entry: SessionEntry): SessionEntr
158
316
  case "session_init":
159
317
  return {
160
318
  ...entry,
161
- systemPrompt: o.obfuscate(entry.systemPrompt),
162
- task: o.obfuscate(entry.task),
319
+ systemPrompt: o.obfuscate(entry.systemPrompt, sharedRegexSecretValues),
320
+ task: o.obfuscate(entry.task, sharedRegexSecretValues),
163
321
  outputSchema: undefined,
164
322
  };
165
323
  case "label":
166
- return { ...entry, label: entry.label === undefined ? undefined : o.obfuscate(entry.label) };
324
+ return {
325
+ ...entry,
326
+ label: entry.label === undefined ? undefined : o.obfuscate(entry.label, sharedRegexSecretValues),
327
+ };
328
+ case TITLE_CHANGE_ENTRY_TYPE:
329
+ return {
330
+ ...entry,
331
+ title: o.obfuscate(entry.title, sharedRegexSecretValues),
332
+ previousTitle:
333
+ entry.previousTitle === undefined
334
+ ? undefined
335
+ : o.obfuscate(entry.previousTitle, sharedRegexSecretValues),
336
+ trigger: entry.trigger === undefined ? undefined : o.obfuscate(entry.trigger, sharedRegexSecretValues),
337
+ };
167
338
  default:
168
339
  return entry;
169
340
  }
@@ -172,43 +343,60 @@ function redactShareEntry(o: SecretObfuscator, entry: SessionEntry): SessionEntr
172
343
  function redactShareContent(
173
344
  o: SecretObfuscator,
174
345
  content: string | (TextContent | ImageContent)[],
346
+ sharedRegexSecretValues: ReadonlySet<string>,
175
347
  ): string | (TextContent | ImageContent)[] {
176
- if (typeof content === "string") return o.obfuscate(content);
177
- return content.map(block => (block.type === "text" ? { ...block, text: o.obfuscate(block.text) } : block));
348
+ if (typeof content === "string") return o.obfuscate(content, sharedRegexSecretValues);
349
+ return content.map(block =>
350
+ block.type === "text" ? { ...block, text: o.obfuscate(block.text, sharedRegexSecretValues) } : block,
351
+ );
178
352
  }
179
353
 
180
354
  /** Redact freeform strings in tool output metadata (source path/URL, diagnostics); numeric truncation info is preserved. */
181
- function redactShareOutputMeta(o: SecretObfuscator, meta: OutputMeta | undefined): OutputMeta | undefined {
355
+ function redactShareOutputMeta(
356
+ o: SecretObfuscator,
357
+ meta: OutputMeta | undefined,
358
+ sharedRegexSecretValues: ReadonlySet<string>,
359
+ ): OutputMeta | undefined {
182
360
  if (!meta) return meta;
183
361
  return {
184
362
  ...meta,
185
- source: meta.source ? { ...meta.source, value: o.obfuscate(meta.source.value) } : meta.source,
363
+ source: meta.source
364
+ ? { ...meta.source, value: o.obfuscate(meta.source.value, sharedRegexSecretValues) }
365
+ : meta.source,
186
366
  diagnostics: meta.diagnostics
187
367
  ? {
188
- summary: o.obfuscate(meta.diagnostics.summary),
189
- messages: meta.diagnostics.messages.map(message => o.obfuscate(message)),
368
+ summary: o.obfuscate(meta.diagnostics.summary, sharedRegexSecretValues),
369
+ messages: meta.diagnostics.messages.map(message => o.obfuscate(message, sharedRegexSecretValues)),
190
370
  }
191
371
  : meta.diagnostics,
192
372
  };
193
373
  }
194
374
 
195
- function redactShareMessage(o: SecretObfuscator, message: AgentMessage): AgentMessage {
375
+ function redactShareMessage(
376
+ o: SecretObfuscator,
377
+ message: AgentMessage,
378
+ sharedRegexSecretValues: ReadonlySet<string>,
379
+ ): AgentMessage {
196
380
  switch (message.role) {
197
381
  case "user":
198
382
  case "developer":
199
383
  return {
200
384
  ...message,
201
385
  providerPayload: undefined,
202
- content: redactShareContent(o, message.content),
386
+ content: redactShareContent(o, message.content, sharedRegexSecretValues),
203
387
  } as AgentMessage;
204
388
  case "custom":
205
389
  case "hookMessage":
206
- return { ...message, details: undefined, content: redactShareContent(o, message.content) } as AgentMessage;
390
+ return {
391
+ ...message,
392
+ details: undefined,
393
+ content: redactShareContent(o, message.content, sharedRegexSecretValues),
394
+ } as AgentMessage;
207
395
  case "toolResult":
208
396
  return {
209
397
  ...message,
210
398
  details: undefined,
211
- content: redactShareContent(o, message.content) as (TextContent | ImageContent)[],
399
+ content: redactShareContent(o, message.content, sharedRegexSecretValues) as (TextContent | ImageContent)[],
212
400
  };
213
401
  case "assistant":
214
402
  // Drop opaque provider-replay state (encrypted reasoning / native history) the viewer
@@ -217,18 +405,27 @@ function redactShareMessage(o: SecretObfuscator, message: AgentMessage): AgentMe
217
405
  return {
218
406
  ...message,
219
407
  providerPayload: undefined,
220
- errorMessage: message.errorMessage === undefined ? undefined : o.obfuscate(message.errorMessage),
408
+ errorMessage:
409
+ message.errorMessage === undefined
410
+ ? undefined
411
+ : o.obfuscate(message.errorMessage, sharedRegexSecretValues),
221
412
  content: message.content.flatMap((block): AssistantMessage["content"] => {
222
413
  if (block.type === "redactedThinking") return [];
223
- if (block.type === "text") return [{ ...block, text: o.obfuscate(block.text) }];
224
- if (block.type === "thinking") return [{ ...block, thinking: o.obfuscate(block.thinking) }];
414
+ if (block.type === "text") return [{ ...block, text: o.obfuscate(block.text, sharedRegexSecretValues) }];
415
+ if (block.type === "thinking") {
416
+ return [{ ...block, thinking: o.obfuscate(block.thinking, sharedRegexSecretValues) }];
417
+ }
225
418
  if (block.type === "toolCall") {
226
419
  return [
227
420
  {
228
421
  ...block,
229
- arguments: obfuscateToolArguments(o, block.arguments),
230
- intent: block.intent === undefined ? undefined : o.obfuscate(block.intent),
231
- rawBlock: block.rawBlock === undefined ? undefined : o.obfuscate(block.rawBlock),
422
+ arguments: obfuscateToolArguments(o, block.arguments, sharedRegexSecretValues),
423
+ intent:
424
+ block.intent === undefined ? undefined : o.obfuscate(block.intent, sharedRegexSecretValues),
425
+ rawBlock:
426
+ block.rawBlock === undefined
427
+ ? undefined
428
+ : o.obfuscate(block.rawBlock, sharedRegexSecretValues),
232
429
  },
233
430
  ];
234
431
  }
@@ -238,37 +435,40 @@ function redactShareMessage(o: SecretObfuscator, message: AgentMessage): AgentMe
238
435
  case "bashExecution":
239
436
  return {
240
437
  ...message,
241
- command: o.obfuscate(message.command),
242
- output: o.obfuscate(message.output),
243
- meta: redactShareOutputMeta(o, message.meta),
438
+ command: o.obfuscate(message.command, sharedRegexSecretValues),
439
+ output: o.obfuscate(message.output, sharedRegexSecretValues),
440
+ meta: redactShareOutputMeta(o, message.meta, sharedRegexSecretValues),
244
441
  };
245
442
  case "pythonExecution":
246
443
  return {
247
444
  ...message,
248
- code: o.obfuscate(message.code),
249
- output: o.obfuscate(message.output),
250
- meta: redactShareOutputMeta(o, message.meta),
445
+ code: o.obfuscate(message.code, sharedRegexSecretValues),
446
+ output: o.obfuscate(message.output, sharedRegexSecretValues),
447
+ meta: redactShareOutputMeta(o, message.meta, sharedRegexSecretValues),
251
448
  };
252
449
  case "branchSummary":
253
- return { ...message, summary: o.obfuscate(message.summary) };
450
+ return { ...message, summary: o.obfuscate(message.summary, sharedRegexSecretValues) };
254
451
  case "compactionSummary":
255
452
  return {
256
453
  ...message,
257
454
  providerPayload: undefined,
258
- summary: o.obfuscate(message.summary),
259
- shortSummary: message.shortSummary === undefined ? undefined : o.obfuscate(message.shortSummary),
455
+ summary: o.obfuscate(message.summary, sharedRegexSecretValues),
456
+ shortSummary:
457
+ message.shortSummary === undefined
458
+ ? undefined
459
+ : o.obfuscate(message.shortSummary, sharedRegexSecretValues),
260
460
  blocks:
261
461
  message.blocks === undefined
262
462
  ? undefined
263
- : (redactShareContent(o, message.blocks) as (TextContent | ImageContent)[]),
463
+ : (redactShareContent(o, message.blocks, sharedRegexSecretValues) as (TextContent | ImageContent)[]),
264
464
  };
265
465
  case "fileMention":
266
466
  return {
267
467
  ...message,
268
468
  files: message.files.map(file => ({
269
469
  ...file,
270
- path: o.obfuscate(file.path),
271
- content: o.obfuscate(file.content),
470
+ path: o.obfuscate(file.path, sharedRegexSecretValues),
471
+ content: o.obfuscate(file.content, sharedRegexSecretValues),
272
472
  })),
273
473
  };
274
474
  default:
@@ -5,13 +5,22 @@ import type * as fs1 from "node:fs";
5
5
  import * as fs from "node:fs/promises";
6
6
  import * as path from "node:path";
7
7
  import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
8
- import type { ImageContent, Model, TextContent, TSchema } from "@oh-my-pi/pi-ai";
8
+ import type {
9
+ ImageContent,
10
+ Model,
11
+ ServiceTier,
12
+ ServiceTierByFamily,
13
+ ServiceTierFamily,
14
+ TextContent,
15
+ TSchema,
16
+ } from "@oh-my-pi/pi-ai";
9
17
  import type { KeyId } from "@oh-my-pi/pi-tui";
10
18
  import { hasFsCode, isEacces, isEnoent, logger } from "@oh-my-pi/pi-utils";
11
19
  import { Type } from "arktype";
12
20
  import * as zodModule from "zod/v4";
13
21
  import { type ExtensionModule, extensionModuleCapability } from "../../capability/extension-module";
14
22
  import { type Hook, hookCapability } from "../../capability/hook";
23
+ import { isServiceTierFamily, isServiceTierForFamily } from "../../config/service-tier";
15
24
  import { loadCapability } from "../../discovery";
16
25
  import { getExtensionNameFromPath } from "../../discovery/helpers";
17
26
  import type { ExecOptions } from "../../exec/exec";
@@ -107,6 +116,14 @@ export class ExtensionRuntime implements IExtensionRuntime {
107
116
  throw new ExtensionRuntimeNotInitializedError();
108
117
  }
109
118
 
119
+ getServiceTiers(): ServiceTierByFamily {
120
+ throw new ExtensionRuntimeNotInitializedError();
121
+ }
122
+
123
+ setServiceTier(): void {
124
+ throw new ExtensionRuntimeNotInitializedError();
125
+ }
126
+
110
127
  getSessionName(): string | undefined {
111
128
  throw new ExtensionRuntimeNotInitializedError();
112
129
  }
@@ -252,6 +269,17 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
252
269
  this.runtime.setThinkingLevel(level, persist);
253
270
  }
254
271
 
272
+ getServiceTiers(): Readonly<ServiceTierByFamily> {
273
+ return { ...this.runtime.getServiceTiers() };
274
+ }
275
+
276
+ setServiceTier(family: ServiceTierFamily, tier: ServiceTier | undefined): void {
277
+ if (!isServiceTierFamily(family) || (tier !== undefined && !isServiceTierForFamily(family, tier))) {
278
+ throw new TypeError(`Invalid service tier "${String(tier)}" for family "${String(family)}"`);
279
+ }
280
+ this.runtime.setServiceTier(family, tier);
281
+ }
282
+
255
283
  getSessionName(): string | undefined {
256
284
  return this.runtime.getSessionName();
257
285
  }
@@ -72,6 +72,10 @@ export type ExtensionErrorListener = (error: ExtensionError) => void;
72
72
  export const EXTENSION_HANDLER_TIMEOUT_MS = 30_000;
73
73
  let extensionHandlerTimeoutMs = EXTENSION_HANDLER_TIMEOUT_MS;
74
74
 
75
+ function throwUnsupportedServiceTierAction(): never {
76
+ throw new Error("This extension host does not support service-tier actions");
77
+ }
78
+
75
79
  export function testSetExtensionHandlerTimeoutMs(timeoutMs: number): void {
76
80
  extensionHandlerTimeoutMs = timeoutMs;
77
81
  }
@@ -298,6 +302,8 @@ export class ExtensionRunner {
298
302
  this.runtime.setModel = actions.setModel;
299
303
  this.runtime.getThinkingLevel = actions.getThinkingLevel;
300
304
  this.runtime.setThinkingLevel = actions.setThinkingLevel;
305
+ this.runtime.getServiceTiers = actions.getServiceTiers ?? throwUnsupportedServiceTierAction;
306
+ this.runtime.setServiceTier = actions.setServiceTier ?? throwUnsupportedServiceTierAction;
301
307
  this.runtime.getSessionName = actions.getSessionName;
302
308
  this.runtime.setSessionName = actions.setSessionName;
303
309
 
@@ -25,6 +25,9 @@ import type {
25
25
  Model,
26
26
  ModelSpec,
27
27
  ProviderResponseMetadata,
28
+ ServiceTier,
29
+ ServiceTierByFamily,
30
+ ServiceTierFamily,
28
31
  SimpleStreamOptions,
29
32
  Static,
30
33
  TextContent,
@@ -1044,6 +1047,13 @@ export interface RegisteredCommand {
1044
1047
  // biome-ignore lint/suspicious/noConfusingVoidType: void allows bare return statements
1045
1048
  export type ExtensionHandler<E, R = undefined> = (event: E, ctx: ExtensionContext) => Promise<R | void> | R | void;
1046
1049
 
1050
+ /** Service tiers accepted by each provider family. */
1051
+ export type ExtensionServiceTier<Family extends ServiceTierFamily> = Family extends "anthropic"
1052
+ ? "priority"
1053
+ : Family extends "google"
1054
+ ? "flex" | "priority"
1055
+ : ServiceTier;
1056
+
1047
1057
  /**
1048
1058
  * ExtensionAPI passed to extension factory functions.
1049
1059
  */
@@ -1230,6 +1240,18 @@ export interface ExtensionAPI {
1230
1240
  /** Set thinking level for the current session. */
1231
1241
  setThinkingLevel(level: ThinkingLevel): void;
1232
1242
 
1243
+ /** Get a snapshot of the current session's per-family service tiers. */
1244
+ getServiceTiers(): Readonly<ServiceTierByFamily>;
1245
+
1246
+ /**
1247
+ * Set one provider family's service tier for subsequent requests, or clear
1248
+ * its session override with `undefined`.
1249
+ */
1250
+ setServiceTier<Family extends ServiceTierFamily>(
1251
+ family: Family,
1252
+ tier: ExtensionServiceTier<Family> | undefined,
1253
+ ): void;
1254
+
1233
1255
  /** Get the current session name. */
1234
1256
  getSessionName(): string | undefined;
1235
1257
 
@@ -1409,6 +1431,10 @@ export type GetThinkingLevelHandler = () => ThinkingLevel | undefined;
1409
1431
 
1410
1432
  export type SetThinkingLevelHandler = (level: ThinkingLevel, persist?: boolean) => void;
1411
1433
 
1434
+ export type GetServiceTiersHandler = () => ServiceTierByFamily;
1435
+
1436
+ export type SetServiceTierHandler = (family: ServiceTierFamily, tier: ServiceTier | undefined) => void;
1437
+
1412
1438
  /** Shared state created by loader, used during registration and runtime. */
1413
1439
  export interface ExtensionRuntimeState {
1414
1440
  flagValues: Map<string, boolean | string>;
@@ -1429,6 +1455,8 @@ export interface ExtensionActions {
1429
1455
  setModel: SetModelHandler;
1430
1456
  getThinkingLevel: GetThinkingLevelHandler;
1431
1457
  setThinkingLevel: SetThinkingLevelHandler;
1458
+ getServiceTiers?: GetServiceTiersHandler;
1459
+ setServiceTier?: SetServiceTierHandler;
1432
1460
  getSessionName: () => string | undefined;
1433
1461
  setSessionName: (name: string) => Promise<void>;
1434
1462
  }
@@ -1460,8 +1488,11 @@ export interface ExtensionCommandContextActions {
1460
1488
  reload: () => Promise<void>;
1461
1489
  }
1462
1490
 
1463
- /** Full runtime = state + actions. */
1464
- export interface ExtensionRuntime extends ExtensionRuntimeState, ExtensionActions {}
1491
+ /** Full runtime = state + actions, including host-compatible service-tier fallbacks. */
1492
+ export interface ExtensionRuntime extends ExtensionRuntimeState, ExtensionActions {
1493
+ getServiceTiers: GetServiceTiersHandler;
1494
+ setServiceTier: SetServiceTierHandler;
1495
+ }
1465
1496
 
1466
1497
  /** Loaded extension with all registered items. */
1467
1498
  export interface Extension {
@@ -1504,7 +1504,25 @@ function evaluateGraphCommonJs(modulePath: string): unknown {
1504
1504
  }
1505
1505
  }
1506
1506
 
1507
- Reflect.set(globalThis, COMMONJS_REQUIRE_GLOBAL, evaluateGraphCommonJs);
1507
+ /**
1508
+ * Register {@link evaluateGraphCommonJs} as the graph-owned CommonJS require
1509
+ * bridge on `globalThis`, first-wins.
1510
+ *
1511
+ * On source-link installs the `@(scope)/pi-coding-agent` root shim is served
1512
+ * from `src/`, so an extension import can evaluate a second instance of this
1513
+ * module with empty graph state. An unconditional set would let that empty
1514
+ * instance clobber the host bundle's populated bridge and break transitive
1515
+ * CommonJS resolution (#6449); guarding preserves the first (host-owned)
1516
+ * registration. Idempotent: a subsequent call with a value already present is a
1517
+ * no-op.
1518
+ */
1519
+ export function ensureGraphCommonJsRequireRegistered(): void {
1520
+ if (!Reflect.get(globalThis, COMMONJS_REQUIRE_GLOBAL)) {
1521
+ Reflect.set(globalThis, COMMONJS_REQUIRE_GLOBAL, evaluateGraphCommonJs);
1522
+ }
1523
+ }
1524
+
1525
+ ensureGraphCommonJsRequireRegistered();
1508
1526
 
1509
1527
  let legacyPiLoadTag = 0;
1510
1528