zidane 8.2.1 → 8.2.3

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 (187) hide show
  1. package/dist/acp-cli.js +8 -8
  2. package/dist/{acp-D7Pr8NGt.js → acp-oYRjlh71.js} +6 -6
  3. package/dist/{acp-D7Pr8NGt.js.map → acp-oYRjlh71.js.map} +1 -1
  4. package/dist/acp.d.ts +2 -2
  5. package/dist/acp.js +1 -1
  6. package/dist/{agent-DecDtKA_.js → agent-CBQglXtH.js} +9 -379
  7. package/dist/agent-CBQglXtH.js.map +1 -0
  8. package/dist/agent.d.ts +1 -1
  9. package/dist/agent.js +1 -1
  10. package/dist/{builtin-artifact-kinds-B_OEgf8b.js → builtin-artifact-kinds-BmorPkgz.js} +36 -9
  11. package/dist/builtin-artifact-kinds-BmorPkgz.js.map +1 -0
  12. package/dist/chat/pure.d.ts +4 -4
  13. package/dist/chat/pure.js +6 -6
  14. package/dist/chat.d.ts +4 -4
  15. package/dist/chat.js +15 -15
  16. package/dist/{cli-shared-Dx4vmif6.js → cli-shared-E5m_pI8A.js} +2 -2
  17. package/dist/{cli-shared-Dx4vmif6.js.map → cli-shared-E5m_pI8A.js.map} +1 -1
  18. package/dist/{client-core-imHuBWV3.d.ts → client-core-EEEX_u6D.d.ts} +24 -5
  19. package/dist/{client-core-imHuBWV3.d.ts.map → client-core-EEEX_u6D.d.ts.map} +1 -1
  20. package/dist/{connection-loop-CdHt3iQi.d.ts → connection-loop-CpgfUmuw.d.ts} +3 -3
  21. package/dist/{connection-loop-CdHt3iQi.d.ts.map → connection-loop-CpgfUmuw.d.ts.map} +1 -1
  22. package/dist/contexts/daytona.d.ts +1 -1
  23. package/dist/contexts/e2b.d.ts +1 -1
  24. package/dist/{credentials-Dti61yoi.js → credentials-B9DfBJP9.js} +11 -7
  25. package/dist/credentials-B9DfBJP9.js.map +1 -0
  26. package/dist/daemon-cli.js +2 -2
  27. package/dist/{dispatcher-vy3J6JMA.js → dispatcher-Bv9-c6I2.js} +28 -14
  28. package/dist/dispatcher-Bv9-c6I2.js.map +1 -0
  29. package/dist/{engine-BN6PjWAW.js → engine-XmoPPB6U.js} +5 -5
  30. package/dist/{engine-BN6PjWAW.js.map → engine-XmoPPB6U.js.map} +1 -1
  31. package/dist/{eval-BaJtxLCW.js → eval-CfQlBR81.js} +2 -2
  32. package/dist/{eval-BaJtxLCW.js.map → eval-CfQlBR81.js.map} +1 -1
  33. package/dist/eval.d.ts +1 -1
  34. package/dist/eval.js +1 -1
  35. package/dist/{executor-CT4oUkg6.js → executor-BhthPE1q.js} +3 -3
  36. package/dist/{executor-CT4oUkg6.js.map → executor-BhthPE1q.js.map} +1 -1
  37. package/dist/{extensions-BKt7lxxv.js → extensions-BgBwogRW.js} +114 -113
  38. package/dist/extensions-BgBwogRW.js.map +1 -0
  39. package/dist/extensions.d.ts +1 -1
  40. package/dist/extensions.js +2 -2
  41. package/dist/{fold-rules-BYedvsH7.d.ts → fold-rules-DSypi36k.d.ts} +2 -2
  42. package/dist/{fold-rules-BYedvsH7.d.ts.map → fold-rules-DSypi36k.d.ts.map} +1 -1
  43. package/dist/google-O6pRnddf.js +881 -0
  44. package/dist/google-O6pRnddf.js.map +1 -0
  45. package/dist/{headless-Ebd1ZEzC.js → headless-R4pbpTkm.js} +3 -3
  46. package/dist/{headless-Ebd1ZEzC.js.map → headless-R4pbpTkm.js.map} +1 -1
  47. package/dist/headless.d.ts +1 -1
  48. package/dist/headless.js +1 -1
  49. package/dist/{host-scoped-settings-Cy_zbmB_.js → host-scoped-settings-C-Nmb6vr.js} +2 -2
  50. package/dist/{host-scoped-settings-Cy_zbmB_.js.map → host-scoped-settings-C-Nmb6vr.js.map} +1 -1
  51. package/dist/{host-signals-Cx2IAbHh.js → host-signals-BFudPyCT.js} +5 -5
  52. package/dist/{host-signals-Cx2IAbHh.js.map → host-signals-BFudPyCT.js.map} +1 -1
  53. package/dist/{index-pc71ADi6.d.ts → index-AqRZ3gEz.d.ts} +71 -4
  54. package/dist/index-AqRZ3gEz.d.ts.map +1 -0
  55. package/dist/{index-Bt_G2Z_2.d.ts → index-CDS7YdUC.d.ts} +5 -5
  56. package/dist/{index-Bt_G2Z_2.d.ts.map → index-CDS7YdUC.d.ts.map} +1 -1
  57. package/dist/{index-CidMTcxS.d.ts → index-DyQyDLX1.d.ts} +167 -10
  58. package/dist/index-DyQyDLX1.d.ts.map +1 -0
  59. package/dist/index.d.ts +3 -3
  60. package/dist/index.js +15 -15
  61. package/dist/ipc/remote.d.ts +5 -5
  62. package/dist/ipc/remote.js +3 -3
  63. package/dist/ipc.d.ts +5 -5
  64. package/dist/ipc.js +7 -7
  65. package/dist/{links-DSZ4uQ5w.js → links-rFaWqS2Y.js} +10 -10
  66. package/dist/{links-DSZ4uQ5w.js.map → links-rFaWqS2Y.js.map} +1 -1
  67. package/dist/{mcp-allowlist-BQ5MvhHi.js → mcp-allowlist-CLXweyxk.js} +14 -3
  68. package/dist/mcp-allowlist-CLXweyxk.js.map +1 -0
  69. package/dist/mcp.d.ts +1 -1
  70. package/dist/{oauth-BSqXgads.js → oauth-7_48zR7n.js} +226 -40
  71. package/dist/oauth-7_48zR7n.js.map +1 -0
  72. package/dist/{openai-dTmPxM6T.js → openai-Rh6u5NXF.js} +121 -2
  73. package/dist/openai-Rh6u5NXF.js.map +1 -0
  74. package/dist/{openrouter-CtZbQDa0.js → openrouter-Dg0yaI-o.js} +91 -2
  75. package/dist/openrouter-Dg0yaI-o.js.map +1 -0
  76. package/dist/output/stream-json.d.ts +1 -1
  77. package/dist/output/terminal.d.ts +1 -1
  78. package/dist/plugins.d.ts +1 -1
  79. package/dist/{presets-EzlHyFSi.js → presets-DXeHsd24.js} +3 -3
  80. package/dist/{presets-EzlHyFSi.js.map → presets-DXeHsd24.js.map} +1 -1
  81. package/dist/presets.d.ts +1 -1
  82. package/dist/presets.js +1 -1
  83. package/dist/{prompts-D2lPHLOV.d.ts → prompts-DmY13jvF.d.ts} +2 -1
  84. package/dist/prompts-DmY13jvF.d.ts.map +1 -0
  85. package/dist/protocol-BIv1Yuvl.js.map +1 -1
  86. package/dist/providers/anthropic.d.ts +1 -1
  87. package/dist/providers/arcee.d.ts +1 -1
  88. package/dist/providers/baseten.d.ts +1 -1
  89. package/dist/providers/cerebras.d.ts +1 -1
  90. package/dist/providers/google.d.ts +1 -1
  91. package/dist/providers/google.js +1 -784
  92. package/dist/providers/local.d.ts +1 -1
  93. package/dist/providers/neuralwatt.d.ts +1 -1
  94. package/dist/providers/openai-compat.d.ts +1 -1
  95. package/dist/providers/openai.d.ts +1 -1
  96. package/dist/providers/openai.js +1 -1
  97. package/dist/providers/openrouter.d.ts +1 -1
  98. package/dist/providers/openrouter.js +1 -1
  99. package/dist/providers/xai.d.ts +1 -1
  100. package/dist/providers/xai.js +1 -101
  101. package/dist/providers.d.ts +2 -2
  102. package/dist/providers.js +6 -5
  103. package/dist/{registry-BBaEgTsX.js → registry-eourRLY2.js} +3 -3
  104. package/dist/{registry-BBaEgTsX.js.map → registry-eourRLY2.js.map} +1 -1
  105. package/dist/{remote-hosts-bO7wOt44.js → remote-hosts-DY2J07ut.js} +4 -4
  106. package/dist/{remote-hosts-bO7wOt44.js.map → remote-hosts-DY2J07ut.js.map} +1 -1
  107. package/dist/restate.d.ts +1 -1
  108. package/dist/{secret-shapes-Cda65uDH.js → secret-shapes-BjW4l6U0.js} +17 -3
  109. package/dist/secret-shapes-BjW4l6U0.js.map +1 -0
  110. package/dist/session/sqlite.d.ts +1 -1
  111. package/dist/{session-factory-BazyVdzj.js → session-factory-CMwIXE-7.js} +5 -5
  112. package/dist/{session-factory-BazyVdzj.js.map → session-factory-CMwIXE-7.js.map} +1 -1
  113. package/dist/session.d.ts +1 -1
  114. package/dist/{settings-keys-zYaer8DY.js → settings-keys-CPdmkTz7.js} +3 -3
  115. package/dist/{settings-keys-zYaer8DY.js.map → settings-keys-CPdmkTz7.js.map} +1 -1
  116. package/dist/{shell-path-od9XHRPt.js → shell-path-B8b3EOtK.js} +3 -3
  117. package/dist/{shell-path-od9XHRPt.js.map → shell-path-B8b3EOtK.js.map} +1 -1
  118. package/dist/{skills-use-D68a1gyZ.js → skills-use-CssTEaIj.js} +375 -2
  119. package/dist/skills-use-CssTEaIj.js.map +1 -0
  120. package/dist/skills.d.ts +1 -1
  121. package/dist/store-L01W0gGI.js.map +1 -1
  122. package/dist/{system-freeze-DXvJpveP.js → system-freeze-Um9ZJiPo.js} +24 -9
  123. package/dist/system-freeze-Um9ZJiPo.js.map +1 -0
  124. package/dist/{todo-edits-CmqNFvLJ.js → todo-edits-BERpCRPj.js} +3 -3
  125. package/dist/{todo-edits-CmqNFvLJ.js.map → todo-edits-BERpCRPj.js.map} +1 -1
  126. package/dist/tools/fetch-url.d.ts +1 -1
  127. package/dist/tools/prompts.d.ts +1 -1
  128. package/dist/tools/prompts.js +7 -0
  129. package/dist/tools/prompts.js.map +1 -1
  130. package/dist/tools/web-search.d.ts +1 -1
  131. package/dist/{tools-9Dpm8F-f.js → tools-pZVFIJ_K.js} +273 -2
  132. package/dist/tools-pZVFIJ_K.js.map +1 -0
  133. package/dist/tools.d.ts +3 -3
  134. package/dist/tools.js +4 -4
  135. package/dist/{transcript-anchors-B3TFJqr_.js → transcript-anchors-jHrg-r-H.js} +10 -10
  136. package/dist/{transcript-anchors-B3TFJqr_.js.map → transcript-anchors-jHrg-r-H.js.map} +1 -1
  137. package/dist/{transcript-reconcile-BC2DbTmp.js → transcript-reconcile-CPdrNbbR.js} +19 -2
  138. package/dist/{transcript-reconcile-BC2DbTmp.js.map → transcript-reconcile-CPdrNbbR.js.map} +1 -1
  139. package/dist/tui.d.ts +7 -7
  140. package/dist/tui.js +25 -23
  141. package/dist/tui.js.map +1 -1
  142. package/dist/{turn-operations-CIFoOebZ.d.ts → turn-operations-DuGtKuc_.d.ts} +10 -3
  143. package/dist/turn-operations-DuGtKuc_.d.ts.map +1 -0
  144. package/dist/{types-iFLlZKd0.d.ts → types-C0PJmBaO.d.ts} +2 -2
  145. package/dist/{types-iFLlZKd0.d.ts.map → types-C0PJmBaO.d.ts.map} +1 -1
  146. package/dist/types-JzccL5GE.js +24 -0
  147. package/dist/types-JzccL5GE.js.map +1 -0
  148. package/dist/types.d.ts +2 -2
  149. package/dist/{url-BTH-HE9Y.js → url-D8KZ7Edw.js} +2 -2
  150. package/dist/{url-BTH-HE9Y.js.map → url-D8KZ7Edw.js.map} +1 -1
  151. package/dist/{version-4T82xRxQ.js → version-bd3DQJkc.js} +2 -2
  152. package/dist/{version-4T82xRxQ.js.map → version-bd3DQJkc.js.map} +1 -1
  153. package/dist/wire-DH77Z-rr.js +80 -0
  154. package/dist/wire-DH77Z-rr.js.map +1 -0
  155. package/dist/{workspace-exec-stream-By8na6vz.js → workspace-exec-stream-CyfYgqAh.js} +4 -4
  156. package/dist/{workspace-exec-stream-By8na6vz.js.map → workspace-exec-stream-CyfYgqAh.js.map} +1 -1
  157. package/dist/{write-file-B4vi3SvQ.js → write-file-QYrcDdl3.js} +3 -3
  158. package/dist/{write-file-B4vi3SvQ.js.map → write-file-QYrcDdl3.js.map} +1 -1
  159. package/dist/{ws-CS312Jpx.js → ws-CURQ2igk.js} +5 -5
  160. package/dist/{ws-CS312Jpx.js.map → ws-CURQ2igk.js.map} +1 -1
  161. package/dist/{ws-client-wWka-Jku.js → ws-client-BVljYbdb.js} +3 -3
  162. package/dist/{ws-client-wWka-Jku.js.map → ws-client-BVljYbdb.js.map} +1 -1
  163. package/dist/xai-CStTwCV2.js +182 -0
  164. package/dist/xai-CStTwCV2.js.map +1 -0
  165. package/docs/ARCHITECTURE.md +1 -0
  166. package/docs/ARTIFACTS.md +1 -1
  167. package/docs/EXTENSIONS.md +9 -5
  168. package/package.json +2 -2
  169. package/dist/agent-DecDtKA_.js.map +0 -1
  170. package/dist/builtin-artifact-kinds-B_OEgf8b.js.map +0 -1
  171. package/dist/credentials-Dti61yoi.js.map +0 -1
  172. package/dist/dispatcher-vy3J6JMA.js.map +0 -1
  173. package/dist/extensions-BKt7lxxv.js.map +0 -1
  174. package/dist/index-CidMTcxS.d.ts.map +0 -1
  175. package/dist/index-pc71ADi6.d.ts.map +0 -1
  176. package/dist/mcp-allowlist-BQ5MvhHi.js.map +0 -1
  177. package/dist/oauth-BSqXgads.js.map +0 -1
  178. package/dist/openai-dTmPxM6T.js.map +0 -1
  179. package/dist/openrouter-CtZbQDa0.js.map +0 -1
  180. package/dist/prompts-D2lPHLOV.d.ts.map +0 -1
  181. package/dist/providers/google.js.map +0 -1
  182. package/dist/providers/xai.js.map +0 -1
  183. package/dist/secret-shapes-Cda65uDH.js.map +0 -1
  184. package/dist/skills-use-D68a1gyZ.js.map +0 -1
  185. package/dist/system-freeze-DXvJpveP.js.map +0 -1
  186. package/dist/tools-9Dpm8F-f.js.map +0 -1
  187. package/dist/turn-operations-CIFoOebZ.d.ts.map +0 -1
