@fased/fased 0.1.58 → 0.1.59

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 (167) hide show
  1. package/dist/agents/anthropic-payload-policy.js +188 -0
  2. package/dist/agents/anthropic-transport-stream.js +736 -0
  3. package/dist/agents/copilot-dynamic-headers.js +1 -1
  4. package/dist/agents/custom-api-registry.js +21 -0
  5. package/dist/agents/google-transport-stream.js +543 -0
  6. package/dist/agents/models-config.js +2 -1
  7. package/dist/agents/openai-completions-compat.js +49 -0
  8. package/dist/agents/openai-responses-payload-policy.js +100 -0
  9. package/dist/agents/openai-tool-schema.js +130 -0
  10. package/dist/agents/openai-transport-stream.js +1040 -0
  11. package/dist/agents/pi-embedded-runner/compact.js +9 -0
  12. package/dist/agents/pi-embedded-runner/run/attempt.js +9 -0
  13. package/dist/agents/provider-request-config.js +1 -1
  14. package/dist/agents/provider-stream.js +29 -0
  15. package/dist/agents/provider-transport-fetch.js +89 -0
  16. package/dist/agents/provider-transport-stream.js +72 -0
  17. package/dist/agents/system-prompt-cache-boundary.js +1 -1
  18. package/dist/agents/system-prompt.js +1 -1
  19. package/dist/agents/transport-message-transform.js +143 -0
  20. package/dist/agents/transport-stream-shared.js +81 -0
  21. package/dist/brand.js +1 -1
  22. package/dist/build-info.json +3 -3
  23. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  24. package/dist/channels/delivery.js +78 -0
  25. package/dist/channels/plugins/catalog.js +10 -5
  26. package/dist/cli/components-cli.js +16 -13
  27. package/dist/cli/program/register.subclis.js +9 -0
  28. package/dist/cli/providers-cli.js +79 -6
  29. package/dist/cli/services-cli.js +28 -0
  30. package/dist/commands/auth-choice.js +1 -1
  31. package/dist/commands/models/list.auth-overview.js +12 -0
  32. package/dist/commands/onboard-channels.js +5 -0
  33. package/dist/commands/onboard-helpers.js +1 -1
  34. package/dist/commands/vllm-setup.js +12 -3
  35. package/dist/control-ui/assets/{app-CpgQGqe6.js → app-S_jg-ADS.js} +1706 -1675
  36. package/dist/control-ui/assets/{index-DiBBflpv.js → index-C22rDOFm.js} +3 -3
  37. package/dist/control-ui/assets/{providers-Cc89iMdx.js → providers-QMn4w2_f.js} +3 -3
  38. package/dist/control-ui/index.html +1 -1
  39. package/dist/control-ui/version.json +1 -1
  40. package/dist/gateway/control-reply-text.js +10 -1
  41. package/dist/gateway/method-scopes.js +2 -0
  42. package/dist/gateway/protocol/schema/agents-models-skills.js +1 -0
  43. package/dist/gateway/server-chat.js +3 -3
  44. package/dist/gateway/server-methods/channels.js +10 -2
  45. package/dist/gateway/server-methods/chat.js +19 -12
  46. package/dist/gateway/server-methods/models.js +4 -0
  47. package/dist/gateway/server-methods/services.js +47 -2
  48. package/dist/gateway/server-methods-list.js +2 -0
  49. package/dist/gateway/session-event-payloads.js +20 -9
  50. package/dist/infra/google-api-base-url.js +29 -0
  51. package/dist/plugin-sdk/{accounts-BJuN64m-.js → accounts-DxblobYM.js} +1 -1
  52. package/dist/plugin-sdk/{actions-DaEvM9Bl.js → actions-D4UNimWa.js} +2 -2
  53. package/dist/plugin-sdk/{active-listener-B8Kuanet.js → active-listener-CFuy106s.js} +1 -1
  54. package/dist/plugin-sdk/{audio-preflight-D7tCO0EH.js → audio-preflight-CxZixyhW.js} +25 -25
  55. package/dist/plugin-sdk/{audit-CVWH4BVP.js → audit-Cs0K7LBp.js} +1 -1
  56. package/dist/plugin-sdk/{channel-access-CH9FAS-I.js → channel-access-D5aHoTY8.js} +1 -1
  57. package/dist/plugin-sdk/{channel-access-configure-CI0je66H.js → channel-access-configure-Dvch9mMQ.js} +1 -1
  58. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  59. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  60. package/dist/plugin-sdk/{chunk-01oBnQjh.js → chunk-B0FUGc5A.js} +1 -1
  61. package/dist/plugin-sdk/{chunk-items-D2zDJ8q8.js → chunk-items-Cy9a3X7x.js} +1 -1
  62. package/dist/plugin-sdk/command-status.js +25 -25
  63. package/dist/plugin-sdk/{commands-registry-ckg4fhYa.js → commands-registry-DmDRhRAU.js} +2 -2
  64. package/dist/plugin-sdk/{common-zHvdO8ly.js → common-CfUOowOp.js} +1 -1
  65. package/dist/plugin-sdk/{config-DrhYEiCM.js → config-mxULv0fQ.js} +1 -1
  66. package/dist/plugin-sdk/{config-schema-BtjeV5Hm.js → config-schema-D8E0LX8X.js} +4405 -460
  67. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  68. package/dist/plugin-sdk/discord.d.ts +3 -3
  69. package/dist/plugin-sdk/discord.js +19 -19
  70. package/dist/plugin-sdk/{handle-action-5dhYpMbP.js → handle-action-DbVCF9HZ.js} +13 -13
  71. package/dist/plugin-sdk/{helpers-E9aafRzA.js → helpers-BXbdgs5H.js} +1 -1
  72. package/dist/plugin-sdk/{image-ops-D2U9ctmu.js → image-ops-D7jPxaph.js} +1 -1
  73. package/dist/plugin-sdk/index.d.ts +4 -4
  74. package/dist/plugin-sdk/index.js +27 -27
  75. package/dist/plugin-sdk/{login-hql0fNuL.js → login-BRxww6nv.js} +3 -3
  76. package/dist/plugin-sdk/{login-qr-DJJ0svux.js → login-qr-DJXsk9XH.js} +3 -3
  77. package/dist/plugin-sdk/{manager-ciNnAMBn.js → manager-Y2S_5IlI.js} +2 -2
  78. package/dist/plugin-sdk/{markdown-tables-BKxbua_h.js → markdown-tables-DHLFQqaF.js} +1 -1
  79. package/dist/plugin-sdk/{media-DUIZ6x9H.js → media-Czo4carg.js} +2 -2
  80. package/dist/plugin-sdk/{monitor-D-giXewh.js → monitor-CFHFAUOj.js} +30 -30
  81. package/dist/plugin-sdk/{monitor-CNSGw4uQ.js → monitor-xS9A8Upr.js} +29 -29
  82. package/dist/plugin-sdk/ollama-runtime.js +970 -0
  83. package/dist/plugin-sdk/{openresponses-http-dbTd8UMk.js → openresponses-http-BcUbeVV6.js} +25 -25
  84. package/dist/plugin-sdk/{optional-runtime-dependency-CKsJL1PD.js → optional-runtime-dependency-DdMbxS24.js} +1 -1
  85. package/dist/plugin-sdk/{outbound-DhGrjRft.js → outbound-Db8mRN6P.js} +6 -6
  86. package/dist/plugin-sdk/{outbound-attachment-DAF8huFz.js → outbound-attachment-BqFjcypb.js} +2 -2
  87. package/dist/plugin-sdk/{pairing-message-BsJVvXkb.js → pairing-message-B3GC_7CD.js} +1 -1
  88. package/dist/plugin-sdk/{pairing-message-C403mq4K.d.ts → pairing-message-CxonJmwM.d.ts} +1 -1
  89. package/dist/plugin-sdk/{pairing-store-DwM-BNbj.js → pairing-store-CQikgIbw.js} +2 -2
  90. package/dist/plugin-sdk/{pi-model-discovery-DkIdlJvJ.js → pi-model-discovery-CY3m5zJP.js} +1 -1
  91. package/dist/plugin-sdk/{pi-model-discovery-runtime-Cv7okUMt.js → pi-model-discovery-runtime-DSh1T2pn.js} +2 -2
  92. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  93. package/dist/plugin-sdk/{pw-ai-Dxd87W3J.js → pw-ai-AFUqn-tQ.js} +3 -3
  94. package/dist/plugin-sdk/{replies-Bx61SvAC.js → replies-Fzh9PhXl.js} +2 -2
  95. package/dist/plugin-sdk/{resolve-outbound-target-bc-FpPZg.js → resolve-outbound-target-KpF3co6T.js} +1 -1
  96. package/dist/plugin-sdk/{resolve-route-D0eBYPtb.js → resolve-route-CDl74O19.js} +1 -1
  97. package/dist/plugin-sdk/{retry-policy-HTrC4zfC.js → retry-policy-D4uiqUOG.js} +1 -1
  98. package/dist/plugin-sdk/{runtime-BVkIrYLQ.js → runtime-Bb6KRnCC.js} +6 -4
  99. package/dist/plugin-sdk/{runtime-dependency-B2PqMdBu.js → runtime-dependency-DLDmB94f.js} +2 -2
  100. package/dist/plugin-sdk/sat-runtime.d.ts +2 -2
  101. package/dist/plugin-sdk/sat-runtime.js +25 -25
  102. package/dist/plugin-sdk/{send-DgzZUx2i.js → send-5nFktR1n.js} +5 -5
  103. package/dist/plugin-sdk/{send-DT2egZNK.js → send-Bfzf11VY.js} +7 -7
  104. package/dist/plugin-sdk/{send-BGDGzxUu.js → send-C4k4JuJN.js} +6 -6
  105. package/dist/plugin-sdk/{send-KJy3ajA3.js → send-D-uDKGIq.js} +6 -6
  106. package/dist/plugin-sdk/{send-C3QWSYLR.js → send-Du8ykP1k.js} +8 -8
  107. package/dist/plugin-sdk/{session-BCIEFOu1.js → session-CS-Td9EM.js} +2 -2
  108. package/dist/plugin-sdk/{skill-commands-CyIkcR8P.js → skill-commands-CFAlXWHk.js} +2 -2
  109. package/dist/plugin-sdk/{slack-DjKNh2uh.js → slack-DXYGbizH.js} +37 -37
  110. package/dist/plugin-sdk/{slack-actions-CeM9LWDp.js → slack-actions-BcMkNv1Z.js} +11 -11
  111. package/dist/plugin-sdk/slack.d.ts +3 -3
  112. package/dist/plugin-sdk/slack.js +9 -9
  113. package/dist/plugin-sdk/{sticker-cache-DcWzQ2wx.js → sticker-cache-Dr3rYfU1.js} +3 -3
  114. package/dist/plugin-sdk/{tables-B2iehyjG.js → tables-bFXyRpkx.js} +1 -1
  115. package/dist/plugin-sdk/{targets-BPket0SF.js → targets-DH7a9lKB.js} +1 -1
  116. package/dist/plugin-sdk/{telegram-actions-Cx_HWVht.js → telegram-actions-Besbw_Hg.js} +28 -28
  117. package/dist/plugin-sdk/telegram.d.ts +3 -3
  118. package/dist/plugin-sdk/telegram.js +8 -8
  119. package/dist/plugin-sdk/{thread-bindings.lifecycle-CYH69SIw.js → thread-bindings.lifecycle-CgOvvdw8.js} +3 -3
  120. package/dist/plugin-sdk/{thread-bindings.state-BN6461WG.js → thread-bindings.state-CQpk44Hq.js} +2 -2
  121. package/dist/plugin-sdk/{tmp-fased-dir-Ouj58axY.d.ts → tmp-fased-dir-DQ00xQ5D.d.ts} +1 -1
  122. package/dist/plugin-sdk/{types-B4hj6HgR.d.ts → types-B6zx-Dl2.d.ts} +1 -1
  123. package/dist/plugin-sdk/{types-dWZFX1fk.d.ts → types-eUM7hcDz.d.ts} +1 -1
  124. package/dist/plugin-sdk/{web-vR-oNiBv.js → web-Q1-G--JH.js} +30 -30
  125. package/dist/plugin-sdk/{whatsapp-actions-BYNf8ceY.js → whatsapp-actions-BtSc2CBv.js} +13 -13
  126. package/dist/plugin-sdk/{whatsapp-heartbeat-DZwa8MF0.js → whatsapp-heartbeat-Bg9TuYzl.js} +2 -2
  127. package/dist/plugin-sdk/whatsapp.d.ts +3 -3
  128. package/dist/plugin-sdk/whatsapp.js +12 -12
  129. package/dist/plugin-sdk/{wsl-WGtdw_6h.js → wsl-C4DGFkOd.js} +3 -3
  130. package/dist/plugins/provider-runtime.js +79 -0
  131. package/dist/providers/refresh.js +14 -0
  132. package/extensions/acpx/package.json +1 -1
  133. package/extensions/bluebubbles/package.json +1 -1
  134. package/extensions/copilot-proxy/package.json +1 -1
  135. package/extensions/diagnostics-otel/package.json +1 -1
  136. package/extensions/fased-federation/package.json +1 -1
  137. package/extensions/google-gemini-cli-auth/package.json +1 -1
  138. package/extensions/imessage/package.json +1 -1
  139. package/extensions/irc/package.json +1 -1
  140. package/extensions/line/package.json +1 -1
  141. package/extensions/llm-task/package.json +1 -1
  142. package/extensions/lobster/package.json +1 -1
  143. package/extensions/matrix/CHANGELOG.md +6 -0
  144. package/extensions/matrix/package.json +1 -1
  145. package/extensions/mattermost/package.json +1 -1
  146. package/extensions/memory-core/package.json +2 -2
  147. package/extensions/memory-lancedb/package.json +1 -1
  148. package/extensions/minimax-portal-auth/package.json +1 -1
  149. package/extensions/msteams/CHANGELOG.md +6 -0
  150. package/extensions/msteams/package.json +1 -1
  151. package/extensions/nextcloud-talk/package.json +1 -1
  152. package/extensions/nostr/CHANGELOG.md +6 -0
  153. package/extensions/nostr/package.json +1 -1
  154. package/extensions/open-prose/package.json +1 -1
  155. package/extensions/sat-mining/package.json +1 -1
  156. package/extensions/signal/package.json +1 -1
  157. package/extensions/synology-chat/package.json +1 -1
  158. package/extensions/tlon/package.json +1 -1
  159. package/extensions/twitch/CHANGELOG.md +6 -0
  160. package/extensions/twitch/package.json +1 -1
  161. package/extensions/voice-call/CHANGELOG.md +6 -0
  162. package/extensions/voice-call/package.json +1 -1
  163. package/extensions/zalo/CHANGELOG.md +6 -0
  164. package/extensions/zalo/package.json +1 -1
  165. package/extensions/zalouser/CHANGELOG.md +6 -0
  166. package/extensions/zalouser/package.json +1 -1
  167. package/package.json +1 -1
