@fased/fased 0.1.57 → 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 (172) 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/current-model-catalog.js +5 -2
  5. package/dist/agents/custom-api-registry.js +21 -0
  6. package/dist/agents/google-transport-stream.js +543 -0
  7. package/dist/agents/model-catalog-status.js +6 -1
  8. package/dist/agents/models-config.js +2 -1
  9. package/dist/agents/openai-completions-compat.js +49 -0
  10. package/dist/agents/openai-responses-payload-policy.js +100 -0
  11. package/dist/agents/openai-tool-schema.js +130 -0
  12. package/dist/agents/openai-transport-stream.js +1040 -0
  13. package/dist/agents/pi-embedded-runner/compact.js +9 -0
  14. package/dist/agents/pi-embedded-runner/run/attempt.js +9 -0
  15. package/dist/agents/pi-embedded-runner/session-manager-init.js +5 -20
  16. package/dist/agents/provider-extension-catalog-manifest.js +2 -2
  17. package/dist/agents/provider-request-config.js +1 -1
  18. package/dist/agents/provider-stream.js +29 -0
  19. package/dist/agents/provider-transport-fetch.js +89 -0
  20. package/dist/agents/provider-transport-stream.js +72 -0
  21. package/dist/agents/system-prompt-cache-boundary.js +1 -1
  22. package/dist/agents/system-prompt.js +1 -1
  23. package/dist/agents/transport-message-transform.js +143 -0
  24. package/dist/agents/transport-stream-shared.js +81 -0
  25. package/dist/brand.js +1 -1
  26. package/dist/build-info.json +3 -3
  27. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  28. package/dist/channels/delivery.js +78 -0
  29. package/dist/channels/plugins/catalog.js +10 -5
  30. package/dist/cli/components-cli.js +16 -13
  31. package/dist/cli/program/register.subclis.js +9 -0
  32. package/dist/cli/providers-cli.js +79 -6
  33. package/dist/cli/services-cli.js +28 -0
  34. package/dist/commands/auth-choice.js +1 -1
  35. package/dist/commands/models/list.auth-overview.js +12 -0
  36. package/dist/commands/onboard-channels.js +5 -0
  37. package/dist/commands/onboard-helpers.js +1 -1
  38. package/dist/commands/vllm-setup.js +12 -3
  39. package/dist/control-ui/assets/{app-DltSyY4Y.js → app-S_jg-ADS.js} +1706 -1675
  40. package/dist/control-ui/assets/{index-D5JlBp7f.js → index-C22rDOFm.js} +3 -3
  41. package/dist/control-ui/assets/{providers-Cc89iMdx.js → providers-QMn4w2_f.js} +3 -3
  42. package/dist/control-ui/index.html +1 -1
  43. package/dist/control-ui/version.json +1 -1
  44. package/dist/gateway/control-reply-text.js +10 -1
  45. package/dist/gateway/method-scopes.js +2 -0
  46. package/dist/gateway/protocol/schema/agents-models-skills.js +1 -0
  47. package/dist/gateway/server-chat.js +3 -3
  48. package/dist/gateway/server-methods/channels.js +10 -2
  49. package/dist/gateway/server-methods/chat.js +19 -12
  50. package/dist/gateway/server-methods/models.js +4 -0
  51. package/dist/gateway/server-methods/services.js +47 -2
  52. package/dist/gateway/server-methods-list.js +2 -0
  53. package/dist/gateway/session-event-payloads.js +20 -9
  54. package/dist/infra/google-api-base-url.js +29 -0
  55. package/dist/plugin-sdk/{accounts-CmMwe9Dt.js → accounts-DxblobYM.js} +1 -1
  56. package/dist/plugin-sdk/{actions-DYViFQiX.js → actions-D4UNimWa.js} +2 -2
  57. package/dist/plugin-sdk/{active-listener-DVbpcisf.js → active-listener-CFuy106s.js} +1 -1
  58. package/dist/plugin-sdk/{audio-preflight-D-Mfay8i.js → audio-preflight-CxZixyhW.js} +25 -25
  59. package/dist/plugin-sdk/{audit-JImYFOzJ.js → audit-Cs0K7LBp.js} +1 -1
  60. package/dist/plugin-sdk/{channel-access-cxCK2mci.js → channel-access-D5aHoTY8.js} +1 -1
  61. package/dist/plugin-sdk/{channel-access-configure-B_qYjml8.js → channel-access-configure-Dvch9mMQ.js} +1 -1
  62. package/dist/plugin-sdk/channel-plugin-common.d.ts +2 -2
  63. package/dist/plugin-sdk/channel-plugin-common.js +4 -4
  64. package/dist/plugin-sdk/{chunk-CF8nvZNj.js → chunk-B0FUGc5A.js} +1 -1
  65. package/dist/plugin-sdk/{chunk-items-yLFoO571.js → chunk-items-Cy9a3X7x.js} +1 -1
  66. package/dist/plugin-sdk/command-status.js +25 -25
  67. package/dist/plugin-sdk/{commands-registry-CK4KmMG0.js → commands-registry-DmDRhRAU.js} +2 -2
  68. package/dist/plugin-sdk/{common-Cky4hpqr.js → common-CfUOowOp.js} +1 -1
  69. package/dist/plugin-sdk/{config-Dbxj_jUP.js → config-mxULv0fQ.js} +4 -1
  70. package/dist/plugin-sdk/{config-schema-aKPMn6ML.js → config-schema-D8E0LX8X.js} +4422 -486
  71. package/dist/plugin-sdk/device-pair.d.ts +1 -1
  72. package/dist/plugin-sdk/discord.d.ts +4 -4
  73. package/dist/plugin-sdk/discord.js +19 -19
  74. package/dist/plugin-sdk/{handle-action-BSNDD2Vc.js → handle-action-DbVCF9HZ.js} +13 -13
  75. package/dist/plugin-sdk/{helpers-CDo2XBSr.js → helpers-BXbdgs5H.js} +1 -1
  76. package/dist/plugin-sdk/{image-ops-DbeBxbn4.js → image-ops-D7jPxaph.js} +1 -1
  77. package/dist/plugin-sdk/index.d.ts +15 -15
  78. package/dist/plugin-sdk/index.js +27 -27
  79. package/dist/plugin-sdk/{login-CWXvFTd8.js → login-BRxww6nv.js} +3 -3
  80. package/dist/plugin-sdk/{login-qr-CJB2zjmM.js → login-qr-DJXsk9XH.js} +3 -3
  81. package/dist/plugin-sdk/{manager-CbiFFLKS.js → manager-Y2S_5IlI.js} +2 -2
  82. package/dist/plugin-sdk/{markdown-tables-Bskt9r8i.js → markdown-tables-DHLFQqaF.js} +1 -1
  83. package/dist/plugin-sdk/{media-B3aAg5rh.js → media-Czo4carg.js} +2 -2
  84. package/dist/plugin-sdk/{monitor-BYGbTI5V.js → monitor-CFHFAUOj.js} +32 -32
  85. package/dist/plugin-sdk/{monitor-BJs6z7AT.js → monitor-xS9A8Upr.js} +29 -29
  86. package/dist/plugin-sdk/ollama-runtime.js +970 -0
  87. package/dist/plugin-sdk/{openresponses-http-BGCl2dS2.js → openresponses-http-BcUbeVV6.js} +25 -25
  88. package/dist/plugin-sdk/{optional-runtime-dependency-BlTVkcAg.js → optional-runtime-dependency-DdMbxS24.js} +1 -1
  89. package/dist/plugin-sdk/{outbound-CIemga5_.js → outbound-Db8mRN6P.js} +6 -6
  90. package/dist/plugin-sdk/{outbound-attachment-2RckWqrk.js → outbound-attachment-BqFjcypb.js} +2 -2
  91. package/dist/plugin-sdk/{pairing-message-r-VQfhFB.js → pairing-message-B3GC_7CD.js} +1 -1
  92. package/dist/plugin-sdk/{pairing-message-C403mq4K.d.ts → pairing-message-CxonJmwM.d.ts} +1 -1
  93. package/dist/plugin-sdk/{pairing-store-uPnLfGgB.js → pairing-store-CQikgIbw.js} +2 -2
  94. package/dist/plugin-sdk/{pi-model-discovery-BpEilAym.js → pi-model-discovery-CY3m5zJP.js} +1 -1
  95. package/dist/plugin-sdk/{pi-model-discovery-runtime-DwNdFaz-.js → pi-model-discovery-runtime-DSh1T2pn.js} +2 -2
  96. package/dist/plugin-sdk/provider-web-search-config-contract.d.ts +1 -1
  97. package/dist/plugin-sdk/{pw-ai-H7UgunQQ.js → pw-ai-AFUqn-tQ.js} +3 -3
  98. package/dist/plugin-sdk/{replies-CKMwkH4G.js → replies-Fzh9PhXl.js} +2 -2
  99. package/dist/plugin-sdk/{resolve-outbound-target-B68fSoGi.js → resolve-outbound-target-KpF3co6T.js} +1 -1
  100. package/dist/plugin-sdk/{resolve-route-B8fOfihG.js → resolve-route-CDl74O19.js} +1 -1
  101. package/dist/plugin-sdk/{retry-policy-B6GQldxB.js → retry-policy-D4uiqUOG.js} +1 -1
  102. package/dist/plugin-sdk/{runtime-nVJl3PlS.js → runtime-Bb6KRnCC.js} +6 -4
  103. package/dist/plugin-sdk/{runtime-dependency-DlqVSsSs.js → runtime-dependency-DLDmB94f.js} +11 -11
  104. package/dist/plugin-sdk/sat-runtime.d.ts +2 -2
  105. package/dist/plugin-sdk/sat-runtime.js +25 -25
  106. package/dist/plugin-sdk/{send-CPK_HlYW.js → send-5nFktR1n.js} +5 -5
  107. package/dist/plugin-sdk/{send-hSu1HWms.js → send-Bfzf11VY.js} +7 -7
  108. package/dist/plugin-sdk/{send-D3DQOg2a.js → send-C4k4JuJN.js} +6 -6
  109. package/dist/plugin-sdk/{send-BHokB4TR.js → send-D-uDKGIq.js} +6 -6
  110. package/dist/plugin-sdk/{send-BCJ4hi3t.js → send-Du8ykP1k.js} +8 -8
  111. package/dist/plugin-sdk/{session-DQ4Dr-mf.js → session-CS-Td9EM.js} +2 -2
  112. package/dist/plugin-sdk/{skill-commands-CmdoKLh_.js → skill-commands-CFAlXWHk.js} +2 -2
  113. package/dist/plugin-sdk/{slack-B70NHpMF.js → slack-DXYGbizH.js} +37 -37
  114. package/dist/plugin-sdk/{slack-actions-fSmP981b.js → slack-actions-BcMkNv1Z.js} +11 -11
  115. package/dist/plugin-sdk/slack.d.ts +4 -4
  116. package/dist/plugin-sdk/slack.js +9 -9
  117. package/dist/plugin-sdk/{sticker-cache-B4dG2sKU.js → sticker-cache-Dr3rYfU1.js} +3 -3
  118. package/dist/plugin-sdk/{tables-EX4Hy-a-.js → tables-bFXyRpkx.js} +1 -1
  119. package/dist/plugin-sdk/{targets-BeGUCGWK.js → targets-DH7a9lKB.js} +1 -1
  120. package/dist/plugin-sdk/{telegram-actions-CM8SJgc9.js → telegram-actions-Besbw_Hg.js} +28 -28
  121. package/dist/plugin-sdk/telegram.d.ts +4 -4
  122. package/dist/plugin-sdk/telegram.js +8 -8
  123. package/dist/plugin-sdk/{thread-bindings.lifecycle-tHK_1mvp.js → thread-bindings.lifecycle-CgOvvdw8.js} +3 -3
  124. package/dist/plugin-sdk/{thread-bindings.state-D7xG1GHu.js → thread-bindings.state-CQpk44Hq.js} +2 -2
  125. package/dist/plugin-sdk/{tmp-fased-dir-Ouj58axY.d.ts → tmp-fased-dir-DQ00xQ5D.d.ts} +1 -1
  126. package/dist/plugin-sdk/{types-B4hj6HgR.d.ts → types-B6zx-Dl2.d.ts} +1 -1
  127. package/dist/plugin-sdk/{types-dWZFX1fk.d.ts → types-eUM7hcDz.d.ts} +1 -1
  128. package/dist/plugin-sdk/{web-BFolfZ3i.js → web-Q1-G--JH.js} +30 -30
  129. package/dist/plugin-sdk/{whatsapp-actions-CYmleHCz.js → whatsapp-actions-BtSc2CBv.js} +13 -13
  130. package/dist/plugin-sdk/{whatsapp-heartbeat-BEwm4GK3.js → whatsapp-heartbeat-Bg9TuYzl.js} +2 -2
  131. package/dist/plugin-sdk/whatsapp.d.ts +9 -9
  132. package/dist/plugin-sdk/whatsapp.js +12 -12
  133. package/dist/plugin-sdk/{wsl-BtMDzMv5.js → wsl-C4DGFkOd.js} +3 -3
  134. package/dist/plugin-sdk/{zod-schema.providers-core-FaYR6KdD.d.ts → zod-schema.providers-core-CIzCjXH9.d.ts} +61 -61
  135. package/dist/plugins/provider-runtime.js +79 -0
  136. package/dist/providers/refresh.js +14 -0
  137. package/extensions/acpx/package.json +1 -1
  138. package/extensions/bluebubbles/package.json +1 -1
  139. package/extensions/copilot-proxy/package.json +1 -1
  140. package/extensions/diagnostics-otel/package.json +1 -1
  141. package/extensions/fased-federation/package.json +1 -1
  142. package/extensions/google-gemini-cli-auth/package.json +1 -1
  143. package/extensions/imessage/package.json +1 -1
  144. package/extensions/irc/package.json +1 -1
  145. package/extensions/line/package.json +1 -1
  146. package/extensions/llm-task/package.json +1 -1
  147. package/extensions/lobster/package.json +1 -1
  148. package/extensions/matrix/CHANGELOG.md +12 -0
  149. package/extensions/matrix/package.json +1 -1
  150. package/extensions/mattermost/package.json +1 -1
  151. package/extensions/memory-core/package.json +2 -2
  152. package/extensions/memory-lancedb/package.json +1 -1
  153. package/extensions/minimax-portal-auth/package.json +1 -1
  154. package/extensions/msteams/CHANGELOG.md +12 -0
  155. package/extensions/msteams/package.json +1 -1
  156. package/extensions/nextcloud-talk/package.json +1 -1
  157. package/extensions/nostr/CHANGELOG.md +12 -0
  158. package/extensions/nostr/package.json +1 -1
  159. package/extensions/open-prose/package.json +1 -1
  160. package/extensions/sat-mining/package.json +1 -1
  161. package/extensions/signal/package.json +1 -1
  162. package/extensions/synology-chat/package.json +1 -1
  163. package/extensions/tlon/package.json +1 -1
  164. package/extensions/twitch/CHANGELOG.md +12 -0
  165. package/extensions/twitch/package.json +1 -1
  166. package/extensions/voice-call/CHANGELOG.md +12 -0
  167. package/extensions/voice-call/package.json +1 -1
  168. package/extensions/zalo/CHANGELOG.md +12 -0
  169. package/extensions/zalo/package.json +1 -1
  170. package/extensions/zalouser/CHANGELOG.md +12 -0
  171. package/extensions/zalouser/package.json +1 -1
  172. 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 };
