@oh-my-pi/pi-coding-agent 15.12.3 → 15.12.4

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 (231) hide show
  1. package/CHANGELOG.md +43 -1
  2. package/dist/cli.js +1120 -870
  3. package/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
  4. package/dist/types/autoresearch/tools/log-experiment.d.ts +1 -1
  5. package/dist/types/autoresearch/tools/run-experiment.d.ts +1 -1
  6. package/dist/types/autoresearch/tools/update-notes.d.ts +1 -1
  7. package/dist/types/cli/args.d.ts +0 -1
  8. package/dist/types/cli/models-cli.d.ts +49 -0
  9. package/dist/types/commands/launch.d.ts +0 -3
  10. package/dist/types/commands/models.d.ts +33 -0
  11. package/dist/types/commands/token.d.ts +25 -0
  12. package/dist/types/commit/agentic/tools/analyze-file.d.ts +1 -1
  13. package/dist/types/commit/agentic/tools/git-file-diff.d.ts +1 -1
  14. package/dist/types/commit/agentic/tools/git-hunk.d.ts +1 -1
  15. package/dist/types/commit/agentic/tools/git-overview.d.ts +1 -1
  16. package/dist/types/commit/agentic/tools/propose-changelog.d.ts +1 -1
  17. package/dist/types/commit/agentic/tools/propose-commit.d.ts +1 -1
  18. package/dist/types/commit/agentic/tools/recent-commits.d.ts +1 -1
  19. package/dist/types/commit/agentic/tools/schemas.d.ts +1 -1
  20. package/dist/types/commit/agentic/tools/split-commit.d.ts +1 -1
  21. package/dist/types/commit/changelog/generate.d.ts +1 -1
  22. package/dist/types/commit/shared-llm.d.ts +1 -1
  23. package/dist/types/config/model-registry.d.ts +7 -0
  24. package/dist/types/config/models-config-schema.d.ts +1 -1
  25. package/dist/types/config/settings-schema.d.ts +20 -0
  26. package/dist/types/edit/hashline/params.d.ts +1 -1
  27. package/dist/types/edit/modes/apply-patch.d.ts +1 -1
  28. package/dist/types/edit/modes/patch.d.ts +1 -1
  29. package/dist/types/edit/modes/replace.d.ts +1 -1
  30. package/dist/types/extensibility/custom-commands/types.d.ts +2 -2
  31. package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
  32. package/dist/types/extensibility/extensions/types.d.ts +2 -2
  33. package/dist/types/extensibility/hooks/types.d.ts +2 -2
  34. package/dist/types/goals/tools/goal-tool.d.ts +1 -1
  35. package/dist/types/lsp/types.d.ts +1 -1
  36. package/dist/types/mcp/manager.d.ts +8 -0
  37. package/dist/types/mnemopi/config.d.ts +28 -0
  38. package/dist/types/modes/acp/acp-agent.d.ts +1 -2
  39. package/dist/types/modes/components/index.d.ts +1 -0
  40. package/dist/types/modes/components/logout-account-selector.d.ts +8 -0
  41. package/dist/types/modes/components/status-line/component.d.ts +9 -5
  42. package/dist/types/modes/components/status-line/types.d.ts +2 -1
  43. package/dist/types/modes/controllers/event-controller.d.ts +0 -17
  44. package/dist/types/modes/interactive-mode.d.ts +0 -3
  45. package/dist/types/modes/types.d.ts +0 -5
  46. package/dist/types/session/agent-session.d.ts +14 -33
  47. package/dist/types/session/agent-storage.d.ts +2 -1
  48. package/dist/types/session/indexed-session-storage.d.ts +1 -0
  49. package/dist/types/session/messages.d.ts +8 -10
  50. package/dist/types/session/session-manager.d.ts +15 -0
  51. package/dist/types/session/session-storage.d.ts +5 -0
  52. package/dist/types/slash-commands/helpers/logout.d.ts +15 -0
  53. package/dist/types/task/types.d.ts +1 -1
  54. package/dist/types/tools/ask.d.ts +1 -1
  55. package/dist/types/tools/ast-edit.d.ts +1 -1
  56. package/dist/types/tools/ast-grep.d.ts +1 -1
  57. package/dist/types/tools/bash.d.ts +1 -1
  58. package/dist/types/tools/browser/cmux/cmux-tab.d.ts +202 -0
  59. package/dist/types/tools/browser/cmux/rpc.d.ts +70 -0
  60. package/dist/types/tools/browser/cmux/socket-client.d.ts +19 -0
  61. package/dist/types/tools/browser/registry.d.ts +16 -3
  62. package/dist/types/tools/browser/render.d.ts +2 -0
  63. package/dist/types/tools/browser/tab-protocol.d.ts +2 -0
  64. package/dist/types/tools/browser/tab-supervisor.d.ts +16 -4
  65. package/dist/types/tools/browser.d.ts +3 -1
  66. package/dist/types/tools/checkpoint.d.ts +1 -1
  67. package/dist/types/tools/debug.d.ts +1 -1
  68. package/dist/types/tools/eval.d.ts +1 -1
  69. package/dist/types/tools/find.d.ts +1 -1
  70. package/dist/types/tools/gh.d.ts +1 -1
  71. package/dist/types/tools/image-gen.d.ts +1 -1
  72. package/dist/types/tools/index.d.ts +3 -1
  73. package/dist/types/tools/inspect-image.d.ts +1 -1
  74. package/dist/types/tools/irc.d.ts +1 -1
  75. package/dist/types/tools/job.d.ts +1 -1
  76. package/dist/types/tools/memory-edit.d.ts +1 -1
  77. package/dist/types/tools/memory-recall.d.ts +1 -1
  78. package/dist/types/tools/memory-reflect.d.ts +1 -1
  79. package/dist/types/tools/memory-retain.d.ts +1 -1
  80. package/dist/types/tools/read.d.ts +1 -1
  81. package/dist/types/tools/render-mermaid.d.ts +1 -1
  82. package/dist/types/tools/resolve.d.ts +1 -1
  83. package/dist/types/tools/review.d.ts +1 -1
  84. package/dist/types/tools/search-tool-bm25.d.ts +1 -1
  85. package/dist/types/tools/search.d.ts +1 -1
  86. package/dist/types/tools/ssh.d.ts +1 -1
  87. package/dist/types/tools/todo.d.ts +1 -1
  88. package/dist/types/tools/tts.d.ts +1 -1
  89. package/dist/types/tools/write.d.ts +1 -1
  90. package/dist/types/utils/clipboard.d.ts +4 -3
  91. package/dist/types/utils/image-loading.d.ts +18 -1
  92. package/dist/types/utils/thinking-display.d.ts +17 -0
  93. package/dist/types/web/search/index.d.ts +1 -1
  94. package/package.json +14 -14
  95. package/src/autoresearch/storage.ts +2 -1
  96. package/src/autoresearch/tools/init-experiment.ts +1 -1
  97. package/src/autoresearch/tools/log-experiment.ts +1 -1
  98. package/src/autoresearch/tools/run-experiment.ts +1 -1
  99. package/src/autoresearch/tools/update-notes.ts +1 -1
  100. package/src/cli/args.ts +0 -8
  101. package/src/cli/auth-gateway-cli.ts +1 -1
  102. package/src/cli/bench-cli.ts +1 -1
  103. package/src/cli/dry-balance-cli.ts +1 -1
  104. package/src/cli/models-cli.ts +427 -0
  105. package/src/cli-commands.ts +2 -0
  106. package/src/collab/host.ts +9 -12
  107. package/src/commands/launch.ts +0 -3
  108. package/src/commands/models.ts +61 -0
  109. package/src/commands/token.ts +89 -0
  110. package/src/commit/agentic/tools/analyze-file.ts +1 -1
  111. package/src/commit/agentic/tools/git-file-diff.ts +1 -1
  112. package/src/commit/agentic/tools/git-hunk.ts +1 -1
  113. package/src/commit/agentic/tools/git-overview.ts +1 -1
  114. package/src/commit/agentic/tools/propose-changelog.ts +1 -1
  115. package/src/commit/agentic/tools/propose-commit.ts +1 -1
  116. package/src/commit/agentic/tools/recent-commits.ts +1 -1
  117. package/src/commit/agentic/tools/schemas.ts +1 -1
  118. package/src/commit/agentic/tools/split-commit.ts +1 -1
  119. package/src/commit/analysis/summary.ts +1 -1
  120. package/src/commit/changelog/generate.ts +1 -1
  121. package/src/commit/shared-llm.ts +1 -1
  122. package/src/config/model-registry.ts +15 -12
  123. package/src/config/model-resolver.ts +2 -2
  124. package/src/config/models-config-schema.ts +1 -1
  125. package/src/config/settings-schema.ts +18 -0
  126. package/src/edit/hashline/params.ts +1 -1
  127. package/src/edit/modes/apply-patch.ts +1 -1
  128. package/src/edit/modes/patch.ts +1 -1
  129. package/src/edit/modes/replace.ts +1 -1
  130. package/src/eval/agent-bridge.ts +1 -1
  131. package/src/eval/completion-bridge.ts +1 -1
  132. package/src/export/html/template.js +24 -2
  133. package/src/export/html/tool-views.generated.js +2 -2
  134. package/src/extensibility/custom-commands/loader.ts +1 -1
  135. package/src/extensibility/custom-commands/types.ts +2 -2
  136. package/src/extensibility/custom-tools/loader.ts +1 -1
  137. package/src/extensibility/custom-tools/types.ts +2 -2
  138. package/src/extensibility/extensions/loader.ts +2 -2
  139. package/src/extensibility/extensions/types.ts +2 -2
  140. package/src/extensibility/hooks/loader.ts +1 -1
  141. package/src/extensibility/hooks/types.ts +2 -2
  142. package/src/extensibility/skills.ts +18 -3
  143. package/src/goals/tools/goal-tool.ts +1 -1
  144. package/src/internal-urls/docs-index.generated.ts +5 -2
  145. package/src/lsp/types.ts +1 -1
  146. package/src/main.ts +0 -25
  147. package/src/mcp/config-writer.ts +7 -3
  148. package/src/mcp/manager.ts +11 -0
  149. package/src/memories/index.ts +3 -1
  150. package/src/memories/storage.ts +2 -1
  151. package/src/mnemopi/config.ts +95 -11
  152. package/src/modes/acp/acp-agent.ts +5 -48
  153. package/src/modes/acp/acp-event-mapper.ts +5 -1
  154. package/src/modes/components/agent-hub.ts +2 -1
  155. package/src/modes/components/assistant-message.ts +8 -7
  156. package/src/modes/components/index.ts +1 -0
  157. package/src/modes/components/logout-account-selector.ts +130 -0
  158. package/src/modes/components/mcp-add-wizard.ts +1 -1
  159. package/src/modes/components/model-selector.ts +2 -2
  160. package/src/modes/components/status-line/component.ts +54 -157
  161. package/src/modes/components/status-line/segments.ts +1 -1
  162. package/src/modes/components/status-line/types.ts +2 -1
  163. package/src/modes/controllers/command-controller.ts +0 -12
  164. package/src/modes/controllers/event-controller.ts +23 -62
  165. package/src/modes/controllers/input-controller.ts +53 -30
  166. package/src/modes/controllers/mcp-command-controller.ts +44 -3
  167. package/src/modes/controllers/selector-controller.ts +56 -10
  168. package/src/modes/controllers/streaming-reveal.ts +4 -3
  169. package/src/modes/interactive-mode.ts +2 -8
  170. package/src/modes/theme/theme.ts +1 -1
  171. package/src/modes/types.ts +0 -5
  172. package/src/modes/utils/ui-helpers.ts +2 -1
  173. package/src/prompts/system/empty-stop-retry.md +4 -6
  174. package/src/sdk.ts +15 -19
  175. package/src/session/agent-session.ts +125 -234
  176. package/src/session/agent-storage.ts +18 -9
  177. package/src/session/history-storage.ts +2 -1
  178. package/src/session/indexed-session-storage.ts +7 -0
  179. package/src/session/messages.ts +9 -11
  180. package/src/session/session-dump-format.ts +4 -2
  181. package/src/session/session-manager.ts +116 -0
  182. package/src/session/session-storage.ts +20 -0
  183. package/src/slash-commands/builtin-registry.ts +15 -1
  184. package/src/slash-commands/helpers/logout.ts +88 -0
  185. package/src/task/types.ts +1 -1
  186. package/src/tools/ask.ts +1 -1
  187. package/src/tools/ast-edit.ts +1 -1
  188. package/src/tools/ast-grep.ts +1 -1
  189. package/src/tools/bash.ts +1 -1
  190. package/src/tools/browser/cmux/cmux-tab.ts +1264 -0
  191. package/src/tools/browser/cmux/rpc.ts +156 -0
  192. package/src/tools/browser/cmux/socket-client.ts +309 -0
  193. package/src/tools/browser/registry.ts +37 -3
  194. package/src/tools/browser/render.ts +6 -1
  195. package/src/tools/browser/tab-protocol.ts +2 -0
  196. package/src/tools/browser/tab-supervisor.ts +189 -18
  197. package/src/tools/browser/tab-worker.ts +1 -1
  198. package/src/tools/browser.ts +16 -1
  199. package/src/tools/checkpoint.ts +1 -1
  200. package/src/tools/debug.ts +1 -1
  201. package/src/tools/eval.ts +11 -6
  202. package/src/tools/fetch.ts +13 -2
  203. package/src/tools/find.ts +1 -1
  204. package/src/tools/gh.ts +1 -1
  205. package/src/tools/github-cache.ts +2 -1
  206. package/src/tools/image-gen.ts +1 -1
  207. package/src/tools/index.ts +3 -1
  208. package/src/tools/inspect-image.ts +3 -1
  209. package/src/tools/irc.ts +1 -1
  210. package/src/tools/job.ts +1 -1
  211. package/src/tools/memory-edit.ts +1 -1
  212. package/src/tools/memory-recall.ts +1 -1
  213. package/src/tools/memory-reflect.ts +1 -1
  214. package/src/tools/memory-retain.ts +1 -1
  215. package/src/tools/read.ts +8 -2
  216. package/src/tools/render-mermaid.ts +1 -1
  217. package/src/tools/report-tool-issue.ts +3 -2
  218. package/src/tools/resolve.ts +1 -1
  219. package/src/tools/review.ts +1 -1
  220. package/src/tools/search-tool-bm25.ts +1 -1
  221. package/src/tools/search.ts +1 -1
  222. package/src/tools/ssh.ts +1 -1
  223. package/src/tools/todo.ts +1 -1
  224. package/src/tools/tts.ts +1 -1
  225. package/src/tools/write.ts +1 -1
  226. package/src/utils/clipboard.ts +35 -18
  227. package/src/utils/image-loading.ts +35 -4
  228. package/src/utils/thinking-display.ts +37 -0
  229. package/src/web/search/index.ts +1 -1
  230. package/dist/types/cli/list-models.d.ts +0 -30
  231. package/src/cli/list-models.ts +0 -194
