@copilotkit/runtime 1.77.2 → 1.79.0

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 (30) hide show
  1. package/README.md +16 -0
  2. package/dist/agent/index.cjs +84 -14
  3. package/dist/agent/index.cjs.map +1 -1
  4. package/dist/agent/index.d.cts +4 -3
  5. package/dist/agent/index.d.cts.map +1 -1
  6. package/dist/agent/index.d.mts +4 -3
  7. package/dist/agent/index.d.mts.map +1 -1
  8. package/dist/agent/index.mjs +84 -14
  9. package/dist/agent/index.mjs.map +1 -1
  10. package/dist/package.cjs +1 -1
  11. package/dist/package.mjs +1 -1
  12. package/dist/service-adapters/google/google-genai-adapter.cjs +2 -2
  13. package/dist/service-adapters/google/google-genai-adapter.cjs.map +1 -1
  14. package/dist/service-adapters/google/google-genai-adapter.mjs +2 -2
  15. package/dist/service-adapters/google/google-genai-adapter.mjs.map +1 -1
  16. package/dist/v2/runtime/core/package-info.cjs +1 -1
  17. package/dist/v2/runtime/core/package-info.mjs +1 -1
  18. package/dist/v2/runtime/intelligence-platform/client.cjs +37 -7
  19. package/dist/v2/runtime/intelligence-platform/client.cjs.map +1 -1
  20. package/dist/v2/runtime/intelligence-platform/client.d.cts.map +1 -1
  21. package/dist/v2/runtime/intelligence-platform/client.d.mts.map +1 -1
  22. package/dist/v2/runtime/intelligence-platform/client.mjs +37 -7
  23. package/dist/v2/runtime/intelligence-platform/client.mjs.map +1 -1
  24. package/dist/v2/runtime/runner/intelligence.cjs +1 -1
  25. package/dist/v2/runtime/runner/intelligence.cjs.map +1 -1
  26. package/dist/v2/runtime/runner/intelligence.d.cts.map +1 -1
  27. package/dist/v2/runtime/runner/intelligence.d.mts.map +1 -1
  28. package/dist/v2/runtime/runner/intelligence.mjs +1 -1
  29. package/dist/v2/runtime/runner/intelligence.mjs.map +1 -1
  30. package/package.json +5 -5
package/README.md CHANGED
@@ -192,3 +192,19 @@ See [Skill delivery](https://docs.copilotkit.ai/intelligence/learned-skills) for
192
192
  Explicit `containers` accepts 1–50 unique container IDs and sends one batch request for all sources that need a refresh. This also applies to a list with one entry.
193
193
  The server must support `POST /api/v1/learning/skills/batch` before you use this configuration. The SDK does not fall back to separate requests.
194
194
  Legacy `containerId` configuration keeps its existing single-container request. Both interfaces use the same authentication configuration.
195
+
196
+ ## Intelligence runner reconnects
197
+
198
+ `IntelligenceAgentRunner` retains unacknowledged events and retries them with the
199
+ same IDs after reconnect. An unexpected normal WebSocket close (`1000`) also
200
+ reconnects while the run remains active. Intentional cleanup does not reconnect.
201
+
202
+ Recovery also depends on the Intelligence gateway's reconnect grace period.
203
+ The current gateway defaults to 15 seconds and stores pending disconnects in
204
+ Redis so another gateway replica can finish cleanup. The gateway controls this
205
+ period; the runner does not negotiate it. A gateway with no reconnect grace can
206
+ record `RUNNER_CONNECTION_DROPPED` before the runner reconnects.
207
+
208
+ This recovers a lost WebSocket while the agent runtime remains alive. It does
209
+ not restart an agent after its runtime process exits. The existing 60-second
210
+ event acknowledgment deadline still applies.
@@ -86,20 +86,88 @@ function toolResultOutput(content) {
86
86
  }] : [], ...media]
87
87
  };
88
88
  }