@@ -11,7 +11,7 @@ import { buildCopilotModelDefinition, buildCopilotProxyModelDefinition, getDefau
11
11
  import { CHUTES_BASE_URL, CHUTES_MODEL_CATALOG, buildChutesModelDefinition } from "./chutes-models.js";
12
12
  import { DOUBAO_BASE_URL, DOUBAO_CODING_BASE_URL, DOUBAO_CODING_MODEL_CATALOG, DOUBAO_MODEL_CATALOG, buildDoubaoModelDefinition } from "./doubao-models.js";
13
13
  import "./huggingface-models.js";
14
- import { normalizeProviderCatalogRows } from "./model-catalog-normalized.js";
14
+ import { normalizeModelCatalogProviderId, normalizeProviderCatalogRows } from "./model-catalog-normalized.js";
15
15
  import { getOpencodeZenStaticFallbackModels } from "./opencode-zen-models.js";
16
16
  import "./synthetic-models.js";
17
17
  import "./together-models.js";
@@ -945,6 +945,9 @@ function cloneCurrentModelProvider(provider) {
945
945
  }))
946
946
  };
947
947
  }
948
+ function listCurrentModelCatalogProviderIds() {
949
+ return Object.keys(CURRENT_MODEL_PROVIDER_CATALOG).map(normalizeModelCatalogProviderId).filter(Boolean).toSorted((left, right) => left.localeCompare(right));
950
+ }
948
951
  function listCurrentModelCatalogRows() {
949
952
  return Object.entries(CURRENT_MODEL_PROVIDER_CATALOG).flatMap(([provider, providerConfig]) => normalizeProviderCatalogRows({
950
953
  provider,
@@ -955,4 +958,4 @@ function listCurrentModelCatalogRows() {
955
958
  }
956
959
 
957
960
  //#endregion
958
- export { CURRENT_MODEL_PROVIDER_CATALOG, cloneCurrentModelProvider, listCurrentModelCatalogRows };
961
+ export { CURRENT_MODEL_PROVIDER_CATALOG, cloneCurrentModelProvider, listCurrentModelCatalogProviderIds, listCurrentModelCatalogRows };
@@ -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 };
@@ -1,4 +1,5 @@
1
1
  import { normalizeProviderId } from "./provider-id.js";
2
+ import { listCurrentModelCatalogProviderIds } from "./current-model-catalog.js";
2
3
  import { deriveModelMetadata } from "./model-metadata.js";
3
4
  import { listProviderExtensionCatalogManifestEntries } from "./provider-extension-catalog-manifest.js";
4
5
 
@@ -137,7 +138,11 @@ function buildModelCatalogStatus(params) {
137
138
  })).toSorted((left, right) => left.provider.localeCompare(right.provider));
138
139
  const manifestEntries = listProviderExtensionCatalogManifestEntries();
139
140
  const mappedManifestProviderIds = [...new Set(manifestEntries.flatMap((entry) => entry.status === "mapped" ? entry.fasedProviderIds.map(normalizeProviderId) : []))].filter(Boolean).toSorted((left, right) => left.localeCompare(right));
140
- const catalogProviderIds = new Set(providerRows.map((provider) => provider.provider));
141
+ const catalogProviderIds = new Set([
142
+ ...listCurrentModelCatalogProviderIds(),
143
+ ...providerRows.map((provider) => provider.provider),
144
+ ...Object.keys(params.providerExtensionCatalog?.providers ?? {}).map(normalizeProviderId)
145
+ ]);
141
146
  const deferredProviderIds = manifestEntries.filter((entry) => entry.status === "deferred").map((entry) => normalizeProviderId(entry.upstreamProviderId)).filter(Boolean).toSorted((left, right) => left.localeCompare(right));
142
147
  const providerExtensionEntries = (params.providerExtensionCatalog?.entries ?? []).map((entry) => ({
143
148
  id: entry.id,
@@ -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 };