@copilotkitnext/core 0.0.11 → 0.0.13-alpha.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.
package/dist/index.mjs CHANGED
@@ -1,12 +1,6 @@
1
- // src/core.ts
2
- import {
3
- DEFAULT_AGENT_ID,
4
- randomUUID,
5
- logger
6
- } from "@copilotkitnext/shared";
7
- import {
8
- HttpAgent as HttpAgent2
9
- } from "@ag-ui/client";
1
+ // src/core/agent-registry.ts
2
+ import { HttpAgent as HttpAgent2 } from "@ag-ui/client";
3
+ import { logger } from "@copilotkitnext/shared";
10
4
 
11
5
  // src/agent.ts
12
6
  import {
@@ -32,179 +26,133 @@ var ProxiedCopilotRuntimeAgent = class extends HttpAgent {
32
26
  }
33
27
  };
34
28
 
35
- // src/core.ts
36
- import { zodToJsonSchema } from "zod-to-json-schema";
37
- var CopilotKitCoreErrorCode = /* @__PURE__ */ ((CopilotKitCoreErrorCode2) => {
38
- CopilotKitCoreErrorCode2["RUNTIME_INFO_FETCH_FAILED"] = "runtime_info_fetch_failed";
39
- CopilotKitCoreErrorCode2["AGENT_CONNECT_FAILED"] = "agent_connect_failed";
40
- CopilotKitCoreErrorCode2["AGENT_RUN_FAILED"] = "agent_run_failed";
41
- CopilotKitCoreErrorCode2["AGENT_RUN_FAILED_EVENT"] = "agent_run_failed_event";
42
- CopilotKitCoreErrorCode2["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
43
- CopilotKitCoreErrorCode2["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
44
- CopilotKitCoreErrorCode2["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
45
- return CopilotKitCoreErrorCode2;
46
- })(CopilotKitCoreErrorCode || {});
47
- var CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ ((CopilotKitCoreRuntimeConnectionStatus2) => {
48
- CopilotKitCoreRuntimeConnectionStatus2["Disconnected"] = "disconnected";
49
- CopilotKitCoreRuntimeConnectionStatus2["Connected"] = "connected";
50
- CopilotKitCoreRuntimeConnectionStatus2["Connecting"] = "connecting";
51
- CopilotKitCoreRuntimeConnectionStatus2["Error"] = "error";
52
- return CopilotKitCoreRuntimeConnectionStatus2;
53
- })(CopilotKitCoreRuntimeConnectionStatus || {});
54
- var CopilotKitCore = class {
55
- headers;
56
- properties;
57
- _context = {};
29
+ // src/core/agent-registry.ts
30
+ var AgentRegistry = class {
31
+ constructor(core) {
32
+ this.core = core;
33
+ }
58
34
  _agents = {};
59
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
- _tools = [];
61
35
  localAgents = {};
62
36
  remoteAgents = {};
63
- subscribers = /* @__PURE__ */ new Set();
64
37
  _runtimeUrl;
65
38
  _runtimeVersion;
66
39
  _runtimeConnectionStatus = "disconnected" /* Disconnected */;
67
- constructor({
68
- runtimeUrl,
69
- headers = {},
70
- properties = {},
71
- agents = {},
72
- tools = []
73
- }) {
74
- this.headers = headers;
75
- this.properties = properties;
40
+ /**
41
+ * Get all agents as a readonly record
42
+ */
43
+ get agents() {
44
+ return this._agents;
45
+ }
46
+ get runtimeUrl() {
47
+ return this._runtimeUrl;
48
+ }
49
+ get runtimeVersion() {
50
+ return this._runtimeVersion;
51
+ }
52
+ get runtimeConnectionStatus() {
53
+ return this._runtimeConnectionStatus;
54
+ }
55
+ /**
56
+ * Initialize agents from configuration
57
+ */
58
+ initialize(agents) {
76
59
  this.localAgents = this.assignAgentIds(agents);
77
60
  this.applyHeadersToAgents(this.localAgents);
78
61
  this._agents = this.localAgents;
79
- this._tools = tools;
80
- this.setRuntimeUrl(runtimeUrl);
81
62
  }
82
- applyHeadersToAgent(agent) {
83
- if (agent instanceof HttpAgent2) {
84
- agent.headers = { ...this.headers };
63
+ /**
64
+ * Set the runtime URL and update connection
65
+ */
66
+ setRuntimeUrl(runtimeUrl) {
67
+ const normalizedRuntimeUrl = runtimeUrl ? runtimeUrl.replace(/\/$/, "") : void 0;
68
+ if (this._runtimeUrl === normalizedRuntimeUrl) {
69
+ return;
85
70
  }
71
+ this._runtimeUrl = normalizedRuntimeUrl;
72
+ void this.updateRuntimeConnection();
86
73
  }
87
- applyHeadersToAgents(agents) {
88
- Object.values(agents).forEach((agent) => {
89
- this.applyHeadersToAgent(agent);
90
- });
91
- }
92
- assignAgentIds(agents) {
74
+ /**
75
+ * Set all agents at once (for development use)
76
+ */
77
+ setAgents__unsafe_dev_only(agents) {
93
78
  Object.entries(agents).forEach(([id, agent]) => {
94
- if (agent && !agent.agentId) {
95
- agent.agentId = id;
79
+ if (agent) {
80
+ this.validateAndAssignAgentId(id, agent);
96
81
  }
97
82
  });
98
- return agents;
83
+ this.localAgents = agents;
84
+ this._agents = { ...this.localAgents, ...this.remoteAgents };
85
+ this.applyHeadersToAgents(this._agents);
86
+ void this.notifyAgentsChanged();
99
87
  }
100
- async notifySubscribers(handler, errorMessage) {
101
- await Promise.all(
102
- Array.from(this.subscribers).map(async (subscriber) => {
103
- try {
104
- await handler(subscriber);
105
- } catch (error) {
106
- logger.error(errorMessage, error);
107
- }
108
- })
109
- );
88
+ /**
89
+ * Add a single agent (for development use)
90
+ */
91
+ addAgent__unsafe_dev_only({ id, agent }) {
92
+ this.validateAndAssignAgentId(id, agent);
93
+ this.localAgents[id] = agent;
94
+ this.applyHeadersToAgent(agent);
95
+ this._agents = { ...this.localAgents, ...this.remoteAgents };
96
+ void this.notifyAgentsChanged();
110
97
  }
111
- async emitError({
112
- error,
113
- code,
114
- context = {}
115
- }) {
116
- await this.notifySubscribers(
117
- (subscriber) => subscriber.onError?.({
118
- copilotkit: this,
119
- error,
120
- code,
121
- context
122
- }),
123
- "Subscriber onError error:"
124
- );
98
+ /**
99
+ * Remove an agent by ID (for development use)
100
+ */
101
+ removeAgent__unsafe_dev_only(id) {
102
+ delete this.localAgents[id];
103
+ this._agents = { ...this.localAgents, ...this.remoteAgents };
104
+ void this.notifyAgentsChanged();
125
105
  }
126
- resolveAgentId(agent, providedAgentId) {
127
- if (providedAgentId) {
128
- return providedAgentId;
129
- }
130
- if (agent.agentId) {
131
- return agent.agentId;
106
+ /**
107
+ * Get an agent by ID
108
+ */
109
+ getAgent(id) {
110
+ if (id in this._agents) {
111
+ return this._agents[id];
132
112
  }
133
- const found = Object.entries(this._agents).find(([, storedAgent]) => {
134
- return storedAgent === agent;
135
- });
136
- if (found) {
137
- agent.agentId = found[0];
138
- return found[0];
113
+ if (this.runtimeUrl !== void 0 && (this.runtimeConnectionStatus === "disconnected" /* Disconnected */ || this.runtimeConnectionStatus === "connecting" /* Connecting */)) {
114
+ return void 0;
139
115
  }
140
- agent.agentId = DEFAULT_AGENT_ID;
141
- return DEFAULT_AGENT_ID;
116
+ console.warn(`Agent ${id} not found`);
117
+ return void 0;
142
118
  }
143
119
  /**
144
- * Snapshot accessors
120
+ * Apply current headers to an agent
145
121
  */
146
- get context() {
147
- return this._context;
148
- }
149
- get agents() {
150
- return this._agents;
151
- }
152
- get tools() {
153
- return this._tools;
154
- }
155
- get runtimeUrl() {
156
- return this._runtimeUrl;
157
- }
158
- setRuntimeUrl(runtimeUrl) {
159
- const normalizedRuntimeUrl = runtimeUrl ? runtimeUrl.replace(/\/$/, "") : void 0;
160
- if (this._runtimeUrl === normalizedRuntimeUrl) {
161
- return;
122
+ applyHeadersToAgent(agent) {
123
+ if (agent instanceof HttpAgent2) {
124
+ agent.headers = { ...this.core.headers };
162
125
  }
163
- this._runtimeUrl = normalizedRuntimeUrl;
164
- void this.updateRuntimeConnection();
165
126
  }
166
- get runtimeVersion() {
167
- return this._runtimeVersion;
168
- }
169
- get runtimeConnectionStatus() {
170
- return this._runtimeConnectionStatus;
127
+ /**
128
+ * Apply current headers to all agents
129
+ */
130
+ applyHeadersToAgents(agents) {
131
+ Object.values(agents).forEach((agent) => {
132
+ this.applyHeadersToAgent(agent);
133
+ });
171
134
  }
172
135
  /**
173
- * Runtime connection
136
+ * Update runtime connection and fetch remote agents
174
137
  */
175
138
  async updateRuntimeConnection() {
139
+ if (typeof window === "undefined") {
140
+ return;
141
+ }
176
142
  if (!this.runtimeUrl) {
177
143
  this._runtimeConnectionStatus = "disconnected" /* Disconnected */;
178
144
  this._runtimeVersion = void 0;
179
145
  this.remoteAgents = {};
180
146
  this._agents = this.localAgents;
181
- await this.notifySubscribers(
182
- (subscriber) => subscriber.onRuntimeConnectionStatusChanged?.({
183
- copilotkit: this,
184
- status: "disconnected" /* Disconnected */
185
- }),
186
- "Error in CopilotKitCore subscriber (onRuntimeConnectionStatusChanged):"
187
- );
188
- await this.notifySubscribers(
189
- (subscriber) => subscriber.onAgentsChanged?.({
190
- copilotkit: this,
191
- agents: this._agents
192
- }),
193
- "Subscriber onAgentsChanged error:"
194
- );
147
+ await this.notifyRuntimeStatusChanged("disconnected" /* Disconnected */);
148
+ await this.notifyAgentsChanged();
195
149
  return;
196
150
  }
197
151
  this._runtimeConnectionStatus = "connecting" /* Connecting */;
198
- await this.notifySubscribers(
199
- (subscriber) => subscriber.onRuntimeConnectionStatusChanged?.({
200
- copilotkit: this,
201
- status: "connecting" /* Connecting */
202
- }),
203
- "Error in CopilotKitCore subscriber (onRuntimeConnectionStatusChanged):"
204
- );
152
+ await this.notifyRuntimeStatusChanged("connecting" /* Connecting */);
205
153
  try {
206
154
  const response = await fetch(`${this.runtimeUrl}/info`, {
207
- headers: this.headers
155
+ headers: this.core.headers
208
156
  });
209
157
  const {
210
158
  version,
@@ -215,6 +163,7 @@ var CopilotKitCore = class {
215
163
  const agent = new ProxiedCopilotRuntimeAgent({
216
164
  runtimeUrl: this.runtimeUrl,
217
165
  agentId: id,
166
+ // Runtime agents always have their ID set correctly
218
167
  description
219
168
  });
220
169
  this.applyHeadersToAgent(agent);
@@ -225,45 +174,19 @@ var CopilotKitCore = class {
225
174
  this._agents = { ...this.localAgents, ...this.remoteAgents };
226
175
  this._runtimeConnectionStatus = "connected" /* Connected */;
227
176
  this._runtimeVersion = version;
228
- await this.notifySubscribers(
229
- (subscriber) => subscriber.onRuntimeConnectionStatusChanged?.({
230
- copilotkit: this,
231
- status: "connected" /* Connected */
232
- }),
233
- "Error in CopilotKitCore subscriber (onRuntimeConnectionStatusChanged):"
234
- );
235
- await this.notifySubscribers(
236
- (subscriber) => subscriber.onAgentsChanged?.({
237
- copilotkit: this,
238
- agents: this._agents
239
- }),
240
- "Subscriber onAgentsChanged error:"
241
- );
177
+ await this.notifyRuntimeStatusChanged("connected" /* Connected */);
178
+ await this.notifyAgentsChanged();
242
179
  } catch (error) {
243
180
  this._runtimeConnectionStatus = "error" /* Error */;
244
181
  this._runtimeVersion = void 0;
245
182
  this.remoteAgents = {};
246
183
  this._agents = this.localAgents;
247
- await this.notifySubscribers(
248
- (subscriber) => subscriber.onRuntimeConnectionStatusChanged?.({
249
- copilotkit: this,
250
- status: "error" /* Error */
251
- }),
252
- "Error in CopilotKitCore subscriber (onRuntimeConnectionStatusChanged):"
253
- );
254
- await this.notifySubscribers(
255
- (subscriber) => subscriber.onAgentsChanged?.({
256
- copilotkit: this,
257
- agents: this._agents
258
- }),
259
- "Subscriber onAgentsChanged error:"
260
- );
184
+ await this.notifyRuntimeStatusChanged("error" /* Error */);
185
+ await this.notifyAgentsChanged();
261
186
  const message = error instanceof Error ? error.message : JSON.stringify(error);
262
- logger.warn(
263
- `Failed to load runtime info (${this.runtimeUrl}/info): ${message}`
264
- );
187
+ logger.warn(`Failed to load runtime info (${this.runtimeUrl}/info): ${message}`);
265
188
  const runtimeError = error instanceof Error ? error : new Error(String(error));
266
- await this.emitError({
189
+ await this.core.emitError({
267
190
  error: runtimeError,
268
191
  code: "runtime_info_fetch_failed" /* RUNTIME_INFO_FETCH_FAILED */,
269
192
  context: {
@@ -273,118 +196,479 @@ var CopilotKitCore = class {
273
196
  }
274
197
  }
275
198
  /**
276
- * Configuration updates
199
+ * Assign agent IDs to a record of agents
277
200
  */
278
- setHeaders(headers) {
279
- this.headers = headers;
280
- this.applyHeadersToAgents(this._agents);
281
- void this.notifySubscribers(
282
- (subscriber) => subscriber.onHeadersChanged?.({
283
- copilotkit: this,
284
- headers: this.headers
285
- }),
286
- "Subscriber onHeadersChanged error:"
287
- );
288
- }
289
- setProperties(properties) {
290
- this.properties = properties;
291
- void this.notifySubscribers(
292
- (subscriber) => subscriber.onPropertiesChanged?.({
293
- copilotkit: this,
294
- properties: this.properties
295
- }),
296
- "Subscriber onPropertiesChanged error:"
297
- );
298
- }
299
- setAgents(agents) {
300
- this.localAgents = this.assignAgentIds(agents);
301
- this._agents = { ...this.localAgents, ...this.remoteAgents };
302
- this.applyHeadersToAgents(this._agents);
303
- void this.notifySubscribers(
304
- (subscriber) => subscriber.onAgentsChanged?.({
305
- copilotkit: this,
306
- agents: this._agents
307
- }),
308
- "Subscriber onAgentsChanged error:"
309
- );
201
+ assignAgentIds(agents) {
202
+ Object.entries(agents).forEach(([id, agent]) => {
203
+ if (agent) {
204
+ this.validateAndAssignAgentId(id, agent);
205
+ }
206
+ });
207
+ return agents;
310
208
  }
311
- addAgent({ id, agent }) {
312
- this.localAgents[id] = agent;
209
+ /**
210
+ * Validate and assign an agent ID
211
+ */
212
+ validateAndAssignAgentId(registrationId, agent) {
213
+ if (agent.agentId && agent.agentId !== registrationId) {
214
+ throw new Error(
215
+ `Agent registration mismatch: Agent with ID "${agent.agentId}" cannot be registered under key "${registrationId}". The agent ID must match the registration key or be undefined.`
216
+ );
217
+ }
313
218
  if (!agent.agentId) {
314
- agent.agentId = id;
219
+ agent.agentId = registrationId;
315
220
  }
316
- this.applyHeadersToAgent(agent);
317
- this._agents = { ...this.localAgents, ...this.remoteAgents };
318
- void this.notifySubscribers(
319
- (subscriber) => subscriber.onAgentsChanged?.({
320
- copilotkit: this,
321
- agents: this._agents
221
+ }
222
+ /**
223
+ * Notify subscribers of runtime status changes
224
+ */
225
+ async notifyRuntimeStatusChanged(status) {
226
+ await this.core.notifySubscribers(
227
+ (subscriber) => subscriber.onRuntimeConnectionStatusChanged?.({
228
+ copilotkit: this.core,
229
+ status
322
230
  }),
323
- "Subscriber onAgentsChanged error:"
231
+ "Error in CopilotKitCore subscriber (onRuntimeConnectionStatusChanged):"
324
232
  );
325
233
  }
326
- removeAgent(id) {
327
- delete this.localAgents[id];
328
- this._agents = { ...this.localAgents, ...this.remoteAgents };
329
- void this.notifySubscribers(
234
+ /**
235
+ * Notify subscribers of agent changes
236
+ */
237
+ async notifyAgentsChanged() {
238
+ await this.core.notifySubscribers(
330
239
  (subscriber) => subscriber.onAgentsChanged?.({
331
- copilotkit: this,
240
+ copilotkit: this.core,
332
241
  agents: this._agents
333
242
  }),
334
243
  "Subscriber onAgentsChanged error:"
335
244
  );
336
245
  }
337
- getAgent(id) {
338
- if (id in this._agents) {
339
- return this._agents[id];
340
- }
341
- if (this.runtimeUrl !== void 0 && (this.runtimeConnectionStatus === "disconnected" /* Disconnected */ || this.runtimeConnectionStatus === "connecting" /* Connecting */)) {
342
- return void 0;
343
- } else {
344
- console.warn(`Agent ${id} not found`);
345
- return void 0;
346
- }
246
+ };
247
+
248
+ // src/core/context-store.ts
249
+ import { randomUUID } from "@copilotkitnext/shared";
250
+ var ContextStore = class {
251
+ constructor(core) {
252
+ this.core = core;
253
+ }
254
+ _context = {};
255
+ /**
256
+ * Get all context entries as a readonly record
257
+ */
258
+ get context() {
259
+ return this._context;
347
260
  }
348
261
  /**
349
- * Context management
262
+ * Add a new context entry
263
+ * @returns The ID of the created context entry
350
264
  */
351
265
  addContext({ description, value }) {
352
266
  const id = randomUUID();
353
267
  this._context[id] = { description, value };
354
- void this.notifySubscribers(
355
- (subscriber) => subscriber.onContextChanged?.({
356
- copilotkit: this,
357
- context: this._context
358
- }),
359
- "Subscriber onContextChanged error:"
360
- );
268
+ void this.notifySubscribers();
361
269
  return id;
362
270
  }
271
+ /**
272
+ * Remove a context entry by ID
273
+ */
363
274
  removeContext(id) {
364
275
  delete this._context[id];
365
- void this.notifySubscribers(
276
+ void this.notifySubscribers();
277
+ }
278
+ /**
279
+ * Notify all subscribers of context changes
280
+ */
281
+ async notifySubscribers() {
282
+ await this.core.notifySubscribers(
366
283
  (subscriber) => subscriber.onContextChanged?.({
367
- copilotkit: this,
284
+ copilotkit: this.core,
368
285
  context: this._context
369
286
  }),
370
287
  "Subscriber onContextChanged error:"
371
288
  );
372
289
  }
290
+ };
291
+
292
+ // src/core/suggestion-engine.ts
293
+ import { randomUUID as randomUUID2, partialJSONParse } from "@copilotkitnext/shared";
294
+ var SuggestionEngine = class {
295
+ constructor(core) {
296
+ this.core = core;
297
+ }
298
+ _suggestionsConfig = {};
299
+ _suggestions = {};
300
+ _runningSuggestions = {};
301
+ /**
302
+ * Initialize with suggestion configs
303
+ */
304
+ initialize(suggestionsConfig) {
305
+ for (const config of suggestionsConfig) {
306
+ this._suggestionsConfig[randomUUID2()] = config;
307
+ }
308
+ }
309
+ /**
310
+ * Add a suggestion configuration
311
+ * @returns The ID of the created config
312
+ */
313
+ addSuggestionsConfig(config) {
314
+ const id = randomUUID2();
315
+ this._suggestionsConfig[id] = config;
316
+ void this.notifySuggestionsConfigChanged();
317
+ return id;
318
+ }
319
+ /**
320
+ * Remove a suggestion configuration by ID
321
+ */
322
+ removeSuggestionsConfig(id) {
323
+ delete this._suggestionsConfig[id];
324
+ void this.notifySuggestionsConfigChanged();
325
+ }
326
+ /**
327
+ * Reload suggestions for a specific agent
328
+ * This triggers generation of new suggestions based on current configs
329
+ */
330
+ reloadSuggestions(agentId) {
331
+ this.clearSuggestions(agentId);
332
+ const agent = this.core.getAgent(agentId);
333
+ if (!agent) {
334
+ return;
335
+ }
336
+ const messageCount = agent.messages?.length ?? 0;
337
+ let hasAnySuggestions = false;
338
+ for (const config of Object.values(this._suggestionsConfig)) {
339
+ if (config.consumerAgentId !== void 0 && config.consumerAgentId !== "*" && config.consumerAgentId !== agentId) {
340
+ continue;
341
+ }
342
+ if (!this.shouldShowSuggestions(config, messageCount)) {
343
+ continue;
344
+ }
345
+ const suggestionId = randomUUID2();
346
+ if (isDynamicSuggestionsConfig(config)) {
347
+ if (!hasAnySuggestions) {
348
+ hasAnySuggestions = true;
349
+ void this.notifySuggestionsStartedLoading(agentId);
350
+ }
351
+ void this.generateSuggestions(suggestionId, config, agentId);
352
+ } else if (isStaticSuggestionsConfig(config)) {
353
+ this.addStaticSuggestions(suggestionId, config, agentId);
354
+ }
355
+ }
356
+ }
357
+ /**
358
+ * Clear all suggestions for a specific agent
359
+ */
360
+ clearSuggestions(agentId) {
361
+ const runningAgents = this._runningSuggestions[agentId];
362
+ if (runningAgents) {
363
+ for (const agent of runningAgents) {
364
+ agent.abortRun();
365
+ }
366
+ delete this._runningSuggestions[agentId];
367
+ }
368
+ this._suggestions[agentId] = {};
369
+ void this.notifySuggestionsChanged(agentId, []);
370
+ }
371
+ /**
372
+ * Get current suggestions for an agent
373
+ */
374
+ getSuggestions(agentId) {
375
+ const suggestions = Object.values(this._suggestions[agentId] ?? {}).flat();
376
+ const isLoading = (this._runningSuggestions[agentId]?.length ?? 0) > 0;
377
+ return { suggestions, isLoading };
378
+ }
379
+ /**
380
+ * Generate suggestions using a provider agent
381
+ */
382
+ async generateSuggestions(suggestionId, config, consumerAgentId) {
383
+ let agent = void 0;
384
+ try {
385
+ const suggestionsProviderAgent = this.core.getAgent(config.providerAgentId ?? "default");
386
+ if (!suggestionsProviderAgent) {
387
+ throw new Error(`Suggestions provider agent not found: ${config.providerAgentId}`);
388
+ }
389
+ const suggestionsConsumerAgent = this.core.getAgent(consumerAgentId);
390
+ if (!suggestionsConsumerAgent) {
391
+ throw new Error(`Suggestions consumer agent not found: ${consumerAgentId}`);
392
+ }
393
+ const clonedAgent = suggestionsProviderAgent.clone();
394
+ agent = clonedAgent;
395
+ agent.agentId = suggestionId;
396
+ agent.threadId = suggestionId;
397
+ agent.messages = JSON.parse(JSON.stringify(suggestionsConsumerAgent.messages));
398
+ agent.state = JSON.parse(JSON.stringify(suggestionsConsumerAgent.state));
399
+ this._suggestions[consumerAgentId] = {
400
+ ...this._suggestions[consumerAgentId] ?? {},
401
+ [suggestionId]: []
402
+ };
403
+ this._runningSuggestions[consumerAgentId] = [...this._runningSuggestions[consumerAgentId] ?? [], agent];
404
+ agent.addMessage({
405
+ id: suggestionId,
406
+ role: "user",
407
+ content: [
408
+ `Suggest what the user could say next. Provide clear, highly relevant suggestions by calling the \`copilotkitSuggest\` tool.`,
409
+ `Provide at least ${config.minSuggestions ?? 1} and at most ${config.maxSuggestions ?? 3} suggestions.`,
410
+ `The user has the following tools available: ${JSON.stringify(this.core.buildFrontendTools(consumerAgentId))}.`,
411
+ ` ${config.instructions}`
412
+ ].join("\n")
413
+ });
414
+ await agent.runAgent(
415
+ {
416
+ context: Object.values(this.core.context),
417
+ forwardedProps: {
418
+ ...this.core.properties,
419
+ toolChoice: { type: "function", function: { name: "copilotkitSuggest" } }
420
+ },
421
+ tools: [SUGGEST_TOOL]
422
+ },
423
+ {
424
+ onMessagesChanged: ({ messages }) => {
425
+ this.extractSuggestions(messages, suggestionId, consumerAgentId, true);
426
+ }
427
+ }
428
+ );
429
+ } catch (error) {
430
+ console.warn("Error generating suggestions:", error);
431
+ } finally {
432
+ this.finalizeSuggestions(suggestionId, consumerAgentId);
433
+ const runningAgents = this._runningSuggestions[consumerAgentId];
434
+ if (agent && runningAgents) {
435
+ const filteredAgents = runningAgents.filter((a) => a !== agent);
436
+ this._runningSuggestions[consumerAgentId] = filteredAgents;
437
+ if (filteredAgents.length === 0) {
438
+ delete this._runningSuggestions[consumerAgentId];
439
+ await this.notifySuggestionsFinishedLoading(consumerAgentId);
440
+ }
441
+ }
442
+ }
443
+ }
444
+ /**
445
+ * Finalize suggestions by marking them as no longer loading
446
+ */
447
+ finalizeSuggestions(suggestionId, consumerAgentId) {
448
+ const agentSuggestions = this._suggestions[consumerAgentId];
449
+ const currentSuggestions = agentSuggestions?.[suggestionId];
450
+ if (agentSuggestions && currentSuggestions && currentSuggestions.length > 0) {
451
+ const finalizedSuggestions = currentSuggestions.filter((suggestion) => suggestion.title !== "" || suggestion.message !== "").map((suggestion) => ({
452
+ ...suggestion,
453
+ isLoading: false
454
+ }));
455
+ if (finalizedSuggestions.length > 0) {
456
+ agentSuggestions[suggestionId] = finalizedSuggestions;
457
+ } else {
458
+ delete agentSuggestions[suggestionId];
459
+ }
460
+ const allSuggestions = Object.values(this._suggestions[consumerAgentId] ?? {}).flat();
461
+ void this.notifySuggestionsChanged(consumerAgentId, allSuggestions, "finalized");
462
+ }
463
+ }
464
+ /**
465
+ * Extract suggestions from messages (called during streaming)
466
+ */
467
+ extractSuggestions(messages, suggestionId, consumerAgentId, isRunning) {
468
+ const idx = messages.findIndex((message) => message.id === suggestionId);
469
+ if (idx == -1) {
470
+ return;
471
+ }
472
+ const suggestions = [];
473
+ const newMessages = messages.slice(idx + 1);
474
+ for (const message of newMessages) {
475
+ if (message.role === "assistant" && message.toolCalls) {
476
+ for (const toolCall of message.toolCalls) {
477
+ if (toolCall.function.name === "copilotkitSuggest") {
478
+ const fullArgs = Array.isArray(toolCall.function.arguments) ? toolCall.function.arguments.join("") : toolCall.function.arguments;
479
+ const parsed = partialJSONParse(fullArgs);
480
+ if (parsed && typeof parsed === "object" && "suggestions" in parsed) {
481
+ const parsedSuggestions = parsed.suggestions;
482
+ if (Array.isArray(parsedSuggestions)) {
483
+ for (const item of parsedSuggestions) {
484
+ if (item && typeof item === "object" && "title" in item) {
485
+ suggestions.push({
486
+ title: item.title ?? "",
487
+ message: item.message ?? "",
488
+ isLoading: false
489
+ // Will be set correctly below
490
+ });
491
+ }
492
+ }
493
+ }
494
+ }
495
+ }
496
+ }
497
+ }
498
+ }
499
+ if (isRunning && suggestions.length > 0) {
500
+ suggestions[suggestions.length - 1].isLoading = true;
501
+ }
502
+ const agentSuggestions = this._suggestions[consumerAgentId];
503
+ if (agentSuggestions) {
504
+ agentSuggestions[suggestionId] = suggestions;
505
+ const allSuggestions = Object.values(this._suggestions[consumerAgentId] ?? {}).flat();
506
+ void this.notifySuggestionsChanged(consumerAgentId, allSuggestions, "suggestions changed");
507
+ }
508
+ }
509
+ /**
510
+ * Notify subscribers of suggestions config changes
511
+ */
512
+ async notifySuggestionsConfigChanged() {
513
+ await this.core.notifySubscribers(
514
+ (subscriber) => subscriber.onSuggestionsConfigChanged?.({
515
+ copilotkit: this.core,
516
+ suggestionsConfig: this._suggestionsConfig
517
+ }),
518
+ "Subscriber onSuggestionsConfigChanged error:"
519
+ );
520
+ }
521
+ /**
522
+ * Notify subscribers of suggestions changes
523
+ */
524
+ async notifySuggestionsChanged(agentId, suggestions, context = "") {
525
+ await this.core.notifySubscribers(
526
+ (subscriber) => subscriber.onSuggestionsChanged?.({
527
+ copilotkit: this.core,
528
+ agentId,
529
+ suggestions
530
+ }),
531
+ `Subscriber onSuggestionsChanged error: ${context}`
532
+ );
533
+ }
534
+ /**
535
+ * Notify subscribers that suggestions started loading
536
+ */
537
+ async notifySuggestionsStartedLoading(agentId) {
538
+ await this.core.notifySubscribers(
539
+ (subscriber) => subscriber.onSuggestionsStartedLoading?.({
540
+ copilotkit: this.core,
541
+ agentId
542
+ }),
543
+ "Subscriber onSuggestionsStartedLoading error:"
544
+ );
545
+ }
546
+ /**
547
+ * Notify subscribers that suggestions finished loading
548
+ */
549
+ async notifySuggestionsFinishedLoading(agentId) {
550
+ await this.core.notifySubscribers(
551
+ (subscriber) => subscriber.onSuggestionsFinishedLoading?.({
552
+ copilotkit: this.core,
553
+ agentId
554
+ }),
555
+ "Subscriber onSuggestionsFinishedLoading error:"
556
+ );
557
+ }
558
+ /**
559
+ * Check if suggestions should be shown based on availability and message count
560
+ */
561
+ shouldShowSuggestions(config, messageCount) {
562
+ const availability = config.available;
563
+ if (!availability) {
564
+ if (isDynamicSuggestionsConfig(config)) {
565
+ return messageCount > 0;
566
+ } else {
567
+ return messageCount === 0;
568
+ }
569
+ }
570
+ switch (availability) {
571
+ case "disabled":
572
+ return false;
573
+ case "before-first-message":
574
+ return messageCount === 0;
575
+ case "after-first-message":
576
+ return messageCount > 0;
577
+ case "always":
578
+ return true;
579
+ default:
580
+ return false;
581
+ }
582
+ }
583
+ /**
584
+ * Add static suggestions directly without AI generation
585
+ */
586
+ addStaticSuggestions(suggestionId, config, consumerAgentId) {
587
+ const suggestions = config.suggestions.map((s) => ({
588
+ ...s,
589
+ isLoading: false
590
+ }));
591
+ this._suggestions[consumerAgentId] = {
592
+ ...this._suggestions[consumerAgentId] ?? {},
593
+ [suggestionId]: suggestions
594
+ };
595
+ const allSuggestions = Object.values(this._suggestions[consumerAgentId] ?? {}).flat();
596
+ void this.notifySuggestionsChanged(consumerAgentId, allSuggestions, "static suggestions added");
597
+ }
598
+ };
599
+ function isDynamicSuggestionsConfig(config) {
600
+ return "instructions" in config;
601
+ }
602
+ function isStaticSuggestionsConfig(config) {
603
+ return "suggestions" in config;
604
+ }
605
+ var SUGGEST_TOOL = {
606
+ name: "copilotkitSuggest",
607
+ description: "Suggest what the user could say next",
608
+ parameters: {
609
+ type: "object",
610
+ properties: {
611
+ suggestions: {
612
+ type: "array",
613
+ description: "List of suggestions shown to the user as buttons.",
614
+ items: {
615
+ type: "object",
616
+ properties: {
617
+ title: {
618
+ type: "string",
619
+ description: "The title of the suggestion. This is shown as a button and should be short."
620
+ },
621
+ message: {
622
+ type: "string",
623
+ description: "The message to send when the suggestion is clicked. This should be a clear, complete sentence and will be sent as an instruction to the AI."
624
+ }
625
+ },
626
+ required: ["title", "message"],
627
+ additionalProperties: false
628
+ }
629
+ }
630
+ },
631
+ required: ["suggestions"],
632
+ additionalProperties: false
633
+ }
634
+ };
635
+
636
+ // src/core/run-handler.ts
637
+ import { HttpAgent as HttpAgent3 } from "@ag-ui/client";
638
+ import { randomUUID as randomUUID3, logger as logger2 } from "@copilotkitnext/shared";
639
+ import { zodToJsonSchema } from "zod-to-json-schema";
640
+ var RunHandler = class {
641
+ constructor(core) {
642
+ this.core = core;
643
+ }
644
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
645
+ _tools = [];
646
+ /**
647
+ * Get all tools as a readonly array
648
+ */
649
+ get tools() {
650
+ return this._tools;
651
+ }
652
+ /**
653
+ * Initialize with tools
654
+ */
655
+ initialize(tools) {
656
+ this._tools = tools;
657
+ }
373
658
  /**
374
- * Tool management
659
+ * Add a tool to the registry
375
660
  */
376
661
  addTool(tool) {
377
- const existingToolIndex = this._tools.findIndex(
378
- (t) => t.name === tool.name && t.agentId === tool.agentId
379
- );
662
+ const existingToolIndex = this._tools.findIndex((t) => t.name === tool.name && t.agentId === tool.agentId);
380
663
  if (existingToolIndex !== -1) {
381
- logger.warn(
382
- `Tool already exists: '${tool.name}' for agent '${tool.agentId || "global"}', skipping.`
383
- );
664
+ logger2.warn(`Tool already exists: '${tool.name}' for agent '${tool.agentId || "global"}', skipping.`);
384
665
  return;
385
666
  }
386
667
  this._tools.push(tool);
387
668
  }
669
+ /**
670
+ * Remove a tool by name and optionally by agentId
671
+ */
388
672
  removeTool(id, agentId) {
389
673
  this._tools = this._tools.filter((tool) => {
390
674
  if (agentId !== void 0) {
@@ -401,9 +685,7 @@ var CopilotKitCore = class {
401
685
  getTool(params) {
402
686
  const { toolName, agentId } = params;
403
687
  if (agentId) {
404
- const agentTool = this._tools.find(
405
- (tool) => tool.name === toolName && tool.agentId === agentId
406
- );
688
+ const agentTool = this._tools.find((tool) => tool.name === toolName && tool.agentId === agentId);
407
689
  if (agentTool) {
408
690
  return agentTool;
409
691
  }
@@ -417,43 +699,28 @@ var CopilotKitCore = class {
417
699
  this._tools = [...tools];
418
700
  }
419
701
  /**
420
- * Subscription lifecycle
421
- */
422
- subscribe(subscriber) {
423
- this.subscribers.add(subscriber);
424
- return () => {
425
- this.unsubscribe(subscriber);
426
- };
427
- }
428
- unsubscribe(subscriber) {
429
- this.subscribers.delete(subscriber);
430
- }
431
- /**
432
- * Agent connectivity
702
+ * Connect an agent (establish initial connection)
433
703
  */
434
- async connectAgent({
435
- agent,
436
- agentId
437
- }) {
704
+ async connectAgent({ agent }) {
438
705
  try {
439
- if (agent instanceof HttpAgent2) {
440
- agent.headers = { ...this.headers };
706
+ if (agent instanceof HttpAgent3) {
707
+ agent.headers = { ...this.core.headers };
441
708
  }
442
709
  const runAgentResult = await agent.connectAgent(
443
710
  {
444
- forwardedProps: this.properties,
445
- tools: this.buildFrontendTools(agentId)
711
+ forwardedProps: this.core.properties,
712
+ tools: this.buildFrontendTools(agent.agentId)
446
713
  },
447
- this.createAgentErrorSubscriber(agent, agentId)
714
+ this.createAgentErrorSubscriber(agent)
448
715
  );
449
- return this.processAgentResult({ runAgentResult, agent, agentId });
716
+ return this.processAgentResult({ runAgentResult, agent });
450
717
  } catch (error) {
451
718
  const connectError = error instanceof Error ? error : new Error(String(error));
452
719
  const context = {};
453
- if (agentId ?? agent.agentId) {
454
- context.agentId = agentId ?? agent.agentId;
720
+ if (agent.agentId) {
721
+ context.agentId = agent.agentId;
455
722
  }
456
- await this.emitError({
723
+ await this.core.emitError({
457
724
  error: connectError,
458
725
  code: "agent_connect_failed" /* AGENT_CONNECT_FAILED */,
459
726
  context
@@ -461,13 +728,13 @@ var CopilotKitCore = class {
461
728
  throw error;
462
729
  }
463
730
  }
464
- async runAgent({
465
- agent,
466
- withMessages,
467
- agentId
468
- }) {
469
- if (agent instanceof HttpAgent2) {
470
- agent.headers = { ...this.headers };
731
+ /**
732
+ * Run an agent
733
+ */
734
+ async runAgent({ agent, withMessages }) {
735
+ void this.core.suggestionEngine.clearSuggestions(agent.agentId);
736
+ if (agent instanceof HttpAgent3) {
737
+ agent.headers = { ...this.core.headers };
471
738
  }
472
739
  if (withMessages) {
473
740
  agent.addMessages(withMessages);
@@ -475,22 +742,22 @@ var CopilotKitCore = class {
475
742
  try {
476
743
  const runAgentResult = await agent.runAgent(
477
744
  {
478
- forwardedProps: this.properties,
479
- tools: this.buildFrontendTools(agentId)
745
+ forwardedProps: this.core.properties,
746
+ tools: this.buildFrontendTools(agent.agentId)
480
747
  },
481
- this.createAgentErrorSubscriber(agent, agentId)
748
+ this.createAgentErrorSubscriber(agent)
482
749
  );
483
- return this.processAgentResult({ runAgentResult, agent, agentId });
750
+ return this.processAgentResult({ runAgentResult, agent });
484
751
  } catch (error) {
485
752
  const runError = error instanceof Error ? error : new Error(String(error));
486
753
  const context = {};
487
- if (agentId ?? agent.agentId) {
488
- context.agentId = agentId ?? agent.agentId;
754
+ if (agent.agentId) {
755
+ context.agentId = agent.agentId;
489
756
  }
490
757
  if (withMessages) {
491
758
  context.messageCount = withMessages.length;
492
759
  }
493
- await this.emitError({
760
+ await this.core.emitError({
494
761
  error: runError,
495
762
  code: "agent_run_failed" /* AGENT_RUN_FAILED */,
496
763
  context
@@ -498,231 +765,35 @@ var CopilotKitCore = class {
498
765
  throw error;
499
766
  }
500
767
  }
768
+ /**
769
+ * Process agent result and execute tools
770
+ */
501
771
  async processAgentResult({
502
772
  runAgentResult,
503
- agent,
504
- agentId
773
+ agent
505
774
  }) {
506
775
  const { newMessages } = runAgentResult;
507
- const effectiveAgentId = this.resolveAgentId(agent, agentId);
776
+ const agentId = agent.agentId;
508
777
  let needsFollowUp = false;
509
778
  for (const message of newMessages) {
510
779
  if (message.role === "assistant") {
511
780
  for (const toolCall of message.toolCalls || []) {
512
- if (newMessages.findIndex(
513
- (m) => m.role === "tool" && m.toolCallId === toolCall.id
514
- ) === -1) {
781
+ if (newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id) === -1) {
515
782
  const tool = this.getTool({
516
783
  toolName: toolCall.function.name,
517
- agentId
784
+ agentId: agent.agentId
518
785
  });
519
786
  if (tool) {
520
- if (tool?.agentId && tool.agentId !== agentId) {
521
- continue;
522
- }
523
- let toolCallResult = "";
524
- let errorMessage;
525
- let isArgumentError = false;
526
- if (tool?.handler) {
527
- let parsedArgs;
528
- try {
529
- parsedArgs = JSON.parse(toolCall.function.arguments);
530
- } catch (error) {
531
- const parseError = error instanceof Error ? error : new Error(String(error));
532
- errorMessage = parseError.message;
533
- isArgumentError = true;
534
- await this.emitError({
535
- error: parseError,
536
- code: "tool_argument_parse_failed" /* TOOL_ARGUMENT_PARSE_FAILED */,
537
- context: {
538
- agentId: effectiveAgentId,
539
- toolCallId: toolCall.id,
540
- toolName: toolCall.function.name,
541
- rawArguments: toolCall.function.arguments,
542
- toolType: "specific",
543
- messageId: message.id
544
- }
545
- });
546
- }
547
- await this.notifySubscribers(
548
- (subscriber) => subscriber.onToolExecutionStart?.({
549
- copilotkit: this,
550
- toolCallId: toolCall.id,
551
- agentId: effectiveAgentId,
552
- toolName: toolCall.function.name,
553
- args: parsedArgs
554
- }),
555
- "Subscriber onToolExecutionStart error:"
556
- );
557
- if (!errorMessage) {
558
- try {
559
- const result = await tool.handler(
560
- parsedArgs,
561
- toolCall
562
- );
563
- if (result === void 0 || result === null) {
564
- toolCallResult = "";
565
- } else if (typeof result === "string") {
566
- toolCallResult = result;
567
- } else {
568
- toolCallResult = JSON.stringify(result);
569
- }
570
- } catch (error) {
571
- const handlerError = error instanceof Error ? error : new Error(String(error));
572
- errorMessage = handlerError.message;
573
- await this.emitError({
574
- error: handlerError,
575
- code: "tool_handler_failed" /* TOOL_HANDLER_FAILED */,
576
- context: {
577
- agentId: effectiveAgentId,
578
- toolCallId: toolCall.id,
579
- toolName: toolCall.function.name,
580
- parsedArgs,
581
- toolType: "specific",
582
- messageId: message.id
583
- }
584
- });
585
- }
586
- }
587
- if (errorMessage) {
588
- toolCallResult = `Error: ${errorMessage}`;
589
- }
590
- await this.notifySubscribers(
591
- (subscriber) => subscriber.onToolExecutionEnd?.({
592
- copilotkit: this,
593
- toolCallId: toolCall.id,
594
- agentId: effectiveAgentId,
595
- toolName: toolCall.function.name,
596
- result: errorMessage ? "" : toolCallResult,
597
- error: errorMessage
598
- }),
599
- "Subscriber onToolExecutionEnd error:"
600
- );
601
- if (isArgumentError) {
602
- throw new Error(errorMessage ?? "Tool execution failed");
603
- }
604
- }
605
- if (!errorMessage || !isArgumentError) {
606
- const messageIndex = agent.messages.findIndex(
607
- (m) => m.id === message.id
608
- );
609
- const toolMessage = {
610
- id: randomUUID(),
611
- role: "tool",
612
- toolCallId: toolCall.id,
613
- content: toolCallResult
614
- };
615
- agent.messages.splice(messageIndex + 1, 0, toolMessage);
616
- if (!errorMessage && tool?.followUp !== false) {
617
- needsFollowUp = true;
618
- }
787
+ const followUp = await this.executeSpecificTool(tool, toolCall, message, agent, agentId);
788
+ if (followUp) {
789
+ needsFollowUp = true;
619
790
  }
620
791
  } else {
621
- const wildcardTool = this.getTool({ toolName: "*", agentId });
792
+ const wildcardTool = this.getTool({ toolName: "*", agentId: agent.agentId });
622
793
  if (wildcardTool) {
623
- if (wildcardTool?.agentId && wildcardTool.agentId !== agentId) {
624
- continue;
625
- }
626
- let toolCallResult = "";
627
- let errorMessage;
628
- let isArgumentError = false;
629
- if (wildcardTool?.handler) {
630
- let parsedArgs;
631
- try {
632
- parsedArgs = JSON.parse(toolCall.function.arguments);
633
- } catch (error) {
634
- const parseError = error instanceof Error ? error : new Error(String(error));
635
- errorMessage = parseError.message;
636
- isArgumentError = true;
637
- await this.emitError({
638
- error: parseError,
639
- code: "tool_argument_parse_failed" /* TOOL_ARGUMENT_PARSE_FAILED */,
640
- context: {
641
- agentId: effectiveAgentId,
642
- toolCallId: toolCall.id,
643
- toolName: toolCall.function.name,
644
- rawArguments: toolCall.function.arguments,
645
- toolType: "wildcard",
646
- messageId: message.id
647
- }
648
- });
649
- }
650
- const wildcardArgs = {
651
- toolName: toolCall.function.name,
652
- args: parsedArgs
653
- };
654
- await this.notifySubscribers(
655
- (subscriber) => subscriber.onToolExecutionStart?.({
656
- copilotkit: this,
657
- toolCallId: toolCall.id,
658
- agentId: effectiveAgentId,
659
- toolName: toolCall.function.name,
660
- args: wildcardArgs
661
- }),
662
- "Subscriber onToolExecutionStart error:"
663
- );
664
- if (!errorMessage) {
665
- try {
666
- const result = await wildcardTool.handler(
667
- wildcardArgs,
668
- toolCall
669
- );
670
- if (result === void 0 || result === null) {
671
- toolCallResult = "";
672
- } else if (typeof result === "string") {
673
- toolCallResult = result;
674
- } else {
675
- toolCallResult = JSON.stringify(result);
676
- }
677
- } catch (error) {
678
- const handlerError = error instanceof Error ? error : new Error(String(error));
679
- errorMessage = handlerError.message;
680
- await this.emitError({
681
- error: handlerError,
682
- code: "tool_handler_failed" /* TOOL_HANDLER_FAILED */,
683
- context: {
684
- agentId: effectiveAgentId,
685
- toolCallId: toolCall.id,
686
- toolName: toolCall.function.name,
687
- parsedArgs: wildcardArgs,
688
- toolType: "wildcard",
689
- messageId: message.id
690
- }
691
- });
692
- }
693
- }
694
- if (errorMessage) {
695
- toolCallResult = `Error: ${errorMessage}`;
696
- }
697
- await this.notifySubscribers(
698
- (subscriber) => subscriber.onToolExecutionEnd?.({
699
- copilotkit: this,
700
- toolCallId: toolCall.id,
701
- agentId: effectiveAgentId,
702
- toolName: toolCall.function.name,
703
- result: errorMessage ? "" : toolCallResult,
704
- error: errorMessage
705
- }),
706
- "Subscriber onToolExecutionEnd error:"
707
- );
708
- if (isArgumentError) {
709
- throw new Error(errorMessage ?? "Tool execution failed");
710
- }
711
- }
712
- if (!errorMessage || !isArgumentError) {
713
- const messageIndex = agent.messages.findIndex(
714
- (m) => m.id === message.id
715
- );
716
- const toolMessage = {
717
- id: randomUUID(),
718
- role: "tool",
719
- toolCallId: toolCall.id,
720
- content: toolCallResult
721
- };
722
- agent.messages.splice(messageIndex + 1, 0, toolMessage);
723
- if (!errorMessage && wildcardTool?.followUp !== false) {
724
- needsFollowUp = true;
725
- }
794
+ const followUp = await this.executeWildcardTool(wildcardTool, toolCall, message, agent, agentId);
795
+ if (followUp) {
796
+ needsFollowUp = true;
726
797
  }
727
798
  }
728
799
  }
@@ -731,10 +802,220 @@ var CopilotKitCore = class {
731
802
  }
732
803
  }
733
804
  if (needsFollowUp) {
734
- return await this.runAgent({ agent, agentId });
805
+ return await this.runAgent({ agent });
735
806
  }
807
+ void this.core.suggestionEngine.reloadSuggestions(agentId);
736
808
  return runAgentResult;
737
809
  }
810
+ /**
811
+ * Execute a specific tool
812
+ */
813
+ async executeSpecificTool(tool, toolCall, message, agent, agentId) {
814
+ if (tool?.agentId && tool.agentId !== agent.agentId) {
815
+ return false;
816
+ }
817
+ let toolCallResult = "";
818
+ let errorMessage;
819
+ let isArgumentError = false;
820
+ if (tool?.handler) {
821
+ let parsedArgs;
822
+ try {
823
+ parsedArgs = JSON.parse(toolCall.function.arguments);
824
+ } catch (error) {
825
+ const parseError = error instanceof Error ? error : new Error(String(error));
826
+ errorMessage = parseError.message;
827
+ isArgumentError = true;
828
+ await this.core.emitError({
829
+ error: parseError,
830
+ code: "tool_argument_parse_failed" /* TOOL_ARGUMENT_PARSE_FAILED */,
831
+ context: {
832
+ agentId,
833
+ toolCallId: toolCall.id,
834
+ toolName: toolCall.function.name,
835
+ rawArguments: toolCall.function.arguments,
836
+ toolType: "specific",
837
+ messageId: message.id
838
+ }
839
+ });
840
+ }
841
+ await this.core.notifySubscribers(
842
+ (subscriber) => subscriber.onToolExecutionStart?.({
843
+ copilotkit: this.core,
844
+ toolCallId: toolCall.id,
845
+ agentId,
846
+ toolName: toolCall.function.name,
847
+ args: parsedArgs
848
+ }),
849
+ "Subscriber onToolExecutionStart error:"
850
+ );
851
+ if (!errorMessage) {
852
+ try {
853
+ const result = await tool.handler(parsedArgs, toolCall);
854
+ if (result === void 0 || result === null) {
855
+ toolCallResult = "";
856
+ } else if (typeof result === "string") {
857
+ toolCallResult = result;
858
+ } else {
859
+ toolCallResult = JSON.stringify(result);
860
+ }
861
+ } catch (error) {
862
+ const handlerError = error instanceof Error ? error : new Error(String(error));
863
+ errorMessage = handlerError.message;
864
+ await this.core.emitError({
865
+ error: handlerError,
866
+ code: "tool_handler_failed" /* TOOL_HANDLER_FAILED */,
867
+ context: {
868
+ agentId,
869
+ toolCallId: toolCall.id,
870
+ toolName: toolCall.function.name,
871
+ parsedArgs,
872
+ toolType: "specific",
873
+ messageId: message.id
874
+ }
875
+ });
876
+ }
877
+ }
878
+ if (errorMessage) {
879
+ toolCallResult = `Error: ${errorMessage}`;
880
+ }
881
+ await this.core.notifySubscribers(
882
+ (subscriber) => subscriber.onToolExecutionEnd?.({
883
+ copilotkit: this.core,
884
+ toolCallId: toolCall.id,
885
+ agentId,
886
+ toolName: toolCall.function.name,
887
+ result: errorMessage ? "" : toolCallResult,
888
+ error: errorMessage
889
+ }),
890
+ "Subscriber onToolExecutionEnd error:"
891
+ );
892
+ if (isArgumentError) {
893
+ throw new Error(errorMessage ?? "Tool execution failed");
894
+ }
895
+ }
896
+ if (!errorMessage || !isArgumentError) {
897
+ const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
898
+ const toolMessage = {
899
+ id: randomUUID3(),
900
+ role: "tool",
901
+ toolCallId: toolCall.id,
902
+ content: toolCallResult
903
+ };
904
+ agent.messages.splice(messageIndex + 1, 0, toolMessage);
905
+ if (!errorMessage && tool?.followUp !== false) {
906
+ return true;
907
+ }
908
+ }
909
+ return false;
910
+ }
911
+ /**
912
+ * Execute a wildcard tool
913
+ */
914
+ async executeWildcardTool(wildcardTool, toolCall, message, agent, agentId) {
915
+ if (wildcardTool?.agentId && wildcardTool.agentId !== agent.agentId) {
916
+ return false;
917
+ }
918
+ let toolCallResult = "";
919
+ let errorMessage;
920
+ let isArgumentError = false;
921
+ if (wildcardTool?.handler) {
922
+ let parsedArgs;
923
+ try {
924
+ parsedArgs = JSON.parse(toolCall.function.arguments);
925
+ } catch (error) {
926
+ const parseError = error instanceof Error ? error : new Error(String(error));
927
+ errorMessage = parseError.message;
928
+ isArgumentError = true;
929
+ await this.core.emitError({
930
+ error: parseError,
931
+ code: "tool_argument_parse_failed" /* TOOL_ARGUMENT_PARSE_FAILED */,
932
+ context: {
933
+ agentId,
934
+ toolCallId: toolCall.id,
935
+ toolName: toolCall.function.name,
936
+ rawArguments: toolCall.function.arguments,
937
+ toolType: "wildcard",
938
+ messageId: message.id
939
+ }
940
+ });
941
+ }
942
+ const wildcardArgs = {
943
+ toolName: toolCall.function.name,
944
+ args: parsedArgs
945
+ };
946
+ await this.core.notifySubscribers(
947
+ (subscriber) => subscriber.onToolExecutionStart?.({
948
+ copilotkit: this.core,
949
+ toolCallId: toolCall.id,
950
+ agentId,
951
+ toolName: toolCall.function.name,
952
+ args: wildcardArgs
953
+ }),
954
+ "Subscriber onToolExecutionStart error:"
955
+ );
956
+ if (!errorMessage) {
957
+ try {
958
+ const result = await wildcardTool.handler(wildcardArgs, toolCall);
959
+ if (result === void 0 || result === null) {
960
+ toolCallResult = "";
961
+ } else if (typeof result === "string") {
962
+ toolCallResult = result;
963
+ } else {
964
+ toolCallResult = JSON.stringify(result);
965
+ }
966
+ } catch (error) {
967
+ const handlerError = error instanceof Error ? error : new Error(String(error));
968
+ errorMessage = handlerError.message;
969
+ await this.core.emitError({
970
+ error: handlerError,
971
+ code: "tool_handler_failed" /* TOOL_HANDLER_FAILED */,
972
+ context: {
973
+ agentId,
974
+ toolCallId: toolCall.id,
975
+ toolName: toolCall.function.name,
976
+ parsedArgs: wildcardArgs,
977
+ toolType: "wildcard",
978
+ messageId: message.id
979
+ }
980
+ });
981
+ }
982
+ }
983
+ if (errorMessage) {
984
+ toolCallResult = `Error: ${errorMessage}`;
985
+ }
986
+ await this.core.notifySubscribers(
987
+ (subscriber) => subscriber.onToolExecutionEnd?.({
988
+ copilotkit: this.core,
989
+ toolCallId: toolCall.id,
990
+ agentId,
991
+ toolName: toolCall.function.name,
992
+ result: errorMessage ? "" : toolCallResult,
993
+ error: errorMessage
994
+ }),
995
+ "Subscriber onToolExecutionEnd error:"
996
+ );
997
+ if (isArgumentError) {
998
+ throw new Error(errorMessage ?? "Tool execution failed");
999
+ }
1000
+ }
1001
+ if (!errorMessage || !isArgumentError) {
1002
+ const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
1003
+ const toolMessage = {
1004
+ id: randomUUID3(),
1005
+ role: "tool",
1006
+ toolCallId: toolCall.id,
1007
+ content: toolCallResult
1008
+ };
1009
+ agent.messages.splice(messageIndex + 1, 0, toolMessage);
1010
+ if (!errorMessage && wildcardTool?.followUp !== false) {
1011
+ return true;
1012
+ }
1013
+ }
1014
+ return false;
1015
+ }
1016
+ /**
1017
+ * Build frontend tools for an agent
1018
+ */
738
1019
  buildFrontendTools(agentId) {
739
1020
  return this._tools.filter((tool) => !tool.agentId || tool.agentId === agentId).map((tool) => ({
740
1021
  name: tool.name,
@@ -742,13 +1023,16 @@ var CopilotKitCore = class {
742
1023
  parameters: createToolSchema(tool)
743
1024
  }));
744
1025
  }
745
- createAgentErrorSubscriber(agent, agentId) {
1026
+ /**
1027
+ * Create an agent error subscriber
1028
+ */
1029
+ createAgentErrorSubscriber(agent) {
746
1030
  const emitAgentError = async (error, code, extraContext = {}) => {
747
1031
  const context = { ...extraContext };
748
- if (agentId ?? agent.agentId) {
749
- context.agentId = agentId ?? agent.agentId;
1032
+ if (agent.agentId) {
1033
+ context.agentId = agent.agentId;
750
1034
  }
751
- await this.emitError({
1035
+ await this.core.emitError({
752
1036
  error,
753
1037
  code,
754
1038
  context
@@ -756,13 +1040,9 @@ var CopilotKitCore = class {
756
1040
  };
757
1041
  return {
758
1042
  onRunFailed: async ({ error }) => {
759
- await emitAgentError(
760
- error,
761
- "agent_run_failed_event" /* AGENT_RUN_FAILED_EVENT */,
762
- {
763
- source: "onRunFailed"
764
- }
765
- );
1043
+ await emitAgentError(error, "agent_run_failed_event" /* AGENT_RUN_FAILED_EVENT */, {
1044
+ source: "onRunFailed"
1045
+ });
766
1046
  },
767
1047
  onRunErrorEvent: async ({ event }) => {
768
1048
  const eventError = event?.rawEvent instanceof Error ? event.rawEvent : event?.rawEvent?.error instanceof Error ? event.rawEvent.error : void 0;
@@ -771,15 +1051,11 @@ var CopilotKitCore = class {
771
1051
  if (event?.code && !rawError.code) {
772
1052
  rawError.code = event.code;
773
1053
  }
774
- await emitAgentError(
775
- rawError,
776
- "agent_run_error_event" /* AGENT_RUN_ERROR_EVENT */,
777
- {
778
- source: "onRunErrorEvent",
779
- event,
780
- runtimeErrorCode: event?.code
781
- }
782
- );
1054
+ await emitAgentError(rawError, "agent_run_error_event" /* AGENT_RUN_ERROR_EVENT */, {
1055
+ source: "onRunErrorEvent",
1056
+ event,
1057
+ runtimeErrorCode: event?.code
1058
+ });
783
1059
  }
784
1060
  };
785
1061
  }
@@ -812,6 +1088,224 @@ function createToolSchema(tool) {
812
1088
  return schema;
813
1089
  }
814
1090
 
1091
+ // src/core/core.ts
1092
+ var CopilotKitCoreErrorCode = /* @__PURE__ */ ((CopilotKitCoreErrorCode2) => {
1093
+ CopilotKitCoreErrorCode2["RUNTIME_INFO_FETCH_FAILED"] = "runtime_info_fetch_failed";
1094
+ CopilotKitCoreErrorCode2["AGENT_CONNECT_FAILED"] = "agent_connect_failed";
1095
+ CopilotKitCoreErrorCode2["AGENT_RUN_FAILED"] = "agent_run_failed";
1096
+ CopilotKitCoreErrorCode2["AGENT_RUN_FAILED_EVENT"] = "agent_run_failed_event";
1097
+ CopilotKitCoreErrorCode2["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
1098
+ CopilotKitCoreErrorCode2["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
1099
+ CopilotKitCoreErrorCode2["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
1100
+ return CopilotKitCoreErrorCode2;
1101
+ })(CopilotKitCoreErrorCode || {});
1102
+ var CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ ((CopilotKitCoreRuntimeConnectionStatus2) => {
1103
+ CopilotKitCoreRuntimeConnectionStatus2["Disconnected"] = "disconnected";
1104
+ CopilotKitCoreRuntimeConnectionStatus2["Connected"] = "connected";
1105
+ CopilotKitCoreRuntimeConnectionStatus2["Connecting"] = "connecting";
1106
+ CopilotKitCoreRuntimeConnectionStatus2["Error"] = "error";
1107
+ return CopilotKitCoreRuntimeConnectionStatus2;
1108
+ })(CopilotKitCoreRuntimeConnectionStatus || {});
1109
+ var CopilotKitCore = class {
1110
+ _headers;
1111
+ _properties;
1112
+ subscribers = /* @__PURE__ */ new Set();
1113
+ // Delegate classes
1114
+ agentRegistry;
1115
+ contextStore;
1116
+ suggestionEngine;
1117
+ runHandler;
1118
+ constructor({
1119
+ runtimeUrl,
1120
+ headers = {},
1121
+ properties = {},
1122
+ agents__unsafe_dev_only = {},
1123
+ tools = [],
1124
+ suggestionsConfig = []
1125
+ }) {
1126
+ this._headers = headers;
1127
+ this._properties = properties;
1128
+ this.agentRegistry = new AgentRegistry(this);
1129
+ this.contextStore = new ContextStore(this);
1130
+ this.suggestionEngine = new SuggestionEngine(this);
1131
+ this.runHandler = new RunHandler(this);
1132
+ this.agentRegistry.initialize(agents__unsafe_dev_only);
1133
+ this.runHandler.initialize(tools);
1134
+ this.suggestionEngine.initialize(suggestionsConfig);
1135
+ this.agentRegistry.setRuntimeUrl(runtimeUrl);
1136
+ }
1137
+ /**
1138
+ * Internal method used by delegate classes to notify subscribers
1139
+ */
1140
+ async notifySubscribers(handler, errorMessage) {
1141
+ await Promise.all(
1142
+ Array.from(this.subscribers).map(async (subscriber) => {
1143
+ try {
1144
+ await handler(subscriber);
1145
+ } catch (error) {
1146
+ console.error(errorMessage, error);
1147
+ }
1148
+ })
1149
+ );
1150
+ }
1151
+ /**
1152
+ * Internal method used by delegate classes to emit errors
1153
+ */
1154
+ async emitError({
1155
+ error,
1156
+ code,
1157
+ context = {}
1158
+ }) {
1159
+ await this.notifySubscribers(
1160
+ (subscriber) => subscriber.onError?.({
1161
+ copilotkit: this,
1162
+ error,
1163
+ code,
1164
+ context
1165
+ }),
1166
+ "Subscriber onError error:"
1167
+ );
1168
+ }
1169
+ /**
1170
+ * Snapshot accessors
1171
+ */
1172
+ get context() {
1173
+ return this.contextStore.context;
1174
+ }
1175
+ get agents() {
1176
+ return this.agentRegistry.agents;
1177
+ }
1178
+ get tools() {
1179
+ return this.runHandler.tools;
1180
+ }
1181
+ get runtimeUrl() {
1182
+ return this.agentRegistry.runtimeUrl;
1183
+ }
1184
+ setRuntimeUrl(runtimeUrl) {
1185
+ this.agentRegistry.setRuntimeUrl(runtimeUrl);
1186
+ }
1187
+ get runtimeVersion() {
1188
+ return this.agentRegistry.runtimeVersion;
1189
+ }
1190
+ get headers() {
1191
+ return this._headers;
1192
+ }
1193
+ get properties() {
1194
+ return this._properties;
1195
+ }
1196
+ get runtimeConnectionStatus() {
1197
+ return this.agentRegistry.runtimeConnectionStatus;
1198
+ }
1199
+ /**
1200
+ * Configuration updates
1201
+ */
1202
+ setHeaders(headers) {
1203
+ this._headers = headers;
1204
+ this.agentRegistry.applyHeadersToAgents(this.agentRegistry.agents);
1205
+ void this.notifySubscribers(
1206
+ (subscriber) => subscriber.onHeadersChanged?.({
1207
+ copilotkit: this,
1208
+ headers: this.headers
1209
+ }),
1210
+ "Subscriber onHeadersChanged error:"
1211
+ );
1212
+ }
1213
+ setProperties(properties) {
1214
+ this._properties = properties;
1215
+ void this.notifySubscribers(
1216
+ (subscriber) => subscriber.onPropertiesChanged?.({
1217
+ copilotkit: this,
1218
+ properties: this.properties
1219
+ }),
1220
+ "Subscriber onPropertiesChanged error:"
1221
+ );
1222
+ }
1223
+ /**
1224
+ * Agent management (delegated to AgentRegistry)
1225
+ */
1226
+ setAgents__unsafe_dev_only(agents) {
1227
+ this.agentRegistry.setAgents__unsafe_dev_only(agents);
1228
+ }
1229
+ addAgent__unsafe_dev_only(params) {
1230
+ this.agentRegistry.addAgent__unsafe_dev_only(params);
1231
+ }
1232
+ removeAgent__unsafe_dev_only(id) {
1233
+ this.agentRegistry.removeAgent__unsafe_dev_only(id);
1234
+ }
1235
+ getAgent(id) {
1236
+ return this.agentRegistry.getAgent(id);
1237
+ }
1238
+ /**
1239
+ * Context management (delegated to ContextStore)
1240
+ */
1241
+ addContext(context) {
1242
+ return this.contextStore.addContext(context);
1243
+ }
1244
+ removeContext(id) {
1245
+ this.contextStore.removeContext(id);
1246
+ }
1247
+ /**
1248
+ * Suggestions management (delegated to SuggestionEngine)
1249
+ */
1250
+ addSuggestionsConfig(config) {
1251
+ return this.suggestionEngine.addSuggestionsConfig(config);
1252
+ }
1253
+ removeSuggestionsConfig(id) {
1254
+ this.suggestionEngine.removeSuggestionsConfig(id);
1255
+ }
1256
+ reloadSuggestions(agentId) {
1257
+ this.suggestionEngine.reloadSuggestions(agentId);
1258
+ }
1259
+ clearSuggestions(agentId) {
1260
+ this.suggestionEngine.clearSuggestions(agentId);
1261
+ }
1262
+ getSuggestions(agentId) {
1263
+ return this.suggestionEngine.getSuggestions(agentId);
1264
+ }
1265
+ /**
1266
+ * Tool management (delegated to RunHandler)
1267
+ */
1268
+ addTool(tool) {
1269
+ this.runHandler.addTool(tool);
1270
+ }
1271
+ removeTool(id, agentId) {
1272
+ this.runHandler.removeTool(id, agentId);
1273
+ }
1274
+ getTool(params) {
1275
+ return this.runHandler.getTool(params);
1276
+ }
1277
+ setTools(tools) {
1278
+ this.runHandler.setTools(tools);
1279
+ }
1280
+ /**
1281
+ * Subscription lifecycle
1282
+ */
1283
+ subscribe(subscriber) {
1284
+ this.subscribers.add(subscriber);
1285
+ return () => {
1286
+ this.unsubscribe(subscriber);
1287
+ };
1288
+ }
1289
+ unsubscribe(subscriber) {
1290
+ this.subscribers.delete(subscriber);
1291
+ }
1292
+ /**
1293
+ * Agent connectivity (delegated to RunHandler)
1294
+ */
1295
+ async connectAgent(params) {
1296
+ return this.runHandler.connectAgent(params);
1297
+ }
1298
+ async runAgent(params) {
1299
+ return this.runHandler.runAgent(params);
1300
+ }
1301
+ /**
1302
+ * Internal method used by RunHandler to build frontend tools
1303
+ */
1304
+ buildFrontendTools(agentId) {
1305
+ return this.runHandler.buildFrontendTools(agentId);
1306
+ }
1307
+ };
1308
+
815
1309
  // src/types.ts
816
1310
  var ToolCallStatus = /* @__PURE__ */ ((ToolCallStatus2) => {
817
1311
  ToolCallStatus2["InProgress"] = "inProgress";
@@ -1036,10 +1530,14 @@ ${indent}${fence}`;
1036
1530
  return result;
1037
1531
  }
1038
1532
  export {
1533
+ AgentRegistry,
1534
+ ContextStore,
1039
1535
  CopilotKitCore,
1040
1536
  CopilotKitCoreErrorCode,
1041
1537
  CopilotKitCoreRuntimeConnectionStatus,
1042
1538
  ProxiedCopilotRuntimeAgent,
1539
+ RunHandler,
1540
+ SuggestionEngine,
1043
1541
  ToolCallStatus,
1044
1542
  completePartialMarkdown
1045
1543
  };