@@ -0,0 +1,881 @@
1
+ import { b as providerLimitReason, d as classifyProviderLimit, g as isRetryableHttpStatus, u as classifyErrorPrelude, v as matchesContextExceeded, y as matchesToolPairingError } from "./errors-C3z7pmOx.js";
2
+ import { i as documentBlockMarker, n as assertResolvedMediaBlock } from "./types-CzmghX7C.js";
3
+ import { A as lookupModel, P as systemNotesAsText, T as fillEstimatedCost, g as createToolNameBoundary, h as userMessage, j as mergeCatalogEntry, m as toolResultsMessage, o as assistantMessage, v as sanitizeToolSpecs, z as unsupportedMediaError } from "./openai-compat-5HkREis_.js";
4
+ import { renderSystemForWire } from "./prompt.js";
5
+ import { c as reasoningWireAction } from "./messages-Wd0QtRUJ.js";
6
+ import { t as GOOGLE_VERTEX_OAUTH_ID } from "./google-oauth-g3JmRoJ-.js";
7
+ import { a as pinnedAccountMissingError, l as resolveOAuthApiKey, o as readAccountOAuthCredential, s as readOAuthCredentials } from "./oauth-DRrs-65k.js";
8
+ import { a as imageHttpError, o as repeatRequest, r as imageCount } from "./wire-DH77Z-rr.js";
9
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { tmpdir } from "node:os";
12
+ import { lstat, readdir, rm } from "node:fs/promises";
13
+ import { normalizeContext } from "@earendil-works/pi-ai";
14
+ import { getBuiltinModel } from "@earendil-works/pi-ai/providers/all";
15
+ //#region src/providers/images/google.ts
16
+ const BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
17
+ /** Gemini image models on the Gemini API. */
18
+ const GOOGLE_IMAGE_MODELS = [
19
+ {
20
+ id: "gemini-3.1-flash-image",
21
+ name: "Gemini 3.1 Flash Image",
22
+ input: ["text", "image"]
23
+ },
24
+ {
25
+ id: "gemini-3-pro-image",
26
+ name: "Gemini 3 Pro Image",
27
+ input: ["text", "image"]
28
+ },
29
+ {
30
+ id: "gemini-3.1-flash-lite-image",
31
+ name: "Gemini 3.1 Flash Lite Image",
32
+ input: ["text", "image"]
33
+ },
34
+ {
35
+ id: "gemini-2.5-flash-image",
36
+ name: "Gemini 2.5 Flash Image",
37
+ input: ["text", "image"]
38
+ }
39
+ ];
40
+ /**
41
+ * Gemini image generation as a {@link ProviderImages}. Unavailable while `resolveKey` returns `undefined`.
42
+ *
43
+ * @param options - Key resolver and test overrides.
44
+ * @param options.resolveKey - Returns the Gemini API key, or `undefined` when none is set. Called on every request.
45
+ * @param options.fetch - Fetch override, for tests.
46
+ */
47
+ function googleImages(options) {
48
+ const doFetch = options.fetch ?? fetch;
49
+ return {
50
+ defaultModel: GOOGLE_IMAGE_MODELS[0].id,
51
+ models: GOOGLE_IMAGE_MODELS,
52
+ availability: () => options.resolveKey() ? { available: true } : {
53
+ available: false,
54
+ reason: "Gemini image generation needs a Gemini API key (GEMINI_API_KEY)."
55
+ },
56
+ async generate(request, signal) {
57
+ const key = options.resolveKey();
58
+ if (!key) throw new Error("Gemini image generation needs a Gemini API key. Set GEMINI_API_KEY, or add a key in the Google Gemini provider settings.");
59
+ const body = {
60
+ contents: [{
61
+ role: "user",
62
+ parts: [{ text: request.prompt }, ...(request.references ?? []).map((ref) => ({ inlineData: {
63
+ mimeType: ref.mediaType,
64
+ data: ref.data
65
+ } }))]
66
+ }],
67
+ generationConfig: {
68
+ responseModalities: ["TEXT", "IMAGE"],
69
+ ...request.aspectRatio ? { imageConfig: { aspectRatio: request.aspectRatio } } : {}
70
+ }
71
+ };
72
+ const once = async () => {
73
+ const res = await doFetch(`${BASE_URL}/models/${encodeURIComponent(request.model)}:generateContent`, {
74
+ method: "POST",
75
+ headers: {
76
+ "x-goog-api-key": key,
77
+ "content-type": "application/json"
78
+ },
79
+ body: JSON.stringify(body),
80
+ signal
81
+ });
82
+ if (!res.ok) throw await imageHttpError("Gemini", res);
83
+ const json = await res.json();
84
+ if (json.promptFeedback?.blockReason) throw new Error(`Gemini refused the prompt (${json.promptFeedback.blockReason}).`);
85
+ const candidate = json.candidates?.[0];
86
+ const parts = candidate?.content?.parts ?? [];
87
+ const images = parts.flatMap((part) => part.inlineData?.data ? [{
88
+ mediaType: part.inlineData.mimeType ?? "image/png",
89
+ data: part.inlineData.data
90
+ }] : []);
91
+ const text = parts.flatMap((part) => part.text ? [part.text] : []).join("\n").trim();
92
+ if (images.length === 0) {
93
+ const reason = candidate?.finishReason && candidate.finishReason !== "STOP" ? ` (${candidate.finishReason})` : "";
94
+ throw new Error(`Gemini returned no image${reason}${text ? `: ${text}` : "."}`);
95
+ }
96
+ return {
97
+ images,
98
+ model: request.model,
99
+ ...text ? { text } : {}
100
+ };
101
+ };
102
+ return repeatRequest(imageCount(request.count), once);
103
+ }
104
+ };
105
+ }
106
+ //#endregion
107
+ //#region src/providers/google.ts
108
+ /** Model used when a request names none. Served on free-tier Gemini API keys. */
109
+ const GOOGLE_DEFAULT_MODEL = "gemini-3.8-flash";
110
+ /**
111
+ * Models pi-ai hasn't bundled yet, served on both routes. A pi-ai release
112
+ * that bundles one supersedes its entry (bundled metadata always wins), so
113
+ * delete the entry then.
114
+ */
115
+ const GOOGLE_EXTRA_MODELS = [];
116
+ const lookupBuiltin = getBuiltinModel;
117
+ function builtinModel(route, modelId) {
118
+ try {
119
+ return lookupBuiltin(route, modelId);
120
+ } catch {
121
+ return;
122
+ }
123
+ }
124
+ /**
125
+ * pi-ai model record for `modelId` on `route`: the bundled entry, else a
126
+ * {@link GOOGLE_EXTRA_MODELS} overlay, else the default model's shape under
127
+ * the requested id. A catalog entry overlays whichever applies.
128
+ *
129
+ * @param route - Backend the request goes to.
130
+ * @param modelId - Requested model id.
131
+ * @param catalog - Per-run catalog override.
132
+ */
133
+ function resolveGoogleModel(route, modelId, catalog) {
134
+ const entry = lookupModel(route, modelId, catalog);
135
+ const bundled = builtinModel(route, modelId);
136
+ if (bundled) return mergeCatalogEntry(bundled, entry);
137
+ const extra = GOOGLE_EXTRA_MODELS.find((m) => m.id === modelId);
138
+ const template = builtinModel(route, extra?.template ?? "gemini-3.8-flash");
139
+ if (!template) throw new Error(`Google model registry (${route}) is missing ${extra?.template ?? "gemini-3.8-flash"}`);
140
+ if (extra) return mergeCatalogEntry({
141
+ ...template,
142
+ id: extra.id,
143
+ name: extra.name,
144
+ contextWindow: extra.contextWindow,
145
+ maxTokens: extra.maxTokens,
146
+ cost: extra.rates()
147
+ }, entry);
148
+ return mergeCatalogEntry({
149
+ ...template,
150
+ id: modelId,
151
+ name: modelId
152
+ }, entry);
153
+ }
154
+ function formatTools(tools) {
155
+ return sanitizeToolSpecs(tools, { profile: "openai" }).map((t) => ({
156
+ name: t.name,
157
+ description: t.description ?? "",
158
+ parameters: t.inputSchema
159
+ }));
160
+ }
161
+ function documentMarker(doc) {
162
+ return documentBlockMarker(doc, "document omitted");
163
+ }
164
+ function emptyUsage() {
165
+ return {
166
+ input: 0,
167
+ output: 0,
168
+ cacheRead: 0,
169
+ cacheWrite: 0,
170
+ totalTokens: 0,
171
+ cost: {
172
+ input: 0,
173
+ output: 0,
174
+ cacheRead: 0,
175
+ cacheWrite: 0,
176
+ total: 0
177
+ }
178
+ };
179
+ }
180
+ function piUserMessage(content, provider) {
181
+ for (const b of content) {
182
+ if (b.type === "audio") throw unsupportedMediaError("audio", provider);
183
+ if (b.type === "video") throw unsupportedMediaError("video", provider);
184
+ }
185
+ const parts = [];
186
+ for (const block of content) if (block.type === "text") parts.push({
187
+ type: "text",
188
+ text: block.text
189
+ });
190
+ else if (block.type === "image") {
191
+ assertResolvedMediaBlock(block, "Google wire messages");
192
+ parts.push({
193
+ type: "image",
194
+ data: block.data,
195
+ mimeType: block.mediaType
196
+ });
197
+ } else if (block.type === "document") {
198
+ assertResolvedMediaBlock(block, "Google wire messages");
199
+ parts.push({
200
+ type: "text",
201
+ text: documentMarker(block)
202
+ });
203
+ }
204
+ if (parts.length === 0) return null;
205
+ if (parts.length === 1 && parts[0].type === "text") return {
206
+ role: "user",
207
+ content: parts[0].text,
208
+ timestamp: Date.now()
209
+ };
210
+ return {
211
+ role: "user",
212
+ content: parts,
213
+ timestamp: Date.now()
214
+ };
215
+ }
216
+ function piToolResultMessage(result, toolName, provider) {
217
+ const content = typeof result.output === "string" ? [{
218
+ type: "text",
219
+ text: result.output
220
+ }] : result.output.map((block) => {
221
+ if (block.type === "image") {
222
+ assertResolvedMediaBlock(block, "Google wire messages");
223
+ return {
224
+ type: "image",
225
+ data: block.data,
226
+ mimeType: block.mediaType
227
+ };
228
+ }
229
+ if (block.type === "audio") throw unsupportedMediaError("audio", provider);
230
+ if (block.type === "video") throw unsupportedMediaError("video", provider);
231
+ if (block.type === "document") {
232
+ assertResolvedMediaBlock(block, "Google wire messages");
233
+ return {
234
+ type: "text",
235
+ text: documentMarker(block)
236
+ };
237
+ }
238
+ return {
239
+ type: "text",
240
+ text: block.text
241
+ };
242
+ });
243
+ return {
244
+ role: "toolResult",
245
+ toolCallId: result.callId,
246
+ toolName,
247
+ content,
248
+ isError: result.isError ?? false,
249
+ timestamp: Date.now()
250
+ };
251
+ }
252
+ /**
253
+ * One assistant turn for pi-ai. A turn holding this route's signatures is
254
+ * stamped with the TARGET model, so pi-ai treats it as its own and replays
255
+ * the signatures (Google accepts them across its models). Any other turn is
256
+ * stamped foreign: pi-ai then sends its reasoning as plain text and its
257
+ * calls unsigned.
258
+ */
259
+ function piAssistantMessage(content, model, route) {
260
+ const native = content.some((block) => (block.type === "text" || block.type === "tool_call" || block.type === "thinking") && block.signature !== void 0 && block.signatureProducer === route);
261
+ const own = (block) => native && block.signatureProducer === route ? block.signature : void 0;
262
+ const piContent = [];
263
+ for (const block of content) if (block.type === "text") {
264
+ const signature = own(block);
265
+ piContent.push({
266
+ type: "text",
267
+ text: block.text,
268
+ ...signature ? { textSignature: signature } : {}
269
+ });
270
+ } else if (block.type === "thinking") {
271
+ const action = reasoningWireAction(block, route);
272
+ if (action === "drop") continue;
273
+ const signature = action === "keep" ? own(block) : void 0;
274
+ piContent.push({
275
+ type: "thinking",
276
+ thinking: block.text,
277
+ ...signature ? { thinkingSignature: signature } : {}
278
+ });
279
+ } else if (block.type === "tool_call") {
280
+ const signature = own(block);
281
+ piContent.push({
282
+ type: "toolCall",
283
+ id: block.id,
284
+ name: block.name,
285
+ arguments: block.input,
286
+ ...signature ? { thoughtSignature: signature } : {}
287
+ });
288
+ }
289
+ return {
290
+ role: "assistant",
291
+ content: piContent,
292
+ api: model.api,
293
+ provider: native ? model.provider : "zidane",
294
+ model: native ? model.id : "history",
295
+ usage: emptyUsage(),
296
+ stopReason: "stop",
297
+ timestamp: Date.now()
298
+ };
299
+ }
300
+ /**
301
+ * Converts session messages to pi-ai messages for a Google backend.
302
+ *
303
+ * Unlike the Responses wire, an assistant turn stays whole: Gemini signs only
304
+ * the FIRST function call of a parallel step, so splitting the calls into
305
+ * separate model turns would leave the others unsigned. Its tool results
306
+ * follow in call order (pi-ai folds them into one user turn), with the
307
+ * synthetic placeholder for any the history lost.
308
+ *
309
+ * @param messages - Session messages, tool names already on the wire.
310
+ * @param model - Target model.
311
+ * @param route - Signature scope of the target (the backend's route name by default).
312
+ * @param provider - Provider named in unsupported-media errors. Defaults to `route`.
313
+ */
314
+ function toGooglePiMessages(messages, model, route, provider = route) {
315
+ messages = systemNotesAsText(messages);
316
+ const out = [];
317
+ for (let i = 0; i < messages.length; i++) {
318
+ const msg = messages[i];
319
+ if (msg.role === "assistant") {
320
+ out.push(piAssistantMessage(msg.content, model, route));
321
+ const calls = msg.content.filter((b) => b.type === "tool_call");
322
+ if (calls.length === 0) continue;
323
+ const next = messages[i + 1];
324
+ const results = new Map((next?.role === "user" ? next.content : []).filter((b) => b.type === "tool_result").map((r) => [r.callId, r]));
325
+ for (const call of calls) {
326
+ const result = results.get(call.id) ?? {
327
+ type: "tool_result",
328
+ callId: call.id,
329
+ output: "[Tool result missing due to internal error]",
330
+ isError: true
331
+ };
332
+ out.push(piToolResultMessage(result, call.name, provider));
333
+ }
334
+ if (next?.role === "user") {
335
+ const rest = piUserMessage(next.content.filter((b) => b.type !== "tool_result"), provider);
336
+ if (rest) out.push(rest);
337
+ i++;
338
+ }
339
+ continue;
340
+ }
341
+ const userMsg = piUserMessage(msg.content.filter((b) => b.type !== "tool_result"), provider);
342
+ if (userMsg) out.push(userMsg);
343
+ }
344
+ return out;
345
+ }
346
+ /**
347
+ * Converts pi-ai's final assistant message to a session message, tagging
348
+ * every thought signature with the route that issued it.
349
+ *
350
+ * @param message - pi-ai assistant message.
351
+ * @param route - Signature scope that produced it.
352
+ */
353
+ function fromGooglePiAssistantMessage(message, route) {
354
+ const signed = (signature) => signature ? {
355
+ signature,
356
+ signatureProducer: route
357
+ } : {};
358
+ const content = [];
359
+ for (const block of message.content) if (block.type === "text") content.push({
360
+ type: "text",
361
+ text: block.text,
362
+ ...signed(block.textSignature)
363
+ });
364
+ else if (block.type === "thinking") content.push({
365
+ type: "thinking",
366
+ text: block.thinking,
367
+ ...signed(block.thinkingSignature)
368
+ });
369
+ else if (block.type === "toolCall") content.push({
370
+ type: "tool_call",
371
+ id: block.id,
372
+ name: block.name,
373
+ input: block.arguments,
374
+ ...signed(block.thoughtSignature)
375
+ });
376
+ return {
377
+ role: "assistant",
378
+ content
379
+ };
380
+ }
381
+ function mapStopReason(stopReason, hasToolCalls) {
382
+ switch (stopReason) {
383
+ case "toolUse": return "tool-calls";
384
+ case "length": return "length";
385
+ case "error": return "error";
386
+ case "aborted": return "other";
387
+ default: return hasToolCalls ? "tool-calls" : "stop";
388
+ }
389
+ }
390
+ /** Forced tool choice, written into Gemini's `functionCallingConfig`. */
391
+ function applyToolChoice(payload, options, toWire) {
392
+ const choice = options.toolChoice;
393
+ if (!choice || choice.type === "auto") return payload;
394
+ const params = payload;
395
+ if (!params.config?.toolConfig) return payload;
396
+ params.config.toolConfig.functionCallingConfig = choice.type === "tool" && choice.name ? {
397
+ mode: "ANY",
398
+ allowedFunctionNames: [toWire(choice.name)]
399
+ } : { mode: "ANY" };
400
+ return params;
401
+ }
402
+ /**
403
+ * Transient Gemini failures worth a loop-level retry once pi-ai's own retries
404
+ * (408 / 409 / 429 / 5xx before the stream opens) are spent.
405
+ */
406
+ const TRANSIENT_GOOGLE_MESSAGE_RE = /RESOURCE_EXHAUSTED|UNAVAILABLE|DEADLINE_EXCEEDED|overloaded|high demand|rate.?limit|try again later/i;
407
+ /** Gemini's `RetryInfo` detail in a 429 body: `"retryDelay": "34s"`. */
408
+ const RETRY_DELAY_RE = /"retryDelay"\s*:\s*"(\d+(?:\.\d+)?)s"/;
409
+ /** pi-ai formats HTTP failures as `<status>: <body>`. */
410
+ function leadingStatus(message) {
411
+ const match = /^(\d{3})\b/.exec(message);
412
+ return match ? Number(match[1]) : void 0;
413
+ }
414
+ function parseGoogleErrorBody(text) {
415
+ try {
416
+ const parsed = JSON.parse(text);
417
+ return parsed && typeof parsed.error === "object" && parsed.error ? parsed.error : void 0;
418
+ } catch {
419
+ return;
420
+ }
421
+ }
422
+ /**
423
+ * The stream error pi-ai reports, as Google wrote it. `@google/genai` wraps a
424
+ * non-JSON response as `{"error":{"message":"<body>"}}`, so Google's own
425
+ * `{"error":{…}}` can arrive JSON-encoded inside it.
426
+ *
427
+ * @param raw - pi-ai's `errorMessage`: `<status>: <body>` or the SDK's message.
428
+ * @returns The innermost message prefixed with its HTTP and RPC status, the
429
+ * status, and the innermost body (it carries details such as `retryDelay`).
430
+ */
431
+ function unwrapGoogleError(raw) {
432
+ let status = leadingStatus(raw);
433
+ let rpcStatus;
434
+ let text = status === void 0 ? raw : raw.replace(/^\d{3}:\s*/, "");
435
+ let message;
436
+ let body = raw;
437
+ for (let error = parseGoogleErrorBody(text); error; error = parseGoogleErrorBody(text)) {
438
+ body = text;
439
+ if (typeof error.code === "number") status = error.code;
440
+ if (typeof error.status === "string" && /^[A-Z_]+$/.test(error.status)) rpcStatus = error.status;
441
+ if (typeof error.message !== "string") break;
442
+ message = text = error.message.trim();
443
+ }
444
+ if (!message) return {
445
+ message: raw,
446
+ ...status !== void 0 ? { status } : {}
447
+ };
448
+ const prefix = [status, rpcStatus].filter((part) => part !== void 0).join(" ");
449
+ return {
450
+ message: prefix ? `${prefix}: ${message}` : message,
451
+ ...status !== void 0 ? { status } : {},
452
+ body
453
+ };
454
+ }
455
+ /**
456
+ * Classify a Gemini API / Vertex AI error.
457
+ *
458
+ * @param err - Error thrown by pi-ai or the SDK.
459
+ * @param label - Product a limit's message names; routes over other services (Antigravity) pass their own.
460
+ */
461
+ function classifyGoogleError(err, label = "Gemini") {
462
+ const prelude = classifyErrorPrelude(err);
463
+ if (prelude === "not-object") return null;
464
+ if (prelude === "aborted") return { kind: "aborted" };
465
+ const anyErr = err;
466
+ const message = typeof anyErr.message === "string" ? anyErr.message : "";
467
+ if (matchesContextExceeded(message)) return {
468
+ kind: "context_exceeded",
469
+ providerCode: "context_length_exceeded",
470
+ message
471
+ };
472
+ if (matchesToolPairingError(message)) return {
473
+ kind: "tool_pairing_corruption",
474
+ message
475
+ };
476
+ if (!message) return null;
477
+ const status = typeof anyErr.status === "number" ? anyErr.status : leadingStatus(message);
478
+ const retryable = status !== void 0 && isRetryableHttpStatus(status) || TRANSIENT_GOOGLE_MESSAGE_RE.test(message);
479
+ const reason = providerLimitReason({
480
+ status,
481
+ message
482
+ });
483
+ if (reason) {
484
+ const delay = RETRY_DELAY_RE.exec(typeof anyErr.body === "string" ? anyErr.body : message);
485
+ return classifyProviderLimit({
486
+ reason,
487
+ provider: label,
488
+ ...delay ? { retryAfterMs: Number(delay[1]) * 1e3 } : {}
489
+ }, {
490
+ ...status !== void 0 ? { providerCode: String(status) } : {},
491
+ retryable
492
+ });
493
+ }
494
+ return {
495
+ kind: "provider_error",
496
+ ...status !== void 0 ? { providerCode: String(status) } : {},
497
+ message,
498
+ ...retryable ? { retryable: true } : {}
499
+ };
500
+ }
501
+ /** Every budget level at `tokens`: pi-ai picks the level, the caller fixed the budget. */
502
+ function budgetsAt(tokens) {
503
+ return {
504
+ minimal: tokens,
505
+ low: tokens,
506
+ medium: tokens,
507
+ high: tokens
508
+ };
509
+ }
510
+ /** Names a {@link googleCompatible} route can't take: built-in providers and signature producers. */
511
+ const RESERVED_ROUTE_NAMES = /* @__PURE__ */ new Set([
512
+ "google",
513
+ "google-vertex",
514
+ "anthropic",
515
+ "openai",
516
+ "zidane"
517
+ ]);
518
+ function createGoogleProvider(route, params) {
519
+ const defaultModel = params?.defaultModel || "gemini-3.8-flash";
520
+ const capabilities = params?.capabilities ?? {
521
+ vision: true,
522
+ imageInToolResult: true,
523
+ audio: false,
524
+ video: false,
525
+ documents: false
526
+ };
527
+ return {
528
+ name: route.name,
529
+ meta: {
530
+ defaultModel,
531
+ isOAuth: route.isOAuth,
532
+ capabilities
533
+ },
534
+ formatTools,
535
+ userMessage,
536
+ assistantMessage,
537
+ toolResultsMessage,
538
+ classifyError: route.classifyError ?? classifyGoogleError,
539
+ async stream(options, callbacks) {
540
+ const auth = await route.auth(callbacks);
541
+ try {
542
+ return await streamTurn(auth, options, callbacks);
543
+ } finally {
544
+ auth.dispose?.();
545
+ }
546
+ }
547
+ };
548
+ async function streamTurn(auth, options, callbacks) {
549
+ const modelId = options.model || defaultModel;
550
+ const model = route.resolveModel ? route.resolveModel(modelId, options.modelCatalog) : resolveGoogleModel(route.name, modelId, options.modelCatalog);
551
+ const scope = signatureScopeOf(route, model.id);
552
+ const tools = options.tools;
553
+ const nameBoundary = createToolNameBoundary(options.messages, tools.map((tool) => tool.name));
554
+ const context = normalizeContext({
555
+ systemPrompt: renderSystemForWire(options.system),
556
+ messages: toGooglePiMessages(nameBoundary.messages, model, scope, route.name),
557
+ tools: tools.map((tool) => ({
558
+ ...tool,
559
+ name: nameBoundary.toWire(tool.name)
560
+ }))
561
+ });
562
+ const api = await route.load();
563
+ const base = {
564
+ ...auth.apiKey ? { apiKey: auth.apiKey } : {},
565
+ ...auth.env ? { env: auth.env } : {},
566
+ ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
567
+ signal: options.signal,
568
+ ...params?.extraHeaders ? { headers: params.extraHeaders } : {},
569
+ onPayload: (payload) => applyToolChoice(payload, options, nameBoundary.toWire)
570
+ };
571
+ let s;
572
+ if (options.thinking === "adaptive" && model.reasoning) {
573
+ const { buildBaseOptions } = await import("@earendil-works/pi-ai/api/simple-options");
574
+ s = api.stream(model, context, {
575
+ ...buildBaseOptions(model, context, base),
576
+ thinking: { enabled: true }
577
+ });
578
+ } else {
579
+ const reasoning = options.thinking && options.thinking !== "off" && options.thinking !== "adaptive" ? options.thinking : void 0;
580
+ s = api.streamSimple(model, context, {
581
+ ...base,
582
+ ...reasoning ? { reasoning } : {},
583
+ ...reasoning && options.thinkingBudget !== void 0 ? { thinkingBudgets: budgetsAt(options.thinkingBudget) } : {}
584
+ });
585
+ }
586
+ let finalMessage;
587
+ let text = "";
588
+ let thinking = "";
589
+ for await (const event of s) if (event.type === "text_delta") {
590
+ text += event.delta;
591
+ callbacks.onText(event.delta);
592
+ } else if (event.type === "thinking_delta") {
593
+ thinking += event.delta;
594
+ callbacks.onThinking?.(event.delta);
595
+ } else if (event.type === "thinking_end") {
596
+ const delta = event.content.startsWith(thinking) ? event.content.slice(thinking.length) : thinking ? "" : event.content;
597
+ if (delta) {
598
+ thinking += delta;
599
+ callbacks.onThinking?.(delta);
600
+ }
601
+ } else if (event.type === "toolcall_start" || event.type === "toolcall_delta") callbacks.onToolCallDelta?.();
602
+ else if (event.type === "done") finalMessage = event.message;
603
+ else if (event.type === "error") {
604
+ const { message, ...details } = unwrapGoogleError(event.error.errorMessage || `Google ${route.name} API error`);
605
+ throw Object.assign(new Error(message), details);
606
+ }
607
+ finalMessage ??= await s.result();
608
+ const toolCalls = finalMessage.content.filter((block) => block.type === "toolCall").map((block) => ({
609
+ id: block.id,
610
+ name: nameBoundary.fromWire(block.name),
611
+ input: block.arguments
612
+ }));
613
+ const assistantTurn = fromGooglePiAssistantMessage(finalMessage, scope);
614
+ assistantTurn.content = assistantTurn.content.map((block) => block.type === "tool_call" ? {
615
+ ...block,
616
+ name: nameBoundary.fromWire(block.name)
617
+ } : block);
618
+ const finishReason = mapStopReason(finalMessage.stopReason, toolCalls.length > 0);
619
+ const usage = await fillEstimatedCost({
620
+ input: finalMessage.usage.input,
621
+ output: finalMessage.usage.output,
622
+ cacheRead: finalMessage.usage.cacheRead || void 0,
623
+ ...finalMessage.usage.reasoning ? { thinking: finalMessage.usage.reasoning } : {},
624
+ cost: finalMessage.usage.cost.total || void 0,
625
+ finishReason,
626
+ modelId
627
+ }, route.name, void 0, options.modelCatalog);
628
+ return {
629
+ assistantMessage: assistantTurn,
630
+ text: text || finalMessage.content.flatMap((block) => block.type === "text" ? [block.text] : []).join(""),
631
+ toolCalls,
632
+ done: toolCalls.length === 0,
633
+ usage
634
+ };
635
+ }
636
+ }
637
+ /**
638
+ * The scope a model's thought signatures carry. A custom route's scope can't
639
+ * impersonate a built-in producer, whose signatures it would then replay.
640
+ */
641
+ function signatureScopeOf(route, modelId) {
642
+ const scope = route.signatureScope?.(modelId) ?? route.name;
643
+ if (scope !== route.name && RESERVED_ROUTE_NAMES.has(scope)) throw new Error(`${route.name}: signature scope "${scope}" is reserved for a built-in provider`);
644
+ return scope;
645
+ }
646
+ /**
647
+ * Creates a provider over another Gemini-wire backend: an extension supplies
648
+ * the transport, auth and model records; message conversion, thought
649
+ * signatures (tagged with the route's scope, never replayed elsewhere), tool
650
+ * names, thinking levels, usage and cost come from the same plumbing as
651
+ * {@link google} and {@link googleVertex}.
652
+ *
653
+ * @param route - The backend: name, transport, auth and models.
654
+ * @param params - Default model, capabilities and extra headers.
655
+ *
656
+ * @example
657
+ * ```ts
658
+ * import { googleCompatible } from 'zidane/providers'
659
+ *
660
+ * const provider = googleCompatible({
661
+ * name: 'my-gateway',
662
+ * isOAuth: false,
663
+ * load: async () => myGeminiTransport,
664
+ * auth: () => ({ apiKey: process.env.MY_GATEWAY_KEY }),
665
+ * resolveModel: id => myModels[id],
666
+ * }, { defaultModel: 'gemini-3.8-flash' })
667
+ * ```
668
+ */
669
+ function googleCompatible(route, params) {
670
+ if (!route.name || RESERVED_ROUTE_NAMES.has(route.name)) throw new Error(`googleCompatible: route name "${route.name}" is reserved for a built-in provider`);
671
+ return createGoogleProvider(route, params);
672
+ }
673
+ /**
674
+ * Load the {@link GoogleWireKit} from the HOST's pi-ai. An extension's
675
+ * transport gets it through `zidane/providers` (mapped to the host's module)
676
+ * instead of importing pi-ai itself: one pi-ai copy, the exact wire the
677
+ * host's pi-ai speaks, and it resolves inside compiled binaries too. pi-ai's
678
+ * bare specifiers can't be host-mapped the way `zidane` is: the mapping's
679
+ * loader would re-import the specifier it intercepts. Loaded on first call,
680
+ * since `@google/genai` comes with it.
681
+ */
682
+ async function loadGoogleWireKit() {
683
+ const [shared, core] = await Promise.all([import("@earendil-works/pi-ai/api/google-shared"), import("@earendil-works/pi-ai")]);
684
+ return {
685
+ convertMessages: shared.convertMessages,
686
+ convertTools: shared.convertTools,
687
+ isThinkingPart: shared.isThinkingPart,
688
+ retainThoughtSignature: shared.retainThoughtSignature,
689
+ requiresToolCallId: shared.requiresToolCallId,
690
+ mapStopReasonString: shared.mapStopReasonString,
691
+ resolveGoogleFunctionCallingMode: shared.resolveGoogleFunctionCallingMode,
692
+ supportsGoogleStrictToolSampling: shared.supportsGoogleStrictToolSampling,
693
+ createAssistantMessageEventStream: core.createAssistantMessageEventStream,
694
+ collapseSystemMessages: core.collapseSystemMessages,
695
+ getInitialSystemMessage: core.getInitialSystemMessage,
696
+ getCurrentTools: core.getCurrentTools,
697
+ getSystemMessageText: core.getSystemMessageText,
698
+ calculateCost: core.calculateCost,
699
+ clampThinkingLevel: core.clampThinkingLevel
700
+ };
701
+ }
702
+ async function loadGeminiApi() {
703
+ return await import("@earendil-works/pi-ai/api/google-generative-ai");
704
+ }
705
+ async function loadVertexApi() {
706
+ return await import("@earendil-works/pi-ai/api/google-vertex");
707
+ }
708
+ /**
709
+ * Creates the Google Gemini provider on the Gemini API (Google AI Studio).
710
+ * Authenticates with `apiKey`, else `GEMINI_API_KEY`, else `GOOGLE_API_KEY`.
711
+ *
712
+ * @param params - API key, default model, capabilities and extra headers.
713
+ *
714
+ * @example
715
+ * ```ts
716
+ * import { createAgent } from 'zidane'
717
+ * import { google } from 'zidane/providers/google'
718
+ *
719
+ * const agent = createAgent({
720
+ * provider: google({ apiKey: process.env.GEMINI_API_KEY }),
721
+ * })
722
+ * await agent.run({ prompt: 'Say hello.' })
723
+ * ```
724
+ */
725
+ function google(params) {
726
+ const resolveKey = () => params?.apiKey || process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY;
727
+ return {
728
+ ...createGoogleProvider({
729
+ name: "google",
730
+ load: loadGeminiApi,
731
+ isOAuth: false,
732
+ auth: () => {
733
+ const apiKey = resolveKey();
734
+ if (!apiKey) throw new Error("No Gemini API key found. Set GEMINI_API_KEY, or add a key in the Google Gemini provider settings.");
735
+ return { apiKey };
736
+ }
737
+ }, params),
738
+ images: googleImages({ resolveKey })
739
+ };
740
+ }
741
+ const VERTEX_DIR_PREFIX = "zidane-vertex-";
742
+ const VERTEX_DIR_RE = /^zidane-vertex-[A-Za-z0-9]{6}$/;
743
+ /** Older than any request: its process crashed or was killed before cleanup. */
744
+ const STALE_VERTEX_DIR_MS = 36e5;
745
+ let nextVertexSweepAt = 0;
746
+ /**
747
+ * Remove this user's credentials dirs that a crashed request left behind;
748
+ * they hold a refresh token. Listing the temp dir can take hundreds of
749
+ * milliseconds, so it runs in the background, at most once an hour.
750
+ */
751
+ function sweepStaleVertexDirs() {
752
+ const now = Date.now();
753
+ if (now < nextVertexSweepAt) return;
754
+ nextVertexSweepAt = now + STALE_VERTEX_DIR_MS;
755
+ const root = tmpdir();
756
+ const uid = process.getuid?.();
757
+ const sweepOne = async (name) => {
758
+ const dir = join(root, name);
759
+ const stat = await lstat(dir);
760
+ if (stat.isDirectory() && (uid === void 0 || stat.uid === uid) && now - stat.mtimeMs > STALE_VERTEX_DIR_MS) await rm(dir, {
761
+ recursive: true,
762
+ force: true
763
+ });
764
+ };
765
+ readdir(root).then((names) => Promise.allSettled(names.filter((name) => VERTEX_DIR_RE.test(name)).map(sweepOne))).catch(() => {});
766
+ }
767
+ /**
768
+ * Request auth from the stored "Sign in with Google" grant (the pinned
769
+ * account's, else the active one), or `undefined` when none is stored.
770
+ *
771
+ * The access token rotates through zidane's OAuth path (cross-process locks,
772
+ * persistence, refresh hooks, a clear "sign in again" for a revoked grant).
773
+ * Vertex itself authenticates from an `authorized_user` credentials file
774
+ * holding the refresh token: pi-ai's Vertex client takes no bare access
775
+ * token. The file lives in a private temp dir for the length of the request.
776
+ */
777
+ async function signedInAuth(params, callbacks) {
778
+ const account = params?.account;
779
+ const read = () => account ? readAccountOAuthCredential(GOOGLE_VERTEX_OAUTH_ID, account) : readOAuthCredentials()[GOOGLE_VERTEX_OAUTH_ID];
780
+ if (!read()) {
781
+ if (account) throw new Error(pinnedAccountMissingError("google-vertex"));
782
+ return;
783
+ }
784
+ await resolveOAuthApiKey({
785
+ provider: "google-vertex",
786
+ providerId: GOOGLE_VERTEX_OAUTH_ID,
787
+ ...account ? { params: { account } } : {},
788
+ missingError: "No Google sign-in found for Vertex AI. Sign in with Google again.",
789
+ refreshError: (reason) => `Google sign-in refresh failed. Sign in with Google again. ${reason}`
790
+ }, callbacks);
791
+ const grant = read();
792
+ if (!grant?.refresh || !grant.clientId || !grant.clientSecret || !grant.projectId) throw new Error("This Google sign-in is missing its OAuth client or project. Sign in with Google again.");
793
+ const project = params?.project || grant.projectId;
794
+ sweepStaleVertexDirs();
795
+ const dir = mkdtempSync(join(tmpdir(), VERTEX_DIR_PREFIX));
796
+ const file = join(dir, "credentials.json");
797
+ try {
798
+ writeFileSync(file, JSON.stringify({
799
+ type: "authorized_user",
800
+ client_id: grant.clientId,
801
+ client_secret: grant.clientSecret,
802
+ refresh_token: grant.refresh,
803
+ quota_project_id: project
804
+ }), { mode: 384 });
805
+ } catch (err) {
806
+ rmSync(dir, {
807
+ recursive: true,
808
+ force: true
809
+ });
810
+ throw err;
811
+ }
812
+ return {
813
+ env: {
814
+ GOOGLE_CLOUD_PROJECT: project,
815
+ GOOGLE_CLOUD_LOCATION: params?.location || process.env.GOOGLE_CLOUD_LOCATION || "global",
816
+ GOOGLE_APPLICATION_CREDENTIALS: file
817
+ },
818
+ dispose: () => rmSync(dir, {
819
+ recursive: true,
820
+ force: true
821
+ })
822
+ };
823
+ }
824
+ /**
825
+ * Credentials and settings for a Vertex AI request, read per turn so a
826
+ * credential configured after the provider was built still applies. In
827
+ * order: a pinned account's sign-in, a Vertex API key, the active "Sign in
828
+ * with Google" grant, then Google Cloud credentials for the project.
829
+ *
830
+ * @param params - Explicit settings; environment variables fill the rest.
831
+ * @param callbacks - Stream callbacks, for the OAuth refresh hook.
832
+ */
833
+ async function resolveVertexAuth(params, callbacks) {
834
+ if (params?.account) return await signedInAuth(params, callbacks);
835
+ const apiKey = params?.apiKey || process.env.GOOGLE_CLOUD_API_KEY;
836
+ if (apiKey) return { apiKey };
837
+ const signedIn = await signedInAuth(params, callbacks);
838
+ if (signedIn) return signedIn;
839
+ const project = params?.project || process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
840
+ if (!project) throw new Error("Vertex AI needs a Google Cloud project. Set GOOGLE_CLOUD_PROJECT, or configure it in the Google Vertex AI provider settings.");
841
+ const location = params?.location || process.env.GOOGLE_CLOUD_LOCATION || "global";
842
+ const credentialsFile = params?.credentialsFile || process.env.GOOGLE_APPLICATION_CREDENTIALS;
843
+ return { env: {
844
+ GOOGLE_CLOUD_PROJECT: project,
845
+ GOOGLE_CLOUD_LOCATION: location,
846
+ ...credentialsFile ? { GOOGLE_APPLICATION_CREDENTIALS: credentialsFile } : {}
847
+ } };
848
+ }
849
+ /**
850
+ * Creates the Google Vertex AI provider. Authenticates with a pinned
851
+ * `account`'s Google sign-in, else a Vertex API key (`apiKey` or
852
+ * `GOOGLE_CLOUD_API_KEY`), else the active "Sign in with Google" grant, else
853
+ * Google Cloud credentials for `project` in `location`. See
854
+ * {@link resolveVertexAuth}.
855
+ *
856
+ * @param params - Credentials, project, location, default model, capabilities and extra headers.
857
+ *
858
+ * @example
859
+ * ```ts
860
+ * import { createAgent } from 'zidane'
861
+ * import { googleVertex } from 'zidane/providers/google'
862
+ *
863
+ * // After `gcloud auth application-default login`:
864
+ * const agent = createAgent({
865
+ * provider: googleVertex({ project: 'my-project' }),
866
+ * })
867
+ * await agent.run({ prompt: 'Say hello.' })
868
+ * ```
869
+ */
870
+ function googleVertex(params) {
871
+ return createGoogleProvider({
872
+ name: "google-vertex",
873
+ load: loadVertexApi,
874
+ isOAuth: !!params?.account || !(params?.apiKey || process.env.GOOGLE_CLOUD_API_KEY),
875
+ auth: (callbacks) => resolveVertexAuth(params, callbacks)
876
+ }, params);
877
+ }
878
+ //#endregion
879
+ export { google as a, loadGoogleWireKit as c, toGooglePiMessages as d, GOOGLE_IMAGE_MODELS as f, fromGooglePiAssistantMessage as i, resolveGoogleModel as l, GOOGLE_EXTRA_MODELS as n, googleCompatible as o, classifyGoogleError as r, googleVertex as s, GOOGLE_DEFAULT_MODEL as t, resolveVertexAuth as u };
880
+
881
+ //# sourceMappingURL=google-O6pRnddf.js.map