@@ -34,4 +34,4 @@ function buildCopilotDynamicHeaders(params) {
34
34
  }
35
35
 
36
36
  //#endregion
37
- export { buildCopilotIdeHeaders };
37
+ export { buildCopilotDynamicHeaders, buildCopilotIdeHeaders, hasCopilotVisionInput };
@@ -0,0 +1,21 @@
1
+ import { getApiProvider, registerApiProvider } from "@mariozechner/pi-ai/compat";
2
+
3
+ //#region src/agents/custom-api-registry.ts
4
+ const CUSTOM_API_SOURCE_PREFIX = "fased-custom-api:";
5
+ function getCustomApiRegistrySourceId(api) {
6
+ return `${CUSTOM_API_SOURCE_PREFIX}${api}`;
7
+ }
8
+ function ensureCustomApiRegistered(api, streamFn) {
9
+ if (getApiProvider(api)) {
10
+ return false;
11
+ }
12
+ registerApiProvider({
13
+ api,
14
+ stream: (model, context, options) => streamFn(model, context, options),
15
+ streamSimple: (model, context, options) => streamFn(model, context, options)
16
+ }, getCustomApiRegistrySourceId(api));
17
+ return true;
18
+ }
19
+
20
+ //#endregion
21
+ export { ensureCustomApiRegistered };
@@ -0,0 +1,543 @@
1
+ import { parseGeminiAuth } from "../infra/gemini-auth.js";
2
+ import { stripSystemPromptCacheBoundary } from "./system-prompt-cache-boundary.js";
3
+ import { buildGuardedModelFetch } from "./provider-transport-fetch.js";
4
+ import { transformTransportMessages } from "./transport-message-transform.js";
5
+ import { createEmptyTransportUsage, createWritableTransportEventStream, failTransportStream, finalizeTransportStream, mergeTransportHeaders, sanitizeTransportPayloadText } from "./transport-stream-shared.js";
6
+ import { normalizeGoogleApiBaseUrl } from "../infra/google-api-base-url.js";
7
+ import { calculateCost, getEnvApiKey } from "@mariozechner/pi-ai/compat";
8
+
9
+ //#region src/agents/google-transport-stream.ts
10
+ let toolCallCounter = 0;
11
+ function isGemini3ProModel(modelId) {
12
+ return /gemini-3(?:\.\d+)?-pro/.test(modelId.toLowerCase());
13
+ }
14
+ function isGemini3FlashModel(modelId) {
15
+ return /gemini-3(?:\.\d+)?-flash/.test(modelId.toLowerCase());
16
+ }
17
+ function requiresToolCallId(modelId) {
18
+ return modelId.startsWith("claude-") || modelId.startsWith("gpt-oss-");
19
+ }
20
+ function supportsMultimodalFunctionResponse(modelId) {
21
+ const match = modelId.toLowerCase().match(/^gemini(?:-live)?-(\d+)/);
22
+ if (!match) {
23
+ return true;
24
+ }
25
+ return Number.parseInt(match[1] ?? "", 10) >= 3;
26
+ }
27
+ function retainThoughtSignature(existing, incoming) {
28
+ if (typeof incoming === "string" && incoming.length > 0) {
29
+ return incoming;
30
+ }
31
+ return existing;
32
+ }
33
+ function mapToolChoice(choice) {
34
+ if (!choice) {
35
+ return undefined;
36
+ }
37
+ if (typeof choice === "object" && choice.type === "function") {
38
+ return {
39
+ mode: "ANY",
40
+ allowedFunctionNames: [choice.function.name]
41
+ };
42
+ }
43
+ switch (choice) {
44
+ case "none": return { mode: "NONE" };
45
+ case "any":
46
+ case "required": return { mode: "ANY" };
47
+ default: return { mode: "AUTO" };
48
+ }
49
+ }
50
+ function mapStopReasonString(reason) {
51
+ switch (reason) {
52
+ case "STOP": return "stop";
53
+ case "MAX_TOKENS": return "length";
54
+ default: return "error";
55
+ }
56
+ }
57
+ function normalizeToolCallId(id) {
58
+ return id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
59
+ }
60
+ function resolveGoogleModelPath(modelId) {
61
+ if (modelId.startsWith("models/") || modelId.startsWith("tunedModels/")) {
62
+ return modelId;
63
+ }
64
+ return `models/${modelId}`;
65
+ }
66
+ function buildGoogleRequestUrl(model) {
67
+ const baseUrl = normalizeGoogleApiBaseUrl(model.baseUrl);
68
+ return `${baseUrl}/${resolveGoogleModelPath(model.id)}:streamGenerateContent?alt=sse`;
69
+ }
70
+ function resolveThinkingLevel(level, modelId) {
71
+ if (isGemini3ProModel(modelId)) {
72
+ switch (level) {
73
+ case "minimal":
74
+ case "low": return "LOW";
75
+ case "medium":
76
+ case "high":
77
+ case "xhigh": return "HIGH";
78
+ }
79
+ }
80
+ switch (level) {
81
+ case "minimal": return "MINIMAL";
82
+ case "low": return "LOW";
83
+ case "medium": return "MEDIUM";
84
+ case "high":
85
+ case "xhigh": return "HIGH";
86
+ }
87
+ }
88
+ function getDisabledThinkingConfig(modelId) {
89
+ if (isGemini3ProModel(modelId)) {
90
+ return { thinkingLevel: "LOW" };
91
+ }
92
+ if (isGemini3FlashModel(modelId)) {
93
+ return { thinkingLevel: "MINIMAL" };
94
+ }
95
+ return { thinkingBudget: 0 };
96
+ }
97
+ function getGoogleThinkingBudget(modelId, effort, customBudgets) {
98
+ const normalizedEffort = effort === "xhigh" ? "high" : effort;
99
+ if (customBudgets?.[normalizedEffort] !== undefined) {
100
+ return customBudgets[normalizedEffort];
101
+ }
102
+ if (modelId.includes("2.5-pro")) {
103
+ return {
104
+ minimal: 128,
105
+ low: 2048,
106
+ medium: 8192,
107
+ high: 32768
108
+ }[normalizedEffort];
109
+ }
110
+ if (modelId.includes("2.5-flash")) {
111
+ return {
112
+ minimal: 128,
113
+ low: 2048,
114
+ medium: 8192,
115
+ high: 24576
116
+ }[normalizedEffort];
117
+ }
118
+ return undefined;
119
+ }
120
+ function resolveGoogleThinkingConfig(model, options) {
121
+ if (!model.reasoning) {
122
+ return undefined;
123
+ }
124
+ if (options?.thinking) {
125
+ if (!options.thinking.enabled) {
126
+ return getDisabledThinkingConfig(model.id);
127
+ }
128
+ const config = { includeThoughts: true };
129
+ if (options.thinking.level) {
130
+ config.thinkingLevel = options.thinking.level;
131
+ } else if (typeof options.thinking.budgetTokens === "number") {
132
+ config.thinkingBudget = options.thinking.budgetTokens;
133
+ }
134
+ return config;
135
+ }
136
+ if (!options?.reasoning) {
137
+ return getDisabledThinkingConfig(model.id);
138
+ }
139
+ if (isGemini3ProModel(model.id) || isGemini3FlashModel(model.id)) {
140
+ return {
141
+ includeThoughts: true,
142
+ thinkingLevel: resolveThinkingLevel(options.reasoning, model.id)
143
+ };
144
+ }
145
+ const budget = getGoogleThinkingBudget(model.id, options.reasoning, options.thinkingBudgets);
146
+ return {
147
+ includeThoughts: true,
148
+ ...typeof budget === "number" ? { thinkingBudget: budget } : {}
149
+ };
150
+ }
151
+ function convertGoogleMessages(model, context) {
152
+ const contents = [];
153
+ const transformedMessages = transformTransportMessages(context.messages, model, (id) => requiresToolCallId(model.id) ? normalizeToolCallId(id) : id);
154
+ for (const msg of transformedMessages) {
155
+ if (msg.role === "user") {
156
+ if (typeof msg.content === "string") {
157
+ contents.push({
158
+ role: "user",
159
+ parts: [{ text: sanitizeTransportPayloadText(msg.content) }]
160
+ });
161
+ continue;
162
+ }
163
+ const parts = msg.content.map((item) => item.type === "text" ? { text: sanitizeTransportPayloadText(item.text) } : { inlineData: {
164
+ mimeType: item.mimeType,
165
+ data: item.data
166
+ } }).filter((item) => model.input.includes("image") || !("inlineData" in item));
167
+ if (parts.length > 0) {
168
+ contents.push({
169
+ role: "user",
170
+ parts
171
+ });
172
+ }
173
+ continue;
174
+ }
175
+ if (msg.role === "assistant") {
176
+ const isSameProviderAndModel = msg.provider === model.provider && msg.model === model.id;
177
+ const parts = [];
178
+ for (const block of msg.content) {
179
+ if (block.type === "text") {
180
+ if (!block.text.trim()) {
181
+ continue;
182
+ }
183
+ parts.push({
184
+ text: sanitizeTransportPayloadText(block.text),
185
+ ...isSameProviderAndModel && block.textSignature ? { thoughtSignature: block.textSignature } : {}
186
+ });
187
+ continue;
188
+ }
189
+ if (block.type === "thinking") {
190
+ if (!block.thinking.trim()) {
191
+ continue;
192
+ }
193
+ if (isSameProviderAndModel) {
194
+ parts.push({
195
+ thought: true,
196
+ text: sanitizeTransportPayloadText(block.thinking),
197
+ ...block.thinkingSignature ? { thoughtSignature: block.thinkingSignature } : {}
198
+ });
199
+ } else {
200
+ parts.push({ text: sanitizeTransportPayloadText(block.thinking) });
201
+ }
202
+ continue;
203
+ }
204
+ if (block.type === "toolCall") {
205
+ parts.push({
206
+ functionCall: {
207
+ name: block.name,
208
+ args: block.arguments ?? {},
209
+ ...requiresToolCallId(model.id) ? { id: block.id } : {}
210
+ },
211
+ ...isSameProviderAndModel && block.thoughtSignature ? { thoughtSignature: block.thoughtSignature } : {}
212
+ });
213
+ }
214
+ }
215
+ if (parts.length > 0) {
216
+ contents.push({
217
+ role: "model",
218
+ parts
219
+ });
220
+ }
221
+ continue;
222
+ }
223
+ if (msg.role === "toolResult") {
224
+ const textResult = msg.content.filter((item) => item.type === "text").map((item) => item.text).join("\n");
225
+ const imageContent = model.input.includes("image") ? msg.content.filter((item) => item.type === "image") : [];
226
+ const responseValue = textResult ? sanitizeTransportPayloadText(textResult) : imageContent.length > 0 ? "(see attached image)" : "";
227
+ const imageParts = imageContent.map((imageBlock) => ({ inlineData: {
228
+ mimeType: imageBlock.mimeType,
229
+ data: imageBlock.data
230
+ } }));
231
+ const functionResponse = { functionResponse: {
232
+ name: msg.toolName,
233
+ response: msg.isError ? { error: responseValue } : { output: responseValue },
234
+ ...supportsMultimodalFunctionResponse(model.id) && imageParts.length > 0 ? { parts: imageParts } : {},
235
+ ...requiresToolCallId(model.id) ? { id: msg.toolCallId } : {}
236
+ } };
237
+ const last = contents[contents.length - 1];
238
+ if (last?.role === "user" && Array.isArray(last.parts) && last.parts.some((part) => "functionResponse" in part)) {
239
+ last.parts.push(functionResponse);
240
+ } else {
241
+ contents.push({
242
+ role: "user",
243
+ parts: [functionResponse]
244
+ });
245
+ }
246
+ if (imageParts.length > 0 && !supportsMultimodalFunctionResponse(model.id)) {
247
+ contents.push({
248
+ role: "user",
249
+ parts: [{ text: "Tool result image:" }, ...imageParts]
250
+ });
251
+ }
252
+ }
253
+ }
254
+ return contents;
255
+ }
256
+ function convertGoogleTools(tools) {
257
+ if (tools.length === 0) {
258
+ return undefined;
259
+ }
260
+ return [{ functionDeclarations: tools.map((tool) => ({
261
+ name: tool.name,
262
+ description: tool.description,
263
+ parametersJsonSchema: tool.parameters
264
+ })) }];
265
+ }
266
+ function buildGoogleGenerativeAiParams(model, context, options) {
267
+ const generationConfig = {};
268
+ if (typeof options?.temperature === "number") {
269
+ generationConfig.temperature = options.temperature;
270
+ }
271
+ if (typeof options?.maxTokens === "number") {
272
+ generationConfig.maxOutputTokens = options.maxTokens;
273
+ }
274
+ const thinkingConfig = resolveGoogleThinkingConfig(model, options);
275
+ if (thinkingConfig) {
276
+ generationConfig.thinkingConfig = thinkingConfig;
277
+ }
278
+ const params = { contents: convertGoogleMessages(model, context) };
279
+ if (typeof options?.cachedContent === "string" && options.cachedContent.trim()) {
280
+ params.cachedContent = options.cachedContent.trim();
281
+ }
282
+ if (Object.keys(generationConfig).length > 0) {
283
+ params.generationConfig = generationConfig;
284
+ }
285
+ if (context.systemPrompt) {
286
+ params.systemInstruction = { parts: [{ text: sanitizeTransportPayloadText(stripSystemPromptCacheBoundary(context.systemPrompt)) }] };
287
+ }
288
+ if (context.tools?.length) {
289
+ params.tools = convertGoogleTools(context.tools);
290
+ const toolChoice = mapToolChoice(options?.toolChoice);
291
+ if (toolChoice) {
292
+ params.toolConfig = { functionCallingConfig: toolChoice };
293
+ }
294
+ }
295
+ return params;
296
+ }
297
+ function buildGoogleHeaders(model, apiKey, optionHeaders) {
298
+ const authHeaders = apiKey ? parseGeminiAuth(apiKey).headers : undefined;
299
+ return mergeTransportHeaders({ accept: "text/event-stream" }, authHeaders, model.headers, optionHeaders) ?? { accept: "text/event-stream" };
300
+ }
301
+ async function* parseGoogleSseChunks(response, signal) {
302
+ if (!response.body) {
303
+ throw new Error("No response body");
304
+ }
305
+ const reader = response.body.getReader();
306
+ const decoder = new TextDecoder();
307
+ let buffer = "";
308
+ const abortHandler = () => {
309
+ void reader.cancel().catch(() => undefined);
310
+ };
311
+ signal?.addEventListener("abort", abortHandler);
312
+ try {
313
+ while (true) {
314
+ if (signal?.aborted) {
315
+ throw new Error("Request was aborted");
316
+ }
317
+ const { done, value } = await reader.read();
318
+ if (done) {
319
+ break;
320
+ }
321
+ buffer += decoder.decode(value, { stream: true }).replace(/\r/g, "");
322
+ let boundary = buffer.indexOf("\n\n");
323
+ while (boundary >= 0) {
324
+ const rawEvent = buffer.slice(0, boundary);
325
+ buffer = buffer.slice(boundary + 2);
326
+ boundary = buffer.indexOf("\n\n");
327
+ const data = rawEvent.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).join("\n");
328
+ if (!data || data === "[DONE]") {
329
+ continue;
330
+ }
331
+ yield JSON.parse(data);
332
+ }
333
+ }
334
+ } finally {
335
+ signal?.removeEventListener("abort", abortHandler);
336
+ }
337
+ }
338
+ function updateUsage(output, model, chunk) {
339
+ const usage = chunk.usageMetadata;
340
+ if (!usage) {
341
+ return;
342
+ }
343
+ const promptTokens = usage.promptTokenCount || 0;
344
+ const cacheRead = usage.cachedContentTokenCount || 0;
345
+ output.usage = {
346
+ input: Math.max(0, promptTokens - cacheRead),
347
+ output: (usage.candidatesTokenCount || 0) + (usage.thoughtsTokenCount || 0),
348
+ cacheRead,
349
+ cacheWrite: 0,
350
+ totalTokens: usage.totalTokenCount || 0,
351
+ cost: {
352
+ input: 0,
353
+ output: 0,
354
+ cacheRead: 0,
355
+ cacheWrite: 0,
356
+ total: 0
357
+ }
358
+ };
359
+ calculateCost(model, output.usage);
360
+ }
361
+ function pushTextBlockEnd(stream, output, blockIndex) {
362
+ const block = output.content[blockIndex];
363
+ if (!block) {
364
+ return;
365
+ }
366
+ if (block.type === "thinking") {
367
+ stream.push({
368
+ type: "thinking_end",
369
+ contentIndex: blockIndex,
370
+ content: block.thinking,
371
+ partial: output
372
+ });
373
+ return;
374
+ }
375
+ if (block.type === "text") {
376
+ stream.push({
377
+ type: "text_end",
378
+ contentIndex: blockIndex,
379
+ content: block.text,
380
+ partial: output
381
+ });
382
+ }
383
+ }
384
+ function createGoogleGenerativeAiTransportStreamFn() {
385
+ return (rawModel, context, rawOptions) => {
386
+ const model = rawModel;
387
+ const options = rawOptions;
388
+ const { eventStream, stream } = createWritableTransportEventStream();
389
+ void (async () => {
390
+ const output = {
391
+ role: "assistant",
392
+ content: [],
393
+ api: "google-generative-ai",
394
+ provider: model.provider,
395
+ model: model.id,
396
+ usage: createEmptyTransportUsage(),
397
+ stopReason: "stop",
398
+ timestamp: Date.now()
399
+ };
400
+ try {
401
+ const apiKey = options?.apiKey ?? getEnvApiKey(model.provider) ?? undefined;
402
+ const fetch = buildGuardedModelFetch(model);
403
+ const params = buildGoogleGenerativeAiParams(model, context, options);
404
+ await Promise.resolve(options?.onPayload?.(params));
405
+ const response = await fetch(buildGoogleRequestUrl(model), {
406
+ method: "POST",
407
+ headers: buildGoogleHeaders(model, apiKey, options?.headers),
408
+ body: JSON.stringify(params),
409
+ signal: options?.signal
410
+ });
411
+ if (!response.ok) {
412
+ const message = await response.text().catch(() => "");
413
+ throw new Error(`Google Generative AI API error (${response.status}): ${message}`);
414
+ }
415
+ stream.push({
416
+ type: "start",
417
+ partial: output
418
+ });
419
+ let currentBlockIndex = -1;
420
+ for await (const chunk of parseGoogleSseChunks(response, options?.signal)) {
421
+ output.responseId ||= chunk.responseId;
422
+ updateUsage(output, model, chunk);
423
+ const candidate = chunk.candidates?.[0];
424
+ if (candidate?.content?.parts) {
425
+ for (const part of candidate.content.parts) {
426
+ if (typeof part.text === "string") {
427
+ const isThinking = part.thought === true;
428
+ const currentBlock = output.content[currentBlockIndex];
429
+ if (currentBlockIndex < 0 || !currentBlock || isThinking && currentBlock.type !== "thinking" || !isThinking && currentBlock.type !== "text") {
430
+ if (currentBlockIndex >= 0) {
431
+ pushTextBlockEnd(stream, output, currentBlockIndex);
432
+ }
433
+ if (isThinking) {
434
+ output.content.push({
435
+ type: "thinking",
436
+ thinking: ""
437
+ });
438
+ currentBlockIndex = output.content.length - 1;
439
+ stream.push({
440
+ type: "thinking_start",
441
+ contentIndex: currentBlockIndex,
442
+ partial: output
443
+ });
444
+ } else {
445
+ output.content.push({
446
+ type: "text",
447
+ text: ""
448
+ });
449
+ currentBlockIndex = output.content.length - 1;
450
+ stream.push({
451
+ type: "text_start",
452
+ contentIndex: currentBlockIndex,
453
+ partial: output
454
+ });
455
+ }
456
+ }
457
+ const activeBlock = output.content[currentBlockIndex];
458
+ if (activeBlock?.type === "thinking") {
459
+ activeBlock.thinking += part.text;
460
+ activeBlock.thinkingSignature = retainThoughtSignature(activeBlock.thinkingSignature, part.thoughtSignature);
461
+ stream.push({
462
+ type: "thinking_delta",
463
+ contentIndex: currentBlockIndex,
464
+ delta: part.text,
465
+ partial: output
466
+ });
467
+ } else if (activeBlock?.type === "text") {
468
+ activeBlock.text += part.text;
469
+ activeBlock.textSignature = retainThoughtSignature(activeBlock.textSignature, part.thoughtSignature);
470
+ stream.push({
471
+ type: "text_delta",
472
+ contentIndex: currentBlockIndex,
473
+ delta: part.text,
474
+ partial: output
475
+ });
476
+ }
477
+ }
478
+ if (part.functionCall) {
479
+ if (currentBlockIndex >= 0) {
480
+ pushTextBlockEnd(stream, output, currentBlockIndex);
481
+ currentBlockIndex = -1;
482
+ }
483
+ const providedId = part.functionCall.id;
484
+ const isDuplicate = output.content.some((block) => block.type === "toolCall" && block.id === providedId);
485
+ const toolCallId = providedId && !isDuplicate ? providedId : `${part.functionCall.name || "tool"}_${Date.now()}_${++toolCallCounter}`;
486
+ const toolCall = {
487
+ type: "toolCall",
488
+ id: toolCallId,
489
+ name: part.functionCall.name || "",
490
+ arguments: part.functionCall.args ?? {}
491
+ };
492
+ output.content.push(toolCall);
493
+ const blockIndex = output.content.length - 1;
494
+ stream.push({
495
+ type: "toolcall_start",
496
+ contentIndex: blockIndex,
497
+ partial: output
498
+ });
499
+ stream.push({
500
+ type: "toolcall_delta",
501
+ contentIndex: blockIndex,
502
+ delta: JSON.stringify(toolCall.arguments),
503
+ partial: output
504
+ });
505
+ stream.push({
506
+ type: "toolcall_end",
507
+ contentIndex: blockIndex,
508
+ toolCall,
509
+ partial: output
510
+ });
511
+ }
512
+ }
513
+ }
514
+ if (typeof candidate?.finishReason === "string") {
515
+ output.stopReason = mapStopReasonString(candidate.finishReason);
516
+ if (output.content.some((block) => block.type === "toolCall")) {
517
+ output.stopReason = "toolUse";
518
+ }
519
+ }
520
+ }
521
+ if (currentBlockIndex >= 0) {
522
+ pushTextBlockEnd(stream, output, currentBlockIndex);
523
+ }
524
+ finalizeTransportStream({
525
+ stream,
526
+ output,
527
+ signal: options?.signal
528
+ });
529
+ } catch (error) {
530
+ failTransportStream({
531
+ stream,
532
+ output,
533
+ signal: options?.signal,
534
+ error
535
+ });
536
+ }
537
+ })();
538
+ return eventStream;
539
+ };
540
+ }
541
+
542
+ //#endregion
543
+ export { createGoogleGenerativeAiTransportStreamFn };
@@ -114,7 +114,8 @@ async function ensureFasedModelsJson(config, agentDirOverride) {
114
114
  }
