@oh-my-pi/pi-coding-agent 15.10.0 → 15.10.2

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 (238) hide show
  1. package/CHANGELOG.md +142 -1
  2. package/dist/types/cli/dry-balance-cli.d.ts +15 -1
  3. package/dist/types/cli/startup-cwd.d.ts +2 -0
  4. package/dist/types/commands/launch.d.ts +3 -0
  5. package/dist/types/commit/analysis/conventional.d.ts +2 -2
  6. package/dist/types/commit/analysis/summary.d.ts +2 -2
  7. package/dist/types/commit/changelog/generate.d.ts +2 -2
  8. package/dist/types/commit/changelog/index.d.ts +2 -2
  9. package/dist/types/commit/map-reduce/index.d.ts +3 -3
  10. package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
  11. package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
  12. package/dist/types/commit/model-selection.d.ts +10 -4
  13. package/dist/types/config/api-key-resolver.d.ts +34 -0
  14. package/dist/types/config/keybindings.d.ts +2 -2
  15. package/dist/types/config/model-provider-priority.d.ts +1 -0
  16. package/dist/types/config/model-registry.d.ts +17 -1
  17. package/dist/types/config/model-resolver.d.ts +4 -1
  18. package/dist/types/config/settings-schema.d.ts +9 -0
  19. package/dist/types/config/settings.d.ts +7 -2
  20. package/dist/types/dap/config.d.ts +14 -1
  21. package/dist/types/dap/types.d.ts +10 -0
  22. package/dist/types/debug/report-bundle.d.ts +3 -0
  23. package/dist/types/edit/file-snapshot-store.d.ts +18 -10
  24. package/dist/types/eval/py/__tests__/prelude.test.d.ts +1 -0
  25. package/dist/types/extensibility/extensions/types.d.ts +4 -1
  26. package/dist/types/lsp/client.d.ts +10 -0
  27. package/dist/types/lsp/utils.d.ts +3 -2
  28. package/dist/types/main.d.ts +3 -9
  29. package/dist/types/mcp/tool-bridge.d.ts +2 -0
  30. package/dist/types/modes/components/chat-block.d.ts +64 -0
  31. package/dist/types/modes/components/custom-editor.d.ts +4 -1
  32. package/dist/types/modes/components/overlay-box.d.ts +17 -0
  33. package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
  34. package/dist/types/modes/components/plan-toc.d.ts +41 -0
  35. package/dist/types/modes/components/read-tool-group.d.ts +2 -0
  36. package/dist/types/modes/components/status-line.d.ts +2 -0
  37. package/dist/types/modes/components/transcript-container.d.ts +11 -0
  38. package/dist/types/modes/controllers/command-controller.d.ts +1 -0
  39. package/dist/types/modes/controllers/event-controller.d.ts +17 -1
  40. package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
  41. package/dist/types/modes/controllers/input-controller.d.ts +1 -1
  42. package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
  43. package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
  44. package/dist/types/modes/interactive-mode.d.ts +16 -5
  45. package/dist/types/modes/magic-keywords.d.ts +1 -1
  46. package/dist/types/modes/markdown-prose.d.ts +1 -1
  47. package/dist/types/modes/theme/theme.d.ts +1 -1
  48. package/dist/types/modes/types.d.ts +21 -5
  49. package/dist/types/modes/utils/copy-targets.d.ts +21 -1
  50. package/dist/types/modes/workflow.d.ts +3 -3
  51. package/dist/types/plan-mode/approved-plan.d.ts +27 -8
  52. package/dist/types/plan-mode/plan-protection.d.ts +4 -4
  53. package/dist/types/sdk.d.ts +2 -0
  54. package/dist/types/session/agent-session.d.ts +21 -0
  55. package/dist/types/session/auth-storage.d.ts +1 -1
  56. package/dist/types/session/messages.d.ts +12 -0
  57. package/dist/types/session/session-manager.d.ts +8 -3
  58. package/dist/types/slash-commands/types.d.ts +4 -6
  59. package/dist/types/task/executor.d.ts +17 -0
  60. package/dist/types/task/index.d.ts +1 -0
  61. package/dist/types/task/render.d.ts +3 -2
  62. package/dist/types/tools/archive-reader.d.ts +5 -0
  63. package/dist/types/tools/ast-edit.d.ts +3 -0
  64. package/dist/types/tools/ast-grep.d.ts +3 -0
  65. package/dist/types/tools/bash.d.ts +1 -0
  66. package/dist/types/tools/eval.d.ts +8 -0
  67. package/dist/types/tools/find.d.ts +8 -4
  68. package/dist/types/tools/gh-cache-invalidation.d.ts +6 -0
  69. package/dist/types/tools/github-cache.d.ts +12 -0
  70. package/dist/types/tools/grouped-file-output.d.ts +95 -12
  71. package/dist/types/tools/memory-render.d.ts +4 -1
  72. package/dist/types/tools/path-utils.d.ts +8 -0
  73. package/dist/types/tools/plan-mode-guard.d.ts +8 -9
  74. package/dist/types/tools/render-utils.d.ts +5 -9
  75. package/dist/types/tools/search.d.ts +6 -2
  76. package/dist/types/tools/sqlite-reader.d.ts +1 -0
  77. package/dist/types/tools/todo.d.ts +3 -2
  78. package/dist/types/tools/write.d.ts +3 -0
  79. package/dist/types/tools/yield.d.ts +8 -0
  80. package/dist/types/tui/output-block.d.ts +16 -4
  81. package/dist/types/tui/status-line.d.ts +3 -0
  82. package/dist/types/utils/enhanced-paste.d.ts +20 -0
  83. package/dist/types/web/search/providers/kimi.d.ts +1 -1
  84. package/package.json +9 -9
  85. package/src/auto-thinking/classifier.ts +5 -1
  86. package/src/cli/args.ts +3 -1
  87. package/src/cli/dry-balance-cli.ts +54 -21
  88. package/src/cli/gallery-cli.ts +4 -1
  89. package/src/cli/gallery-fixtures/misc.ts +29 -0
  90. package/src/cli/startup-cwd.ts +68 -0
  91. package/src/commands/launch.ts +3 -0
  92. package/src/commit/analysis/conventional.ts +2 -2
  93. package/src/commit/analysis/summary.ts +2 -2
  94. package/src/commit/changelog/generate.ts +2 -2
  95. package/src/commit/changelog/index.ts +2 -2
  96. package/src/commit/map-reduce/index.ts +3 -3
  97. package/src/commit/map-reduce/map-phase.ts +2 -2
  98. package/src/commit/map-reduce/reduce-phase.ts +2 -2
  99. package/src/commit/model-selection.ts +36 -11
  100. package/src/commit/pipeline.ts +4 -4
  101. package/src/config/api-key-resolver.ts +58 -0
  102. package/src/config/model-provider-priority.ts +55 -0
  103. package/src/config/model-registry.ts +29 -24
  104. package/src/config/model-resolver.ts +39 -7
  105. package/src/config/settings-schema.ts +10 -0
  106. package/src/config/settings.ts +106 -43
  107. package/src/dap/config.ts +41 -2
  108. package/src/dap/defaults.json +1 -0
  109. package/src/dap/session.ts +1 -0
  110. package/src/dap/types.ts +10 -0
  111. package/src/debug/index.ts +47 -53
  112. package/src/debug/raw-sse-buffer.ts +7 -4
  113. package/src/debug/report-bundle.ts +9 -0
  114. package/src/edit/file-snapshot-store.ts +33 -1
  115. package/src/edit/hashline/filesystem.ts +2 -1
  116. package/src/edit/renderer.ts +82 -78
  117. package/src/eval/__tests__/llm-bridge.test.ts +110 -31
  118. package/src/eval/js/context-manager.ts +32 -15
  119. package/src/eval/llm-bridge.ts +22 -6
  120. package/src/eval/py/__tests__/prelude.test.ts +19 -0
  121. package/src/eval/py/executor.ts +23 -11
  122. package/src/eval/py/prelude.py +1 -1
  123. package/src/extensibility/extensions/types.ts +10 -1
  124. package/src/goals/tools/goal-tool.ts +36 -26
  125. package/src/internal-urls/docs-index.generated.ts +8 -8
  126. package/src/lsp/client.ts +23 -11
  127. package/src/lsp/config.ts +11 -1
  128. package/src/lsp/index.ts +61 -9
  129. package/src/lsp/utils.ts +3 -2
  130. package/src/main.ts +100 -72
  131. package/src/mcp/tool-bridge.ts +2 -0
  132. package/src/memories/index.ts +14 -7
  133. package/src/mnemopi/backend.ts +5 -1
  134. package/src/modes/acp/acp-agent.ts +33 -26
  135. package/src/modes/components/assistant-message.ts +2 -9
  136. package/src/modes/components/chat-block.ts +111 -0
  137. package/src/modes/components/copy-selector.ts +1 -44
  138. package/src/modes/components/custom-editor.ts +164 -109
  139. package/src/modes/components/custom-message.ts +1 -3
  140. package/src/modes/components/execution-shared.ts +1 -2
  141. package/src/modes/components/hook-message.ts +1 -3
  142. package/src/modes/components/model-selector.ts +59 -13
  143. package/src/modes/components/oauth-selector.ts +33 -7
  144. package/src/modes/components/overlay-box.ts +108 -0
  145. package/src/modes/components/plan-review-overlay.ts +799 -0
  146. package/src/modes/components/plan-toc.ts +138 -0
  147. package/src/modes/components/read-tool-group.ts +20 -4
  148. package/src/modes/components/skill-message.ts +0 -1
  149. package/src/modes/components/status-line.ts +19 -4
  150. package/src/modes/components/tips.txt +2 -1
  151. package/src/modes/components/todo-reminder.ts +0 -2
  152. package/src/modes/components/tool-execution.ts +68 -88
  153. package/src/modes/components/transcript-container.ts +84 -24
  154. package/src/modes/components/user-message.ts +2 -3
  155. package/src/modes/controllers/command-controller-shared.ts +7 -6
  156. package/src/modes/controllers/command-controller.ts +57 -55
  157. package/src/modes/controllers/event-controller.ts +67 -40
  158. package/src/modes/controllers/extension-ui-controller.ts +10 -73
  159. package/src/modes/controllers/input-controller.ts +170 -126
  160. package/src/modes/controllers/mcp-command-controller.ts +69 -60
  161. package/src/modes/controllers/selector-controller.ts +23 -25
  162. package/src/modes/controllers/streaming-reveal.ts +212 -0
  163. package/src/modes/controllers/tan-command-controller.ts +173 -0
  164. package/src/modes/interactive-mode.ts +274 -112
  165. package/src/modes/magic-keywords.ts +1 -1
  166. package/src/modes/markdown-prose.ts +1 -1
  167. package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
  168. package/src/modes/theme/shimmer.ts +20 -9
  169. package/src/modes/theme/theme-schema.json +1 -1
  170. package/src/modes/theme/theme.ts +8 -4
  171. package/src/modes/types.ts +21 -7
  172. package/src/modes/utils/copy-targets.ts +133 -27
  173. package/src/modes/utils/ui-helpers.ts +44 -46
  174. package/src/modes/workflow.ts +10 -10
  175. package/src/plan-mode/approved-plan.ts +66 -43
  176. package/src/plan-mode/plan-protection.ts +4 -4
  177. package/src/prompts/system/background-tan-dispatch.md +8 -0
  178. package/src/prompts/system/plan-mode-active.md +67 -58
  179. package/src/prompts/system/plan-mode-approved.md +1 -1
  180. package/src/prompts/system/workflow-notice.md +1 -1
  181. package/src/prompts/tools/bash.md +9 -0
  182. package/src/prompts/tools/browser.md +1 -1
  183. package/src/prompts/tools/eval.md +2 -1
  184. package/src/prompts/tools/read.md +2 -2
  185. package/src/sdk.ts +37 -46
  186. package/src/session/agent-session.ts +119 -18
  187. package/src/session/auth-storage.ts +2 -0
  188. package/src/session/messages.ts +26 -0
  189. package/src/session/session-manager.ts +109 -28
  190. package/src/slash-commands/builtin-registry.ts +36 -9
  191. package/src/slash-commands/types.ts +4 -6
  192. package/src/task/executor.ts +76 -38
  193. package/src/task/index.ts +4 -0
  194. package/src/task/render.ts +211 -147
  195. package/src/tools/archive-reader.ts +64 -0
  196. package/src/tools/ask.ts +119 -164
  197. package/src/tools/ast-edit.ts +98 -71
  198. package/src/tools/ast-grep.ts +37 -43
  199. package/src/tools/bash.ts +57 -6
  200. package/src/tools/browser/tab-supervisor.ts +13 -1
  201. package/src/tools/browser/tab-worker.ts +33 -4
  202. package/src/tools/debug.ts +20 -8
  203. package/src/tools/eval.ts +13 -2
  204. package/src/tools/fetch.ts +297 -7
  205. package/src/tools/find.ts +51 -30
  206. package/src/tools/gh-cache-invalidation.ts +200 -0
  207. package/src/tools/gh-renderer.ts +81 -42
  208. package/src/tools/github-cache.ts +25 -0
  209. package/src/tools/grouped-file-output.ts +272 -48
  210. package/src/tools/image-gen.ts +150 -103
  211. package/src/tools/inspect-image-renderer.ts +63 -41
  212. package/src/tools/inspect-image.ts +10 -3
  213. package/src/tools/job.ts +3 -4
  214. package/src/tools/memory-render.ts +4 -1
  215. package/src/tools/path-utils.ts +28 -2
  216. package/src/tools/plan-mode-guard.ts +66 -39
  217. package/src/tools/read.ts +48 -28
  218. package/src/tools/render-utils.ts +21 -37
  219. package/src/tools/resolve.ts +14 -0
  220. package/src/tools/search-tool-bm25.ts +36 -23
  221. package/src/tools/search.ts +118 -81
  222. package/src/tools/sqlite-reader.ts +9 -12
  223. package/src/tools/todo.ts +118 -52
  224. package/src/tools/write.ts +83 -64
  225. package/src/tools/yield.ts +10 -1
  226. package/src/tui/output-block.ts +60 -13
  227. package/src/tui/status-line.ts +5 -1
  228. package/src/utils/commit-message-generator.ts +11 -3
  229. package/src/utils/enhanced-paste.ts +230 -0
  230. package/src/utils/title-generator.ts +2 -1
  231. package/src/web/search/providers/anthropic.ts +25 -19
  232. package/src/web/search/providers/codex.ts +37 -8
  233. package/src/web/search/providers/exa.ts +11 -3
  234. package/src/web/search/providers/kimi.ts +28 -17
  235. package/src/web/search/providers/parallel.ts +35 -24
  236. package/src/web/search/providers/synthetic.ts +8 -6
  237. package/src/web/search/providers/tavily.ts +9 -8
  238. package/src/web/search/providers/zai.ts +8 -6