@@ -2,10 +2,21 @@ import { getPuppeteerDir, logger, Snowflake, workerHostEntry } from "@oh-my-pi/p
2
2
  import type { Page, Target } from "puppeteer-core";
3
3
  import { callSessionTool } from "../../eval/js/tool-bridge";
4
4
  import type { ToolSession } from "../../sdk";
5
+ import { webpExclusionForModel } from "../../utils/image-loading";
5
6
  import { expandPath } from "../path-utils";
6
7
  import { ToolAbortError, ToolError } from "../tool-errors";
7
8
  import { pickElectronTarget } from "./attach";
8
- import { type BrowserHandle, type BrowserKindTag, holdBrowser, releaseBrowser } from "./registry";
9
+ import { CmuxTab, runCmuxCode } from "./cmux/cmux-tab";
10
+ import { mapWaitUntil } from "./cmux/rpc";
11
+ import { DEFAULT_VIEWPORT } from "./launch";
12
+ import {
13
+ type BrowserHandle,
14
+ type BrowserKindTag,
15
+ type CmuxBrowserHandle,
16
+ holdBrowser,
17
+ type PuppeteerBrowserHandle,
18
+ releaseBrowser,
19
+ } from "./registry";
9
20
  import type {
10
21
  ReadyInfo,
11
22
  RunErrorPayload,
@@ -39,11 +50,10 @@ export interface PendingRun {
39
50
  toolCalls: Map<string, AbortController>;
40
51
  }
41
52
 
42
- export interface TabSession {
53
+ interface TabSessionBase<TBrowser extends BrowserHandle = BrowserHandle> {
43
54
  name: string;
44
- browser: BrowserHandle;
55
+ browser: TBrowser;
45
56
  targetId: string;
46
- worker: WorkerHandle;
47
57
  state: "alive" | "dead";
48
58
  info: ReadyInfo;
49
59
  pending: Map<string, PendingRun>;
@@ -51,6 +61,20 @@ export interface TabSession {
51
61
  kindTag: BrowserKindTag;
52
62
  }
53
63
 
64
+ export interface WorkerTabSession extends TabSessionBase<PuppeteerBrowserHandle> {
65
+ backend: "worker";
66
+ worker: WorkerHandle;
67
+ }
68
+
69
+ export interface CmuxTabSession extends TabSessionBase<CmuxBrowserHandle> {
70
+ backend: "cmux";
71
+ cmuxTab: CmuxTab;
72
+ cmuxOwnsSurface: boolean;
73
+ cmuxAttachedSurface?: string;
74
+ }
75
+
76
+ export type TabSession = WorkerTabSession | CmuxTabSession;
77
+
54
78
  export interface AcquireTabOptions {
55
79
  url?: string;
56
80
  waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
@@ -59,6 +83,7 @@ export interface AcquireTabOptions {
59
83
  signal?: AbortSignal;
60
84
  timeoutMs: number;
61
85
  dialogs?: DialogPolicy;
86
+ cmuxSurface?: string;
62
87
  }
63
88
 
64
89
  export interface AcquireTabResult {
@@ -120,13 +145,18 @@ async function acquireTabImpl(
120
145
  const existing = tabs.get(name);
121
146
  if (existing) {
122
147
  if (existing.browser === browser && existing.state === "alive") {
123
- if (opts.dialogs !== undefined && opts.dialogs !== existing.dialogPolicy) {
148
+ const requestedCmuxSurface = "client" in browser ? (opts.cmuxSurface ?? browser.surface) : undefined;
149
+ if (existing.backend === "cmux" && existing.cmuxAttachedSurface !== requestedCmuxSurface) {
150
+ holdBrowser(browser);
151
+ tempHold = true;
152
+ await releaseTab(name, { kill: false });
153
+ } else if (opts.dialogs !== undefined && opts.dialogs !== existing.dialogPolicy) {
124
154
  holdBrowser(browser);
125
155
  tempHold = true;
126
156
  await releaseTab(name, { kill: false });
127
157
  } else {
128
158
  const reuseSteps: string[] = [];
129
- if (opts.viewport) {
159
+ if (opts.viewport && browser.kind.kind !== "cmux") {
130
160
  const dsf = opts.viewport.deviceScaleFactor;
131
161
  reuseSteps.push(
132
162
  `await page.setViewport({ width: ${opts.viewport.width}, height: ${opts.viewport.height}, deviceScaleFactor: ${dsf === undefined ? "undefined" : String(dsf)} });`,
@@ -159,6 +189,16 @@ async function acquireTabImpl(
159
189
  }
160
190
  }
161
191
 
192
+ if ("client" in browser) {
193
+ try {
194
+ const result = await acquireCmuxTab(name, browser, opts);
195
+ if (tempHold) await releaseBrowser(browser, { kill: false });
196
+ return result;
197
+ } catch (error) {
198
+ if (tempHold || browser.refCount === 0) await releaseBrowser(browser, { kill: false });
199
+ throw error;
200
+ }
201
+ }
162
202
  let initPayload: WorkerInitPayload;
163
203
  let worker: WorkerHandle;
164
204
  try {
@@ -201,10 +241,11 @@ async function acquireTabImpl(
201
241
 
202
242
  holdBrowser(browser);
203
243
  if (tempHold) await releaseBrowser(browser, { kill: false });
204
- const tab: TabSession = {
244
+ const tab: WorkerTabSession = {
205
245
  name,
206
246
  browser,
207
247
  targetId: info.targetId,
248
+ backend: "worker",
208
249
  worker,
209
250
  state: "alive",
210
251
  info,
@@ -217,11 +258,85 @@ async function acquireTabImpl(
217
258
  return { tab, created: true };
218
259
  }
219
260
 
261
+ async function acquireCmuxTab(
262
+ name: string,
263
+ browser: CmuxBrowserHandle,
264
+ opts: AcquireTabOptions,
265
+ ): Promise<AcquireTabResult> {
266
+ const attachedSurface = opts.cmuxSurface ?? browser.surface;
267
+ if (attachedSurface?.startsWith("surface:")) {
268
+ throw new ToolError(
269
+ "app.surface must be a surface UUID (e.g. CMUX_SURFACE_ID), not a 'surface:N' ref; omit it to open a new split",
270
+ );
271
+ }
272
+
273
+ let surfaceId = attachedSurface;
274
+ let initialUrl = opts.url;
275
+ let ownsSurface = false;
276
+ try {
277
+ if (!surfaceId) {
278
+ const params: Record<string, unknown> = { url: opts.url ?? "about:blank", focus: false };
279
+ if (process.env.CMUX_WORKSPACE_ID) params.workspace_id = process.env.CMUX_WORKSPACE_ID;
280
+ if (process.env.CMUX_SURFACE_ID) params.surface_id = process.env.CMUX_SURFACE_ID;
281
+ const result = await browser.client.request("browser.open_split", params, { timeoutMs: opts.timeoutMs });
282
+ if (typeof result.surface_id !== "string" || result.surface_id.length === 0) {
283
+ throw new ToolError("cmux browser.open_split did not return a surface_id");
284
+ }
285
+ surfaceId = result.surface_id;
286
+ ownsSurface = true;
287
+ if (typeof result.url === "string" && result.url.length > 0) initialUrl = result.url;
288
+ if (opts.url) {
289
+ await browser.client.request(
290
+ "browser.wait",
291
+ {
292
+ surface_id: surfaceId,
293
+ load_state: mapWaitUntil(opts.waitUntil ?? "load"),
294
+ timeout_ms: opts.timeoutMs,
295
+ },
296
+ { timeoutMs: opts.timeoutMs },
297
+ );
298
+ }
299
+ }
300
+
301
+ const cmuxTab = new CmuxTab({ client: browser.client, surfaceId, url: initialUrl });
302
+ if (attachedSurface && opts.url) {
303
+ await cmuxTab.goto(opts.url, { waitUntil: opts.waitUntil ?? "load", timeoutMs: opts.timeoutMs });
304
+ }
305
+ const info = await cmuxTab.readyInfo(opts.viewport ?? DEFAULT_VIEWPORT);
306
+ holdBrowser(browser);
307
+ const tab: CmuxTabSession = {
308
+ name,
309
+ browser,
310
+ targetId: surfaceId,
311
+ backend: "cmux",
312
+ cmuxTab,
313
+ cmuxOwnsSurface: ownsSurface,
314
+ state: "alive",
315
+ info,
316
+ pending: new Map(),
317
+ dialogPolicy: opts.dialogs,
318
+ kindTag: browser.kind.kind,
319
+ cmuxAttachedSurface: attachedSurface,
320
+ };
321
+ tabs.set(name, tab);
322
+ return { tab, created: true };
323
+ } catch (error) {
324
+ if (ownsSurface && surfaceId) {
325
+ await browser.client.request("surface.close", { surface_id: surfaceId }).catch(() => undefined);
326
+ }
327
+ throw error;
328
+ }
329
+ }
330
+
220
331
  export async function runInTab(name: string, opts: RunInTabOptions): Promise<RunResultOk> {
221
332
  return await runInTabWithSnapshot(
222
333
  name,
223
334
  { code: opts.code, timeoutMs: opts.timeoutMs, signal: opts.signal, session: opts.session },
224
- { cwd: opts.session.cwd, browserScreenshotDir: expandBrowserScreenshotDir(opts.session) },
335
+ {
336
+ cwd: opts.session.cwd,
337
+ browserScreenshotDir: expandBrowserScreenshotDir(opts.session),
338
+ excludeWebP: webpExclusionForModel(opts.session.getActiveModel?.()),
339
+ },
225
340
  );
226
341
  }
227
342
 
@@ -243,6 +358,19 @@ async function runInTabWithSnapshot(
243
358
  toolCalls: new Map(),
244
359
  };
245
360
  tab.pending.set(id, pending);
361
+ if (tab.backend === "cmux") {
362
+ try {
363
+ return await runCmuxCode(tab.cmuxTab, {
364
+ code: opts.code,
365
+ timeoutMs: opts.timeoutMs,
366
+ signal: opts.signal,
367
+ session: pending.session,
368
+ snapshot,
369
+ });
370
+ } finally {
371
+ tab.pending.delete(id);
372
+ }
373
+ }
246
374
  const abort = (): void => {
247
375
  tab.worker.send({ type: "abort", id });
248
376
  for (const ctrl of pending.toolCalls.values()) ctrl.abort(opts.signal?.reason);
@@ -250,7 +378,14 @@ async function runInTabWithSnapshot(
250
378
  if (opts.signal?.aborted) abort();
251
379
  else opts.signal?.addEventListener("abort", abort, { once: true });
252
380
  try {
253
- tab.worker.send({ type: "run", id, name, code: opts.code, timeoutMs: opts.timeoutMs, session: snapshot });
381
+ tab.worker.send({
382
+ type: "run",
383
+ id,
384
+ name,
385
+ code: opts.code,
386
+ timeoutMs: opts.timeoutMs,
387
+ session: snapshot,
388
+ });
254
389
  return await raceWithTimeout(
255
390
  promise,
256
391
  opts.timeoutMs + GRACE_MS,
@@ -273,12 +408,35 @@ export async function releaseTab(name: string, opts: ReleaseTabOptions = {}): Pr
273
408
  tab.state = "dead";
274
409
  const closeError = new ToolError(`Tab ${JSON.stringify(name)} was closed`);
275
410
  for (const [id, pending] of tab.pending) {
276
- try {
277
- tab.worker.send({ type: "abort", id });
278
- } catch {}
411
+ if (tab.backend === "worker") {
412
+ try {
413
+ tab.worker.send({ type: "abort", id });
414
+ } catch {}
415
+ }
416
+ for (const ctrl of pending.toolCalls.values()) ctrl.abort(closeError);
279
417
  pending.reject(closeError);
280
418
  }
281
419
  tab.pending.clear();
420
+ if (tab.backend === "cmux") {
421
+ let nonLastCloseError: unknown;
422
+ if (wasAlive && tab.cmuxOwnsSurface) {
423
+ try {
424
+ await tab.browser.client.request("surface.close", { surface_id: tab.targetId });
425
+ } catch (err) {
426
+ if (isLastSurfaceCloseError(err)) {
427
+ logger.debug("Leaving cmux browser surface open because it is the last surface in the workspace", {
428
+ error: err instanceof Error ? err.message : String(err),
429
+ });
430
+ } else {
431
+ nonLastCloseError = err;
432
+ }
433
+ }
434
+ }
435
+ await releaseBrowser(tab.browser, { kill: opts.kill ?? false });
436
+ tabs.delete(name);
437
+ if (nonLastCloseError) throw nonLastCloseError;
438
+ return true;
439
+ }
282
440
  let forced = false;
283
441
  if (wasAlive) {
284
442
  try {
@@ -309,7 +467,12 @@ export async function dropHeadlessTabs(): Promise<void> {
309
467
  for (const name of names) await releaseTab(name);
310
468
  }
311
469
 
312
- async function buildInitPayload(browser: BrowserHandle, opts: AcquireTabOptions): Promise<WorkerInitPayload> {
470
+ function isLastSurfaceCloseError(err: unknown): boolean {
471
+ const message = err instanceof Error ? err.message : String(err);
472
+ return /last/i.test(message);
473
+ }
474
+
475
+ async function buildInitPayload(browser: PuppeteerBrowserHandle, opts: AcquireTabOptions): Promise<WorkerInitPayload> {
313
476
  const safeDir = getPuppeteerDir();
314
477
  const browserWSEndpoint = browser.browser.wsEndpoint();
315
478
  if (!browserWSEndpoint) throw new ToolError("Browser websocket endpoint is unavailable");
@@ -336,7 +499,7 @@ async function buildInitPayload(browser: BrowserHandle, opts: AcquireTabOptions)
336
499
  };
337
500
  }
338
501
 
339
- function handleTabMessage(tab: TabSession, msg: WorkerOutbound): void {
502
+ function handleTabMessage(tab: WorkerTabSession, msg: WorkerOutbound): void {
340
503
  if (msg.type === "result") {
341
504
  const pending = tab.pending.get(msg.id);
342
505
  if (!pending) return;
@@ -359,7 +522,10 @@ function handleTabMessage(tab: TabSession, msg: WorkerOutbound): void {
359
522
  if (msg.type === "log") logWorkerMessage(msg);
360
523
  }
361
524
 
362
- async function dispatchToolCall(tab: TabSession, msg: Extract<WorkerOutbound, { type: "tool-call" }>): Promise<void> {
525
+ async function dispatchToolCall(
526
+ tab: WorkerTabSession,
527
+ msg: Extract<WorkerOutbound, { type: "tool-call" }>,
528
+ ): Promise<void> {
363
529
  const pending = tab.pending.get(msg.runId);
364
530
  if (!pending?.session.cwd) {
365
531
  safeSend(tab, {
@@ -395,7 +561,7 @@ async function dispatchToolCall(tab: TabSession, msg: Extract<WorkerOutbound, {
395
561
  }
396
562
  }
397
563
 
398
- function safeSend(tab: TabSession, msg: WorkerInbound): void {
564
+ function safeSend(tab: WorkerTabSession, msg: WorkerInbound): void {
399
565
  if (tab.state !== "alive") return;
400
566
  try {
401
567
  tab.worker.send(msg);
@@ -424,13 +590,18 @@ async function forceKillTab(name: string, reason: string): Promise<void> {
424
590
  const error = new ToolError(reason);
425
591
  for (const pending of tab.pending.values()) pending.reject(error);
426
592
  tab.pending.clear();
593
+ if (tab.backend === "cmux") {
594
+ await releaseBrowser(tab.browser, { kill: false });
595
+ tabs.delete(name);
596
+ return;
597
+ }
427
598
  await tab.worker.terminate().catch(() => undefined);
428
599
  if (tab.kindTag === "headless") await closeOrphanTarget(tab);
429
600
  await releaseBrowser(tab.browser, { kill: false });
430
601
  tabs.delete(name);
431
602
  }
432
603
 
433
- async function closeOrphanTarget(tab: TabSession): Promise<void> {
604
+ async function closeOrphanTarget(tab: WorkerTabSession): Promise<void> {
434
605
  for (const target of tab.browser.browser.targets()) {
435
606
  if ((await targetIdForTarget(target).catch(() => "")) !== tab.targetId) continue;
436
607
  const page = await target.page().catch(() => null);
@@ -439,7 +610,7 @@ async function closeOrphanTarget(tab: TabSession): Promise<void> {
439
610
  }
440
611
  }
441
612
 
442
- async function waitForClosed(tab: TabSession): Promise<void> {
613
+ async function waitForClosed(tab: WorkerTabSession): Promise<void> {
443
614
  const { promise, resolve } = Promise.withResolvers<void>();
444
615
  const unsubscribe = tab.worker.onMessage(msg => {
445
616
  if (msg.type === "closed") resolve();
@@ -981,7 +981,7 @@ export class WorkerCore {
981
981
  }
982
982
  const resized = await resizeImage(
983
983
  { type: "image", data: buffer.toBase64(), mimeType: captureMime },
984
- { maxWidth: 1024, maxHeight: 1024, maxBytes: 150 * 1024, jpegQuality: 70 },
984
+ { maxWidth: 1024, maxHeight: 1024, maxBytes: 150 * 1024, jpegQuality: 70, excludeWebP: session.excludeWebP },
985
985
  );
986
986
  const saveFullRes = !!(explicitPath || session.browserScreenshotDir);
987
987
  const savedBuffer = saveFullRes ? buffer : resized.buffer;
@@ -1,10 +1,11 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
2
  import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
3
- import * as z from "zod/v4";
3
+ import { z } from "zod/v4";
4
4
  import browserDescription from "../prompts/tools/browser.md" with { type: "text" };
5
5
  import type { ToolSession } from "../sdk";
6
6
  import { enforceInlineByteCap } from "../session/streaming-output";
7
7
  import { truncateForPrompt } from "./approval";
8
+ import { resolveCmuxKind } from "./browser/cmux/rpc";
8
9
  import { acquireBrowser, type BrowserHandle, type BrowserKind, type BrowserKindTag } from "./browser/registry";
9
10
  import type { Observation, ScreenshotResult } from "./browser/tab-protocol";
10
11
  import { acquireTab, dropHeadlessTabs, getTab, releaseAllTabs, releaseTab, runInTab } from "./browser/tab-supervisor";
@@ -14,6 +15,8 @@ import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
14
15
  import { toolResult } from "./tool-result";
15
16
  import { clampTimeout } from "./tool-timeouts";
16
17
 
18
+ export { cmuxSnapshotToObservation, mapWaitUntil, resolveCmuxKind, serializeEval } from "./browser/cmux/rpc";
19
+ export { CmuxSocketClient } from "./browser/cmux/socket-client";
17
20
  export { extractReadableFromHtml, type ReadableFormat, type ReadableResult } from "./browser/readable";
18
21
  export type { Observation, ObservationEntry } from "./browser/tab-protocol";
19
22
 
@@ -77,6 +80,12 @@ function resolveBrowserKind(params: BrowserParams, session: ToolSession): Browse
77
80
  const exe = resolveToCwd(app.path, session.cwd);
78
81
  return { kind: "spawned", path: exe };
79
82
  }
83
+ const cmuxKind = resolveCmuxKind({
84
+ settingEnabled: session.settings.get("browser.cmux") as boolean | undefined,
85
+ });
86
+ if (cmuxKind) {
87
+ return cmuxKind;
88
+ }
80
89
  const headless = session.settings.get("browser.headless") as boolean;
81
90
  return { kind: "headless", headless };
82
91
  }
@@ -304,6 +313,9 @@ async function saveBrowserOutputArtifact(session: ToolSession, fullText: string)
304
313
  }
305
314
 
306
315
  function describeBrowser(handle: BrowserHandle): string {
316
+ if (!("browser" in handle)) {
317
+ return `cmux browser (${handle.kind.surface ?? "split"})`;
318
+ }
307
319
  switch (handle.kind.kind) {
308
320
  case "headless":
309
321
  return `headless browser (${handle.kind.headless ? "hidden" : "visible"})`;
@@ -322,6 +334,8 @@ function describeKind(kind: BrowserKind): string {
322
334
  return `spawned:${kind.path}`;
323
335
  case "connected":
324
336
  return `connected:${kind.cdpUrl}`;
337
+ case "cmux":
338
+ return `cmux:${kind.surface ?? "split"}`;
325
339
  }
326
340
  }
327
341
 
@@ -330,6 +344,7 @@ function sameBrowserKind(a: BrowserKind, b: BrowserKind): boolean {
330
344
  if (a.kind === "headless" && b.kind === "headless") return a.headless === b.headless;
331
345
  if (a.kind === "spawned" && b.kind === "spawned") return a.path === b.path;
332
346
  if (a.kind === "connected" && b.kind === "connected") return a.cdpUrl === b.cdpUrl;
347
+ if (a.kind === "cmux" && b.kind === "cmux") return a.socketPath === b.socketPath;
333
348
  return false;
334
349
  }
335
350
 
@@ -1,6 +1,6 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
2
  import { prompt } from "@oh-my-pi/pi-utils";
3
- import * as z from "zod/v4";
3
+ import { z } from "zod/v4";
4
4
  import checkpointDescription from "../prompts/tools/checkpoint.md" with { type: "text" };
5
5
  import rewindDescription from "../prompts/tools/rewind.md" with { type: "text" };
6
6
  import type { ToolSession } from ".";
@@ -9,7 +9,7 @@ import type {
9
9
  } from "@oh-my-pi/pi-agent-core";
10
10
  import { type Component, Text } from "@oh-my-pi/pi-tui";
11
11
  import { isEnoent, prompt } from "@oh-my-pi/pi-utils";
12
- import * as z from "zod/v4";
12
+ import { z } from "zod/v4";
13
13
  import {
14
14
  type DapBreakpointRecord,
15
15
  type DapCapabilities,
package/src/tools/eval.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
2
2
  import type { ImageContent } from "@oh-my-pi/pi-ai";
3
3
  import { prompt } from "@oh-my-pi/pi-utils";
4
- import * as z from "zod/v4";
4
+ import { z } from "zod/v4";
5
5
  import { jsBackend, pythonBackend } from "../eval";
6
6
  import type { ExecutorBackend, ExecutorBackendResult } from "../eval/backend";
7
7
  import { EVAL_TIMEOUT_PAUSE_OP, EVAL_TIMEOUT_RESUME_OP } from "../eval/bridge-timeout";
@@ -10,6 +10,7 @@ import { defaultEvalSessionId } from "../eval/session-id";
10
10
  import type { EvalCellResult, EvalDisplayOutput, EvalLanguage, EvalStatusEvent, EvalToolDetails } from "../eval/types";
11
11
  import evalDescription from "../prompts/tools/eval.md" with { type: "text" };
12
12
  import { DEFAULT_MAX_BYTES, OutputSink, type OutputSummary, TailBuffer } from "../session/streaming-output";
13
+ import { webpExclusionForModel } from "../utils/image-loading";
13
14
  import { formatDimensionNote, resizeImage } from "../utils/image-resize";
14
15
  import type { ToolSession } from ".";
15
16
  import { truncateForPrompt } from "./approval";
@@ -219,6 +220,7 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
219
220
  throw new ToolError("Eval tool requires a session when not using proxy executor");
220
221
  }
221
222
  const session = this.session;
223
+ const excludeWebP = webpExclusionForModel(session.getActiveModel?.());
222
224
 
223
225
  const cells: ResolvedEvalCell[] = [];
224
226
  for (let i = 0; i < params.cells.length; i++) {
@@ -390,11 +392,14 @@ export class EvalTool implements AgentTool<typeof evalSchema> {
390
392
  cellDisplayOutputs.push(output);
391
393
  }
392
394
  if (output.type === "image") {
393
- const resized = await resizeImage({
394
- type: "image",
395
- data: output.data,
396
- mimeType: output.mimeType,
397
- });
395
+ const resized = await resizeImage(
396
+ {
397
+ type: "image",
398
+ data: output.data,
399
+ mimeType: output.mimeType,
400
+ },
401
+ { excludeWebP },
402
+ );
398
403
  const image: ImageContent = {
399
404
  type: "image",
400
405
  data: resized.data,
@@ -17,6 +17,7 @@ import type { AgentStorage } from "../session/agent-storage";
17
17
  import { DEFAULT_MAX_BYTES, truncateHead } from "../session/streaming-output";
18
18
  import { renderStatusLine, urlHyperlink } from "../tui";
19
19
  import { CachedOutputBlock, markFramedBlockComponent } from "../tui/output-block";
20
+ import { webpExclusionForModel } from "../utils/image-loading";
20
21
  import { formatDimensionNote, resizeImage } from "../utils/image-resize";
21
22
  import { ensureTool } from "../utils/tools-manager";
22
23
  import { extractWithParallel, findParallelApiKey, getParallelExtractContent } from "../web/parallel";
@@ -1077,6 +1078,7 @@ async function renderUrl(
1077
1078
  signal: AbortSignal | undefined,
1078
1079
  storage: AgentStorage | null,
1079
1080
  fetchOverride?: FetchImpl,
1081
+ excludeWebP?: true,
1080
1082
  ): Promise<FetchRenderResult> {
1081
1083
  const notes: string[] = [];
1082
1084
  const fetchedAt = new Date().toISOString();
@@ -1190,7 +1192,7 @@ async function renderUrl(
1190
1192
 
1191
1193
  const resized = await resizeImage(
1192
1194
  { type: "image", data: Buffer.from(binary.buffer).toBase64(), mimeType: imageMimeType },
1193
- { maxBytes: MAX_INLINE_IMAGE_OUTPUT_BYTES },
1195
+ { maxBytes: MAX_INLINE_IMAGE_OUTPUT_BYTES, excludeWebP },
1194
1196
  );
1195
1197
  const isDecodedImage =
1196
1198
  resized.originalWidth > 0 && resized.originalHeight > 0 && resized.width > 0 && resized.height > 0;
@@ -1666,7 +1668,16 @@ async function buildReadUrlCacheEntry(
1666
1668
  }
1667
1669
 
1668
1670
  const storage = session.settings.getStorage();
1669
- const result = await renderUrl(url, effectiveTimeout, raw, session.settings, signal, storage, session.fetch);
1671
+ const result = await renderUrl(
1672
+ url,
1673
+ effectiveTimeout,
1674
+ raw,
1675
+ session.settings,
1676
+ signal,
1677
+ storage,
1678
+ session.fetch,
1679
+ webpExclusionForModel(session.getActiveModel?.()),
1680
+ );
1670
1681
  const output = buildUrlReadOutput(result, result.content);
1671
1682
  const artifactId = options?.ensureArtifact ? await persistReadUrlArtifact(session, output) : undefined;
1672
1683
 
package/src/tools/find.ts CHANGED
@@ -5,7 +5,7 @@ import * as natives from "@oh-my-pi/pi-natives";
5
5
  import type { Component } from "@oh-my-pi/pi-tui";
6
6
  import { Text } from "@oh-my-pi/pi-tui";
7
7
  import { formatGroupedPaths, isEnoent, prompt, untilAborted } from "@oh-my-pi/pi-utils";
8
- import * as z from "zod/v4";
8
+ import { z } from "zod/v4";
9
9
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
10
10
  import { InternalUrlRouter } from "../internal-urls";
11
11
  import type { Theme } from "../modes/theme/theme";
package/src/tools/gh.ts CHANGED
@@ -11,7 +11,7 @@ import type {
11
11
  } from "@oh-my-pi/pi-agent-core";
12
12
 
13
13
  import { getWorktreeDir, hashPath, isEnoent, prompt, untilAborted } from "@oh-my-pi/pi-utils";
14
- import * as z from "zod/v4";
14
+ import { z } from "zod/v4";
15
15
  import type { Settings } from "../config/settings";
16
16
  import githubDescription from "../prompts/tools/github.md" with { type: "text" };
17
17
  import * as git from "../utils/git";
@@ -93,10 +93,11 @@ export function openDb(): Database | null {
93
93
  const dbPath = getGithubCacheDbPath();
94
94
  ensureParentDir(dbPath);
95
95
  const db = new Database(dbPath);
96
+ // Install the busy handler BEFORE any lock-taking statement. See #2421.
97
+ db.run("PRAGMA busy_timeout = 5000");
96
98
  db.run(`
97
99
  PRAGMA journal_mode=WAL;
98
100
  PRAGMA synchronous=NORMAL;
99
- PRAGMA busy_timeout=5000;
100
101
  `);
101
102
  // Migrate any pre-existing table whose key/check constraint predates
102
103
  // the current schema. The cache is regenerable, so we drop rows rather
@@ -19,7 +19,7 @@ import {
19
19
  Snowflake,
20
20
  untilAborted,
21
21
  } from "@oh-my-pi/pi-utils";
22
- import * as z from "zod/v4";
22
+ import { z } from "zod/v4";
23
23
  import packageJson from "../../package.json" with { type: "json" };
24
24
 
25
25
  import { isAuthenticated, type ModelRegistry } from "../config/model-registry";
@@ -1,6 +1,6 @@
1
1
  import type { InMemorySnapshotStore } from "@oh-my-pi/hashline";
2
2
  import type { AgentTelemetryConfig, AgentTool } from "@oh-my-pi/pi-agent-core";
3
- import type { FetchImpl, ToolChoice } from "@oh-my-pi/pi-ai";
3
+ import type { FetchImpl, Model, ToolChoice } from "@oh-my-pi/pi-ai";
4
4
  import { logger } from "@oh-my-pi/pi-utils";
5
5
  import type { AsyncJobManager } from "../async/job-manager";
6
6
  import type { Rule } from "../capability/rule";
@@ -217,6 +217,8 @@ export interface ToolSession {
217
217
  getModelString?: () => string | undefined;
218
218
  /** Get the current session model string, regardless of how it was chosen */
219
219
  getActiveModelString?: () => string | undefined;
220
+ /** Get the current session model object (provider/api capabilities), regardless of how it was chosen. */
221
+ getActiveModel?: () => Model | undefined;
220
222
  /** Auth storage for passing to subagents (avoids re-discovery) */
221
223
  authStorage?: import("../session/auth-storage").AuthStorage;
222
224
  /** Model registry for passing to subagents (avoids re-discovery) */
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
2
2
  import { instrumentedCompleteSimple, resolveTelemetry } from "@oh-my-pi/pi-agent-core";
3
3
  import { type Api, completeSimple, type Model } from "@oh-my-pi/pi-ai";
4
4
  import { prompt } from "@oh-my-pi/pi-utils";
5
- import * as z from "zod/v4";
5
+ import { z } from "zod/v4";
6
6
  import { extractTextContent } from "../commit/utils";
7
7
 
8
8
  import { expandRoleAlias, getModelMatchPreferences, resolveModelFromString } from "../config/model-resolver";
@@ -13,6 +13,7 @@ import {
13
13
  type LoadedImageInput,
14
14
  loadImageInput,
15
15
  MAX_IMAGE_INPUT_BYTES,
16
+ webpExclusionForModel,
16
17
  } from "../utils/image-loading";
17
18
  import type { ToolSession } from "./index";
18
19
  import { ToolError } from "./tool-errors";
@@ -109,6 +110,7 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
109
110
  cwd: this.session.cwd,
110
111
  autoResize: this.session.settings.get("images.autoResize"),
111
112
  maxBytes: MAX_IMAGE_INPUT_BYTES,
113
+ excludeWebP: webpExclusionForModel(model),
112
114
  });
113
115
  } catch (error) {
114
116
  if (error instanceof ImageInputTooLargeError) {
package/src/tools/irc.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
13
13
  import { type Component, Text } from "@oh-my-pi/pi-tui";
14
14
  import { formatAge, formatDuration, prompt } from "@oh-my-pi/pi-utils";
15
- import * as z from "zod/v4";
15
+ import { z } from "zod/v4";
16
16
  import type { Settings } from "../config/settings";
17
17
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
18
18
  import { IrcBus, type IrcDeliveryReceipt, type IrcMessage } from "../irc/bus";
package/src/tools/job.ts CHANGED
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
2
2
  import type { Component } from "@oh-my-pi/pi-tui";
3
3
  import { Text } from "@oh-my-pi/pi-tui";
4
4
  import { prompt } from "@oh-my-pi/pi-utils";
5
- import * as z from "zod/v4";
5
+ import { z } from "zod/v4";
6
6
  import type { AsyncJob, AsyncJobManager } from "../async";
7
7
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
8
8
  import { shimmerEnabled, shimmerText } from "../modes/theme/shimmer";
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
- import * as z from "zod/v4";
2
+ import { z } from "zod/v4";
3
3
  import memoryEditDescription from "../prompts/tools/memory-edit.md" with { type: "text" };
4
4
  import type { ToolSession } from ".";
5
5
 
@@ -1,6 +1,6 @@
1
1
  import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
2
  import { logger, untilAborted } from "@oh-my-pi/pi-utils";
3
- import * as z from "zod/v4";
3
+ import { z } from "zod/v4";
4
4
  import { formatCurrentTime, formatMemories } from "../hindsight/content";
5
5
  import recallDescription from "../prompts/tools/recall.md" with { type: "text" };
6
6
  import type { ToolSession } from ".";
@@ -1,6 +1,6 @@
1
1
  import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
2
  import { logger, untilAborted } from "@oh-my-pi/pi-utils";
3
- import * as z from "zod/v4";
3
+ import { z } from "zod/v4";
4
4
  import { ensureBankExists } from "../hindsight/bank";
5
5
  import reflectDescription from "../prompts/tools/reflect.md" with { type: "text" };
6
6
  import type { ToolSession } from ".";
@@ -1,5 +1,5 @@
1
1
  import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
2
- import * as z from "zod/v4";
2
+ import { z } from "zod/v4";
3
3
  import retainDescription from "../prompts/tools/retain.md" with { type: "text" };
4
4
  import type { ToolSession } from ".";
5
5