89
+ /**
90
+ * Split a model string into provider and model id. The provider is the text
91
+ * before the FIRST ":" or "/"; everything after it is the model id, unchanged.
92
+ * So "openai:meta-llama/llama-3.3-70b" keeps the slash in its model id, and
93
+ * "openai/ft:gpt-4o-mini:org::id" keeps its colons. Empty strings mean the
94
+ * part is missing.
95
+ */
96
+ function parseModelSpec(spec) {
97
+ const trimmed = spec.trim();
98
+ const separator = trimmed.search(/[:/]/);
99
+ if (separator === -1) return {
100
+ provider: trimmed.toLowerCase(),
101
+ model: ""
102
+ };
103
+ return {
104
+ provider: trimmed.slice(0, separator).toLowerCase(),
105
+ model: trimmed.slice(separator + 1).trim()
106
+ };
107
+ }
108
+ /**
109
+ * Whether an `OPENAI_BASE_URL` should get Chat Completions instead of the
110
+ * Responses API. OpenAI's own hosts (including `*.api.openai.com`, such as the
111
+ * EU data-residency host) and Azure serve `/responses`, so they keep it, as
112
+ * does an unset value. Any other host gets `/chat/completions`, which is what
113
+ * most OpenAI-compatible servers implement. The host rule is
114
+ * `classifyModelHost`, the same one telemetry uses.
115
+ *
116
+ * A value `new URL` throws on keeps the Responses route; the request will
117
+ * fail on that base URL either way. A schemeless value such as
118
+ * `localhost:11434/v1` does not throw (it parses with scheme `localhost:` and
119
+ * an empty host), so it classifies as `other` and gets Chat Completions.
120
+ */
121
+ function usesChatCompletions(baseURL) {
122
+ const trimmed = baseURL?.trim();
123
+ if (!trimmed) return false;
124
+ try {
125
+ new URL(trimmed);
126
+ } catch {
127
+ return false;
128
+ }
129
+ const host = (0, _copilotkit_shared.classifyModelHost)(trimmed, "openai");
130
+ return host !== "openai" && host !== "azure";
131
+ }
132
+ /** Whether the Chat Completions notice below was already logged. */
133
+ let loggedChatCompletionsRoute = false;
134
+ /**
135
+ * Which OpenAI API an `openai/...` model string targets.
136
+ *
137
+ * `COPILOTKIT_OPENAI_API` (`responses` or `chat`) wins when set, so a proxy in
138
+ * front of OpenAI can keep the Responses API, and an OpenAI host can use Chat
139
+ * Completions. Unset or blank, `usesChatCompletions` decides from
140
+ * `OPENAI_BASE_URL`.
141
+ *
142
+ * When that rule picks Chat Completions, this logs once per process. Through a
143
+ * proxy, Chat Completions drops Responses-only features (such as reasoning
144
+ * summaries) without an error, and the notice names the way back.
145
+ */
146
+ function openAIApiFor(baseURL) {
147
+ const raw = process.env.COPILOTKIT_OPENAI_API;
148
+ const override = raw?.trim().toLowerCase();
149
+ if (override === "responses" || override === "chat") return override;
150
+ if (override) throw new Error(`Invalid COPILOTKIT_OPENAI_API "${raw}". Use "responses" or "chat", or leave it unset.`);
151
+ if (!usesChatCompletions(baseURL)) return "responses";
152
+ if (!loggedChatCompletionsRoute) {
153
+ loggedChatCompletionsRoute = true;
154
+ console.info("[CopilotKit] OPENAI_BASE_URL is not an OpenAI or Azure host, so \"openai/...\" models use the Chat Completions API. To use the Responses API instead, set COPILOTKIT_OPENAI_API=responses.");
155
+ }
156
+ return "chat";
157
+ }
89
158
  function resolveModel(spec, apiKey) {
90
159
  if (typeof spec !== "string") return spec;
91
- const parts = spec.replace("/", ":").trim().split(":");
92
- const rawProvider = parts[0];
93
- const rest = parts.slice(1);
94
- if (!rawProvider) throw new Error(`Invalid model string "${spec}". Use "openai/gpt-5", "anthropic/claude-sonnet-4.5", or "google/gemini-2.5-pro".`);
95
- const provider = rawProvider.toLowerCase();
96
- const model = rest.join(":").trim();
97
- if (!model) throw new Error(`Invalid model string "${spec}". Use "openai/gpt-5", "anthropic/claude-sonnet-4.5", or "google/gemini-2.5-pro".`);
160
+ const { provider, model } = parseModelSpec(spec);
161
+ if (!provider) throw new Error(`Invalid model string "${spec}". Use "openai/gpt-5", "anthropic/claude-sonnet-4.5", or "google/gemini-3.8-flash".`);
162
+ if (!model) throw new Error(`Invalid model string "${spec}". Use "openai/gpt-5", "anthropic/claude-sonnet-4.5", or "google/gemini-3.8-flash".`);
98
163
  switch (provider) {
99
- case "openai": return (0, _ai_sdk_openai.createOpenAI)({
100
- apiKey: apiKey || process.env.OPENAI_API_KEY,
101
- baseURL: process.env.OPENAI_BASE_URL
102
- })(model);
164
+ case "openai": {
165
+ const openai = (0, _ai_sdk_openai.createOpenAI)({
166
+ apiKey: apiKey || process.env.OPENAI_API_KEY,
167
+ baseURL: process.env.OPENAI_BASE_URL
168
+ });
169
+ return openAIApiFor(process.env.OPENAI_BASE_URL) === "chat" ? openai.chat(model) : openai(model);
170
+ }
103
171
  case "anthropic": return (0, _ai_sdk_anthropic.createAnthropic)({
104
172
  apiKey: apiKey || process.env.ANTHROPIC_API_KEY,
105
173
  baseURL: process.env.ANTHROPIC_BASE_URL
@@ -124,8 +192,9 @@ function resolveModel(spec, apiKey) {
124
192
  *
125
193
  * `resolveModel` above is the only place this runtime builds a provider, so it
126
194
  * is the only place that knows the endpoint. Once built, the endpoint is gone:
127
- * an AI SDK model reports `provider: "openai.responses"` whether it points at
128
- * api.openai.com, Azure, OpenRouter or a laptop, and its base URL survives
195
+ * an AI SDK model's `provider` names only the wire API (`"openai.responses"`
196
+ * on OpenAI and Azure, `"openai.chat"` on any other host), so it cannot tell
197
+ * OpenRouter from a laptop, and its base URL survives
129
198
  * only inside a closure that the public `LanguageModelV3` type does not
130
199
  * expose. Azure's own migration guide tells customers to use that same OpenAI
131
200
  * client, so the case we are blindest to is the common one.
@@ -140,7 +209,8 @@ function resolveModel(spec, apiKey) {
140
209
  */
141
210
  function classifyModelSpec(spec) {
142
211
  if (typeof spec !== "string") return "unknown";
143
- switch (spec.replace("/", ":").trim().split(":")[0]?.toLowerCase()) {
212
+ const { provider } = parseModelSpec(spec);
213
+ switch (provider) {
144
214
  case "openai": return (0, _copilotkit_shared.classifyModelHost)(process.env.OPENAI_BASE_URL, "openai");
145
215
  case "anthropic": return (0, _copilotkit_shared.classifyModelHost)(process.env.ANTHROPIC_BASE_URL, "anthropic");
146
216
  case "google":