115
115
  for (const [key, newEntry] of Object.entries(providers)) {
116
116
  const existing = existingProviders[key];
117
- if (existing) {
117
+ const explicitlyConfigured = Object.hasOwn(explicitProviders, key);
118
+ if (existing && !explicitlyConfigured) {
118
119
  const preserved = {};
119
120
  if (typeof existing.apiKey === "string" && existing.apiKey) {
120
121
  preserved.apiKey = existing.apiKey;
@@ -0,0 +1,49 @@
1
+ import { resolveProviderRequestCapabilities } from "./provider-attribution.js";
2
+
3
+ //#region src/agents/openai-completions-compat.ts
4
+ function isDefaultRouteProvider(provider, ...ids) {
5
+ return provider !== undefined && ids.includes(provider);
6
+ }
7
+ function resolveOpenAICompletionsCompatDefaults(input) {
8
+ const { endpointClass, knownProviderFamily, supportsNativeStreamingUsageCompat = false, usesExplicitProxyLikeEndpoint = false } = input;
9
+ const isDefaultRoute = endpointClass === "default";
10
+ const usesConfiguredNonOpenAIEndpoint = endpointClass !== "default" && endpointClass !== "openai-public";
11
+ const isMoonshotLike = knownProviderFamily === "moonshot" || knownProviderFamily === "modelstudio" || endpointClass === "moonshot-native" || endpointClass === "modelstudio-native";
12
+ const isZai = endpointClass === "zai-native" || isDefaultRoute && isDefaultRouteProvider(input.provider, "zai");
13
+ const isNonStandard = endpointClass === "chutes-native" || endpointClass === "mistral-public" || endpointClass === "opencode-native" || endpointClass === "xai-native" || isZai || isDefaultRoute && isDefaultRouteProvider(input.provider, "chutes", "opencode", "xai");
14
+ const isOpenRouterLike = input.provider === "openrouter" || endpointClass === "openrouter";
15
+ const usesMaxTokens = endpointClass === "chutes-native" || endpointClass === "mistral-public" || knownProviderFamily === "mistral" || isDefaultRoute && isDefaultRouteProvider(input.provider, "chutes");
16
+ return {
17
+ supportsStore: !isNonStandard && knownProviderFamily !== "mistral" && !usesExplicitProxyLikeEndpoint,
18
+ supportsDeveloperRole: !isNonStandard && !isMoonshotLike && !usesConfiguredNonOpenAIEndpoint,
19
+ supportsReasoningEffort: !isZai && endpointClass !== "xai-native" && !usesExplicitProxyLikeEndpoint,
20
+ supportsUsageInStreaming: !isNonStandard && (!usesConfiguredNonOpenAIEndpoint || supportsNativeStreamingUsageCompat),
21
+ maxTokensField: usesMaxTokens ? "max_tokens" : "max_completion_tokens",
22
+ thinkingFormat: isZai ? "zai" : isOpenRouterLike ? "openrouter" : "openai",
23
+ supportsStrictMode: !isZai && !usesConfiguredNonOpenAIEndpoint
24
+ };
25
+ }
26
+ function resolveOpenAICompletionsCompatDefaultsFromCapabilities(input) {
27
+ return resolveOpenAICompletionsCompatDefaults(input);
28
+ }
29
+ function detectOpenAICompletionsCompat(model) {
30
+ const capabilities = resolveProviderRequestCapabilities({
31
+ provider: model.provider,
32
+ api: "openai-completions",
33
+ baseUrl: model.baseUrl,
34
+ capability: "llm",
35
+ transport: "stream",
36
+ modelId: model.id,
37
+ compat: model.compat && typeof model.compat === "object" ? model.compat : undefined
38
+ });
39
+ return {
40
+ capabilities,
41
+ defaults: resolveOpenAICompletionsCompatDefaultsFromCapabilities({
42
+ provider: model.provider,
43
+ ...capabilities
44
+ })
45
+ };
46
+ }
47
+
48
+ //#endregion
49
+ export { detectOpenAICompletionsCompat };