@@ -4,7 +4,7 @@
4
4
  * Uses Moonshot Kimi Code search API to retrieve web results.
5
5
  * Endpoint: POST https://api.kimi.com/coding/v1/search
6
6
  */
7
- import type { AuthStorage } from "@oh-my-pi/pi-ai";
7
+ import { type ApiKey, type AuthStorage, withAuth } from "@oh-my-pi/pi-ai";
8
8
  import { $env } from "@oh-my-pi/pi-utils";
9
9
 
10
10
  import type { SearchResponse, SearchSource } from "../../../web/search/types";
@@ -54,20 +54,26 @@ function resolveBaseUrl(): string {
54
54
  return asTrimmed($env.MOONSHOT_SEARCH_BASE_URL) ?? asTrimmed($env.KIMI_SEARCH_BASE_URL) ?? KIMI_SEARCH_URL;
55
55
  }
56
56
 
57
- /** Find Kimi search credentials from environment or AuthStorage. */
58
- async function findApiKey(
57
+ /**
58
+ * Resolve the Kimi search credential. Highest precedence is the static env key;
59
+ * otherwise an AuthStorage-backed resolver for whichever stored provider id
60
+ * holds a key (`moonshot` first, then `kimi-code`), so a stale token triggers
61
+ * the central force-refresh / sibling-rotate retry. Returns `undefined` when
62
+ * neither is configured.
63
+ */
64
+ async function resolveKey(
59
65
  authStorage: AuthStorage,
60
66
  sessionId: string | undefined,
61
67
  signal: AbortSignal | undefined,
62
- ): Promise<string | null> {
68
+ ): Promise<ApiKey | undefined> {
63
69
  const envKey = asTrimmed($env.MOONSHOT_SEARCH_API_KEY) ?? asTrimmed($env.KIMI_SEARCH_API_KEY);
64
70
  if (envKey) return envKey;
65
71
 
66
- return (
67
- (await authStorage.getApiKey("moonshot", sessionId, { signal })) ??
68
- (await authStorage.getApiKey("kimi-code", sessionId, { signal })) ??
69
- null
70
- );
72
+ for (const provider of ["moonshot", "kimi-code"] as const) {
73
+ const stored = await authStorage.getApiKey(provider, sessionId, { signal });
74
+ if (stored) return authStorage.resolver(provider, { sessionId });
75
+ }
76
+ return undefined;
71
77
  }
72
78
 
73
79
  async function callKimiSearch(
@@ -108,20 +114,25 @@ async function callKimiSearch(
108
114
 
109
115
  /** Execute Kimi web search. */
110
116
  export async function searchKimi(params: KimiSearchParams): Promise<SearchResponse> {
111
- const apiKey = await findApiKey(params.authStorage, params.sessionId, params.signal);
112
- if (!apiKey) {
117
+ const keyOrResolver = await resolveKey(params.authStorage, params.sessionId, params.signal);
118
+ if (!keyOrResolver) {
113
119
  throw new Error(
114
120
  "Kimi search credentials not found. Set MOONSHOT_SEARCH_API_KEY, KIMI_SEARCH_API_KEY, MOONSHOT_API_KEY, or login with 'omp /login moonshot'.",
115
121
  );
116
122
  }
117
123
 
118
124
  const limit = clampNumResults(params.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
119
- const { response, requestId } = await callKimiSearch(apiKey, {
120
- query: params.query,
121
- limit,
122
- includeContent: params.include_content ?? false,
123
- signal: params.signal,
124
- });
125
+ const { response, requestId } = await withAuth(
126
+ keyOrResolver,
127
+ key =>
128
+ callKimiSearch(key, {
129
+ query: params.query,
130
+ limit,
131
+ includeContent: params.include_content ?? false,
132
+ signal: params.signal,
133
+ }),
134
+ { signal: params.signal },
135
+ );
125
136
  const sources: SearchSource[] = [];
126
137
 
127
138
  for (const result of response.search_results ?? []) {
@@ -1,4 +1,4 @@
1
- import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
1
+ import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
2
2
  import type { SearchResponse } from "../../../web/search/types";
3
3
  import { SearchProviderError } from "../../../web/search/types";
4
4
  import { ParallelApiError, type ParallelSearchResult, type ParallelSearchSource } from "../../parallel";
@@ -123,30 +123,41 @@ async function searchWithAuthStorage(
123
123
  );
124
124
  }
125
125
 
126
- const response = await fetch(PARALLEL_SEARCH_URL, {
127
- method: "POST",
128
- headers: {
129
- Accept: "application/json",
130
- "Content-Type": "application/json",
131
- "x-api-key": apiKey,
132
- "parallel-beta": PARALLEL_BETA_HEADER,
133
- },
134
- body: JSON.stringify({
135
- objective,
136
- search_queries: queries,
137
- mode: "fast",
138
- excerpts: {
139
- max_chars_per_result: 10_000,
140
- },
141
- }),
142
- signal: withHardTimeout(params.signal),
143
- });
144
- if (!response.ok) {
145
- throw parseParallelErrorResponse(response.status, await response.text());
146
- }
126
+ // Drive the (already-present) credential through the central force-refresh /
127
+ // sibling-rotate retry policy. The `ParallelApiError` thrown below carries a
128
+ // `statusCode`, which `withAuth`'s default classifier reads to detect a
129
+ // retryable 401 / usage-limit.
130
+ const keyOrResolver: ApiKey = authStorage.resolver("parallel", { sessionId });
131
+ return withAuth(
132
+ keyOrResolver,
133
+ async key => {
134
+ const response = await fetch(PARALLEL_SEARCH_URL, {
135
+ method: "POST",
136
+ headers: {
137
+ Accept: "application/json",
138
+ "Content-Type": "application/json",
139
+ "x-api-key": key,
140
+ "parallel-beta": PARALLEL_BETA_HEADER,
141
+ },
142
+ body: JSON.stringify({
143
+ objective,
144
+ search_queries: queries,
145
+ mode: "fast",
146
+ excerpts: {
147
+ max_chars_per_result: 10_000,
148
+ },
149
+ }),
150
+ signal: withHardTimeout(params.signal),
151
+ });
152
+ if (!response.ok) {
153
+ throw parseParallelErrorResponse(response.status, await response.text());
154
+ }
147
155
 
148
- const payload: unknown = await response.json();
149
- return parseSearchPayload(payload);
156
+ const payload: unknown = await response.json();
157
+ return parseSearchPayload(payload);
158
+ },
159
+ { signal: params.signal },
160
+ );
150
161
  }
151
162
 
152
163
  export async function searchParallel(
@@ -5,7 +5,7 @@
5
5
  * Endpoint: POST https://api.synthetic.new/v2/search
6
6
  */
7
7
 
8
- import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
8
+ import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
9
9
  import type { SearchResponse, SearchSource } from "../../../web/search/types";
10
10
  import { SearchProviderError } from "../../../web/search/types";
11
11
  import type { SearchParams } from "./base";
@@ -66,12 +66,14 @@ async function callSyntheticSearch(
66
66
 
67
67
  /** Execute Synthetic web search. */
68
68
  export async function searchSynthetic(params: SearchParams): Promise<SearchResponse> {
69
- const apiKey = await findApiKey(params.authStorage, params.sessionId, params.signal);
70
- if (!apiKey) {
71
- throw new Error("Synthetic credentials not found. Set SYNTHETIC_API_KEY or login with 'omp /login synthetic'.");
72
- }
69
+ const keyOrResolver: ApiKey = params.authStorage.resolver("synthetic", {
70
+ sessionId: params.sessionId,
71
+ });
73
72
 
74
- const data = await callSyntheticSearch(apiKey, params.query, params.signal);
73
+ const data = await withAuth(keyOrResolver, key => callSyntheticSearch(key, params.query, params.signal), {
74
+ signal: params.signal,
75
+ missingKeyMessage: "Synthetic credentials not found. Set SYNTHETIC_API_KEY or login with 'omp /login synthetic'.",
76
+ });
75
77
  const sources: SearchSource[] = [];
76
78
 
77
79
  for (const result of data.results ?? []) {
@@ -4,7 +4,7 @@
4
4
  * Uses Tavily's agent-focused search API to return structured results with an
5
5
  * optional synthesized answer.
6
6
  */
7
- import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
7
+ import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
8
8
  import type { SearchResponse, SearchSource } from "../../../web/search/types";
9
9
  import { SearchProviderError } from "../../../web/search/types";
10
10
  import { clampNumResults, dateToAgeSeconds } from "../utils";
@@ -127,15 +127,16 @@ export async function searchTavily(params: SearchParams): Promise<SearchResponse
127
127
  recency: params.recency,
128
128
  signal: params.signal,
129
129
  };
130
- const apiKey = await findApiKey(params.authStorage, params.sessionId, params.signal);
131
- if (!apiKey) {
132
- throw new Error(
133
- 'Tavily credentials not found. Set TAVILY_API_KEY or configure an API key for provider "tavily".',
134
- );
135
- }
130
+ const keyOrResolver: ApiKey = params.authStorage.resolver("tavily", {
131
+ sessionId: params.sessionId,
132
+ });
136
133
 
137
134
  const numResults = clampNumResults(tavilyParams.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
138
- const response = await callTavilySearch(apiKey, tavilyParams);
135
+ const response = await withAuth(keyOrResolver, key => callTavilySearch(key, tavilyParams), {
136
+ signal: params.signal,
137
+ missingKeyMessage:
138
+ 'Tavily credentials not found. Set TAVILY_API_KEY or configure an API key for provider "tavily".',
139
+ });
139
140
  const sources: SearchSource[] = [];
140
141
 
141
142
  for (const result of response.results ?? []) {
@@ -4,7 +4,7 @@
4
4
  * Calls Z.AI's remote MCP server (`webSearchPrime`) and adapts results into
5
5
  * the unified SearchResponse shape used by the web search tool.
6
6
  */
7
- import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
7
+ import { type ApiKey, type AuthStorage, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
8
8
  import { asRecord, asString } from "../../../web/scrapers/utils";
9
9
  import type { SearchResponse, SearchSource } from "../../../web/search/types";
10
10
  import { SearchProviderError } from "../../../web/search/types";
@@ -278,12 +278,14 @@ function toSources(results: ZaiSearchResult[]): SearchSource[] {
278
278
 
279
279
  /** Execute Z.AI web search via remote MCP endpoint. */
280
280
  export async function searchZai(params: ZaiSearchParams): Promise<SearchResponse> {
281
- const apiKey = await findApiKey(params.authStorage, params.sessionId, params.signal);
282
- if (!apiKey) {
283
- throw new Error("Z.AI credentials not found. Set ZAI_API_KEY or login with 'omp /login zai'.");
284
- }
281
+ const keyOrResolver: ApiKey = params.authStorage.resolver("zai", {
282
+ sessionId: params.sessionId,
283
+ });
285
284
 
286
- const rawResult = await callZaiSearch(apiKey, params);
285
+ const rawResult = await withAuth(keyOrResolver, key => callZaiSearch(key, params), {
286
+ signal: params.signal,
287
+ missingKeyMessage: "Z.AI credentials not found. Set ZAI_API_KEY or login with 'omp /login zai'.",
288
+ });
287
289
  const payload = parseSearchPayload(rawResult);
288
290
  let sources = toSources(payload.results);
289
291