@copilotkitnext/core 0.0.12 → 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,6 +1,6 @@
1
- // src/core.ts
2
- import { DEFAULT_AGENT_ID, randomUUID, logger } from "@copilotkitnext/shared";
1
+ // src/core/agent-registry.ts
3
2
  import { HttpAgent as HttpAgent2 } from "@ag-ui/client";
3
+ import { logger } from "@copilotkitnext/shared";
4
4
 
5
5
  // src/agent.ts
6
6
  import {
@@ -26,129 +26,43 @@ var ProxiedCopilotRuntimeAgent = class extends HttpAgent {
26
26
  }
27
27
  };
28
28
 
29
- // src/core.ts
30
- import { zodToJsonSchema } from "zod-to-json-schema";
31
- var CopilotKitCoreErrorCode = /* @__PURE__ */ ((CopilotKitCoreErrorCode2) => {
32
- CopilotKitCoreErrorCode2["RUNTIME_INFO_FETCH_FAILED"] = "runtime_info_fetch_failed";
33
- CopilotKitCoreErrorCode2["AGENT_CONNECT_FAILED"] = "agent_connect_failed";
34
- CopilotKitCoreErrorCode2["AGENT_RUN_FAILED"] = "agent_run_failed";
35
- CopilotKitCoreErrorCode2["AGENT_RUN_FAILED_EVENT"] = "agent_run_failed_event";
36
- CopilotKitCoreErrorCode2["AGENT_RUN_ERROR_EVENT"] = "agent_run_error_event";
37
- CopilotKitCoreErrorCode2["TOOL_ARGUMENT_PARSE_FAILED"] = "tool_argument_parse_failed";
38
- CopilotKitCoreErrorCode2["TOOL_HANDLER_FAILED"] = "tool_handler_failed";
39
- return CopilotKitCoreErrorCode2;
40
- })(CopilotKitCoreErrorCode || {});
41
- var CopilotKitCoreRuntimeConnectionStatus = /* @__PURE__ */ ((CopilotKitCoreRuntimeConnectionStatus2) => {
42
- CopilotKitCoreRuntimeConnectionStatus2["Disconnected"] = "disconnected";
43
- CopilotKitCoreRuntimeConnectionStatus2["Connected"] = "connected";
44
- CopilotKitCoreRuntimeConnectionStatus2["Connecting"] = "connecting";
45
- CopilotKitCoreRuntimeConnectionStatus2["Error"] = "error";
46
- return CopilotKitCoreRuntimeConnectionStatus2;
47
- })(CopilotKitCoreRuntimeConnectionStatus || {});
48
- var CopilotKitCore = class {
49
- _headers;
50
- _properties;
51
- _context = {};
29
+ // src/core/agent-registry.ts
30
+ var AgentRegistry = class {
31
+ constructor(core) {
32
+ this.core = core;
33
+ }
52
34
  _agents = {};
53
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
- _tools = [];
55
35
  localAgents = {};
56
36
  remoteAgents = {};
57
- subscribers = /* @__PURE__ */ new Set();
58
37
  _runtimeUrl;
59
38
  _runtimeVersion;
60
39
  _runtimeConnectionStatus = "disconnected" /* Disconnected */;
61
- constructor({
62
- runtimeUrl,
63
- headers = {},
64
- properties = {},
65
- agents__unsafe_dev_only = {},
66
- tools = []
67
- }) {
68
- this._headers = headers;
69
- this._properties = properties;
70
- this.localAgents = this.assignAgentIds(agents__unsafe_dev_only);
71
- this.applyHeadersToAgents(this.localAgents);
72
- this._agents = this.localAgents;
73
- this._tools = tools;
74
- this.setRuntimeUrl(runtimeUrl);
75
- }
76
- applyHeadersToAgent(agent) {
77
- if (agent instanceof HttpAgent2) {
78
- agent.headers = { ...this.headers };
79
- }
80
- }
81
- applyHeadersToAgents(agents) {
82
- Object.values(agents).forEach((agent) => {
83
- this.applyHeadersToAgent(agent);
84
- });
85
- }
86
- assignAgentIds(agents) {
87
- Object.entries(agents).forEach(([id, agent]) => {
88
- if (agent && !agent.agentId) {
89
- agent.agentId = id;
90
- }
91
- });
92
- return agents;
93
- }
94
- async notifySubscribers(handler, errorMessage) {
95
- await Promise.all(
96
- Array.from(this.subscribers).map(async (subscriber) => {
97
- try {
98
- await handler(subscriber);
99
- } catch (error) {
100
- logger.error(errorMessage, error);
101
- }
102
- })
103
- );
104
- }
105
- async emitError({
106
- error,
107
- code,
108
- context = {}
109
- }) {
110
- await this.notifySubscribers(
111
- (subscriber) => subscriber.onError?.({
112
- copilotkit: this,
113
- error,
114
- code,
115
- context
116
- }),
117
- "Subscriber onError error:"
118
- );
119
- }
120
- resolveAgentId(agent, providedAgentId) {
121
- if (providedAgentId) {
122
- return providedAgentId;
123
- }
124
- if (agent.agentId) {
125
- return agent.agentId;
126
- }
127
- const found = Object.entries(this._agents).find(([, storedAgent]) => {
128
- return storedAgent === agent;
129
- });
130
- if (found) {
131
- agent.agentId = found[0];
132
- return found[0];
133
- }
134
- agent.agentId = DEFAULT_AGENT_ID;
135
- return DEFAULT_AGENT_ID;
136
- }
137
40
  /**
138
- * Snapshot accessors
41
+ * Get all agents as a readonly record
139
42
  */
140
- get context() {
141
- return this._context;
142
- }
143
43
  get agents() {
144
44
  return this._agents;
145
45
  }
146
- get tools() {
147
- return this._tools;
148
- }
149
46
  get runtimeUrl() {
150
47
  return this._runtimeUrl;
151
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) {
59
+ this.localAgents = this.assignAgentIds(agents);
60
+ this.applyHeadersToAgents(this.localAgents);
61
+ this._agents = this.localAgents;
62
+ }
63
+ /**
64
+ * Set the runtime URL and update connection
65
+ */
152
66
  setRuntimeUrl(runtimeUrl) {
153
67
  const normalizedRuntimeUrl = runtimeUrl ? runtimeUrl.replace(/\/$/, "") : void 0;
154
68
  if (this._runtimeUrl === normalizedRuntimeUrl) {
@@ -157,54 +71,88 @@ var CopilotKitCore = class {
157
71
  this._runtimeUrl = normalizedRuntimeUrl;
158
72
  void this.updateRuntimeConnection();
159
73
  }
160
- get runtimeVersion() {
161
- return this._runtimeVersion;
74
+ /**
75
+ * Set all agents at once (for development use)
76
+ */
77
+ setAgents__unsafe_dev_only(agents) {
78
+ Object.entries(agents).forEach(([id, agent]) => {
79
+ if (agent) {
80
+ this.validateAndAssignAgentId(id, agent);
81
+ }
82
+ });
83
+ this.localAgents = agents;
84
+ this._agents = { ...this.localAgents, ...this.remoteAgents };
85
+ this.applyHeadersToAgents(this._agents);
86
+ void this.notifyAgentsChanged();
162
87
  }
163
- get headers() {
164
- return this._headers;
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();
165
97
  }
166
- get properties() {
167
- return this._properties;
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();
168
105
  }
169
- get runtimeConnectionStatus() {
170
- return this._runtimeConnectionStatus;
106
+ /**
107
+ * Get an agent by ID
108
+ */
109
+ getAgent(id) {
110
+ if (id in this._agents) {
111
+ return this._agents[id];
112
+ }
113
+ if (this.runtimeUrl !== void 0 && (this.runtimeConnectionStatus === "disconnected" /* Disconnected */ || this.runtimeConnectionStatus === "connecting" /* Connecting */)) {
114
+ return void 0;
115
+ }
116
+ console.warn(`Agent ${id} not found`);
117
+ return void 0;
118
+ }
119
+ /**
120
+ * Apply current headers to an agent
121
+ */
122
+ applyHeadersToAgent(agent) {
123
+ if (agent instanceof HttpAgent2) {
124
+ agent.headers = { ...this.core.headers };
125
+ }
126
+ }
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,43 +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
187
  logger.warn(`Failed to load runtime info (${this.runtimeUrl}/info): ${message}`);
263
188
  const runtimeError = error instanceof Error ? error : new Error(String(error));
264
- await this.emitError({
189
+ await this.core.emitError({
265
190
  error: runtimeError,
266
191
  code: "runtime_info_fetch_failed" /* RUNTIME_INFO_FETCH_FAILED */,
267
192
  context: {
@@ -271,114 +196,479 @@ var CopilotKitCore = class {
271
196
  }
272
197
  }
273
198
  /**
274
- * Configuration updates
199
+ * Assign agent IDs to a record of agents
275
200
  */
276
- setHeaders(headers) {
277
- this._headers = headers;
278
- this.applyHeadersToAgents(this._agents);
279
- void this.notifySubscribers(
280
- (subscriber) => subscriber.onHeadersChanged?.({
281
- copilotkit: this,
282
- headers: this.headers
283
- }),
284
- "Subscriber onHeadersChanged error:"
285
- );
286
- }
287
- setProperties(properties) {
288
- this._properties = properties;
289
- void this.notifySubscribers(
290
- (subscriber) => subscriber.onPropertiesChanged?.({
291
- copilotkit: this,
292
- properties: this.properties
293
- }),
294
- "Subscriber onPropertiesChanged error:"
295
- );
296
- }
297
- setAgents__unsafe_dev_only(agents) {
298
- this.localAgents = this.assignAgentIds(agents);
299
- this._agents = { ...this.localAgents, ...this.remoteAgents };
300
- this.applyHeadersToAgents(this._agents);
301
- void this.notifySubscribers(
302
- (subscriber) => subscriber.onAgentsChanged?.({
303
- copilotkit: this,
304
- agents: this._agents
305
- }),
306
- "Subscriber onAgentsChanged error:"
307
- );
201
+ assignAgentIds(agents) {
202
+ Object.entries(agents).forEach(([id, agent]) => {
203
+ if (agent) {
204
+ this.validateAndAssignAgentId(id, agent);
205
+ }
206
+ });
207
+ return agents;
308
208
  }
309
- addAgent__unsafe_dev_only({ id, agent }) {
310
- 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
+ }
311
218
  if (!agent.agentId) {
312
- agent.agentId = id;
219
+ agent.agentId = registrationId;
313
220
  }
314
- this.applyHeadersToAgent(agent);
315
- this._agents = { ...this.localAgents, ...this.remoteAgents };
316
- void this.notifySubscribers(
317
- (subscriber) => subscriber.onAgentsChanged?.({
318
- copilotkit: this,
319
- 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
320
230
  }),
321
- "Subscriber onAgentsChanged error:"
231
+ "Error in CopilotKitCore subscriber (onRuntimeConnectionStatusChanged):"
322
232
  );
323
233
  }
324
- removeAgent__unsafe_dev_only(id) {
325
- delete this.localAgents[id];
326
- this._agents = { ...this.localAgents, ...this.remoteAgents };
327
- void this.notifySubscribers(
234
+ /**
235
+ * Notify subscribers of agent changes
236
+ */
237
+ async notifyAgentsChanged() {
238
+ await this.core.notifySubscribers(
328
239
  (subscriber) => subscriber.onAgentsChanged?.({
329
- copilotkit: this,
240
+ copilotkit: this.core,
330
241
  agents: this._agents
331
242
  }),
332
243
  "Subscriber onAgentsChanged error:"
333
244
  );
334
245
  }
335
- getAgent(id) {
336
- if (id in this._agents) {
337
- return this._agents[id];
338
- }
339
- if (this.runtimeUrl !== void 0 && (this.runtimeConnectionStatus === "disconnected" /* Disconnected */ || this.runtimeConnectionStatus === "connecting" /* Connecting */)) {
340
- return void 0;
341
- } else {
342
- console.warn(`Agent ${id} not found`);
343
- return void 0;
344
- }
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;
345
260
  }
346
261
  /**
347
- * Context management
262
+ * Add a new context entry
263
+ * @returns The ID of the created context entry
348
264
  */
349
265
  addContext({ description, value }) {
350
266
  const id = randomUUID();
351
267
  this._context[id] = { description, value };
352
- void this.notifySubscribers(
353
- (subscriber) => subscriber.onContextChanged?.({
354
- copilotkit: this,
355
- context: this._context
356
- }),
357
- "Subscriber onContextChanged error:"
358
- );
268
+ void this.notifySubscribers();
359
269
  return id;
360
270
  }
271
+ /**
272
+ * Remove a context entry by ID
273
+ */
361
274
  removeContext(id) {
362
275
  delete this._context[id];
363
- 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(
364
283
  (subscriber) => subscriber.onContextChanged?.({
365
- copilotkit: this,
284
+ copilotkit: this.core,
366
285
  context: this._context
367
286
  }),
368
287
  "Subscriber onContextChanged error:"
369
288
  );
370
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
+ }
371
658
  /**
372
- * Tool management
659
+ * Add a tool to the registry
373
660
  */
374
661
  addTool(tool) {
375
662
  const existingToolIndex = this._tools.findIndex((t) => t.name === tool.name && t.agentId === tool.agentId);
376
663
  if (existingToolIndex !== -1) {
377
- logger.warn(`Tool already exists: '${tool.name}' for agent '${tool.agentId || "global"}', skipping.`);
664
+ logger2.warn(`Tool already exists: '${tool.name}' for agent '${tool.agentId || "global"}', skipping.`);
378
665
  return;
379
666
  }
380
667
  this._tools.push(tool);
381
668
  }
669
+ /**
670
+ * Remove a tool by name and optionally by agentId
671
+ */
382
672
  removeTool(id, agentId) {
383
673
  this._tools = this._tools.filter((tool) => {
384
674
  if (agentId !== void 0) {
@@ -409,40 +699,28 @@ var CopilotKitCore = class {
409
699
  this._tools = [...tools];
410
700
  }
411
701
  /**
412
- * Subscription lifecycle
413
- */
414
- subscribe(subscriber) {
415
- this.subscribers.add(subscriber);
416
- return () => {
417
- this.unsubscribe(subscriber);
418
- };
419
- }
420
- unsubscribe(subscriber) {
421
- this.subscribers.delete(subscriber);
422
- }
423
- /**
424
- * Agent connectivity
702
+ * Connect an agent (establish initial connection)
425
703
  */
426
- async connectAgent({ agent, agentId }) {
704
+ async connectAgent({ agent }) {
427
705
  try {
428
- if (agent instanceof HttpAgent2) {
429
- agent.headers = { ...this.headers };
706
+ if (agent instanceof HttpAgent3) {
707
+ agent.headers = { ...this.core.headers };
430
708
  }
431
709
  const runAgentResult = await agent.connectAgent(
432
710
  {
433
- forwardedProps: this.properties,
434
- tools: this.buildFrontendTools(agentId)
711
+ forwardedProps: this.core.properties,
712
+ tools: this.buildFrontendTools(agent.agentId)
435
713
  },
436
- this.createAgentErrorSubscriber(agent, agentId)
714
+ this.createAgentErrorSubscriber(agent)
437
715
  );
438
- return this.processAgentResult({ runAgentResult, agent, agentId });
716
+ return this.processAgentResult({ runAgentResult, agent });
439
717
  } catch (error) {
440
718
  const connectError = error instanceof Error ? error : new Error(String(error));
441
719
  const context = {};
442
- if (agentId ?? agent.agentId) {
443
- context.agentId = agentId ?? agent.agentId;
720
+ if (agent.agentId) {
721
+ context.agentId = agent.agentId;
444
722
  }
445
- await this.emitError({
723
+ await this.core.emitError({
446
724
  error: connectError,
447
725
  code: "agent_connect_failed" /* AGENT_CONNECT_FAILED */,
448
726
  context
@@ -450,9 +728,13 @@ var CopilotKitCore = class {
450
728
  throw error;
451
729
  }
452
730
  }
453
- async runAgent({ agent, withMessages, agentId }) {
454
- if (agent instanceof HttpAgent2) {
455
- 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 };
456
738
  }
457
739
  if (withMessages) {
458
740
  agent.addMessages(withMessages);
@@ -460,22 +742,22 @@ var CopilotKitCore = class {
460
742
  try {
461
743
  const runAgentResult = await agent.runAgent(
462
744
  {
463
- forwardedProps: this.properties,
464
- tools: this.buildFrontendTools(agentId)
745
+ forwardedProps: this.core.properties,
746
+ tools: this.buildFrontendTools(agent.agentId)
465
747
  },
466
- this.createAgentErrorSubscriber(agent, agentId)
748
+ this.createAgentErrorSubscriber(agent)
467
749
  );
468
- return this.processAgentResult({ runAgentResult, agent, agentId });
750
+ return this.processAgentResult({ runAgentResult, agent });
469
751
  } catch (error) {
470
752
  const runError = error instanceof Error ? error : new Error(String(error));
471
753
  const context = {};
472
- if (agentId ?? agent.agentId) {
473
- context.agentId = agentId ?? agent.agentId;
754
+ if (agent.agentId) {
755
+ context.agentId = agent.agentId;
474
756
  }
475
757
  if (withMessages) {
476
758
  context.messageCount = withMessages.length;
477
759
  }
478
- await this.emitError({
760
+ await this.core.emitError({
479
761
  error: runError,
480
762
  code: "agent_run_failed" /* AGENT_RUN_FAILED */,
481
763
  context
@@ -483,13 +765,15 @@ var CopilotKitCore = class {
483
765
  throw error;
484
766
  }
485
767
  }
768
+ /**
769
+ * Process agent result and execute tools
770
+ */
486
771
  async processAgentResult({
487
772
  runAgentResult,
488
- agent,
489
- agentId
773
+ agent
490
774
  }) {
491
775
  const { newMessages } = runAgentResult;
492
- const effectiveAgentId = this.resolveAgentId(agent, agentId);
776
+ const agentId = agent.agentId;
493
777
  let needsFollowUp = false;
494
778
  for (const message of newMessages) {
495
779
  if (message.role === "assistant") {
@@ -497,205 +781,19 @@ var CopilotKitCore = class {
497
781
  if (newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id) === -1) {
498
782
  const tool = this.getTool({
499
783
  toolName: toolCall.function.name,
500
- agentId
784
+ agentId: agent.agentId
501
785
  });
502
786
  if (tool) {
503
- if (tool?.agentId && tool.agentId !== agentId) {
504
- continue;
505
- }
506
- let toolCallResult = "";
507
- let errorMessage;
508
- let isArgumentError = false;
509
- if (tool?.handler) {
510
- let parsedArgs;
511
- try {
512
- parsedArgs = JSON.parse(toolCall.function.arguments);
513
- } catch (error) {
514
- const parseError = error instanceof Error ? error : new Error(String(error));
515
- errorMessage = parseError.message;
516
- isArgumentError = true;
517
- await this.emitError({
518
- error: parseError,
519
- code: "tool_argument_parse_failed" /* TOOL_ARGUMENT_PARSE_FAILED */,
520
- context: {
521
- agentId: effectiveAgentId,
522
- toolCallId: toolCall.id,
523
- toolName: toolCall.function.name,
524
- rawArguments: toolCall.function.arguments,
525
- toolType: "specific",
526
- messageId: message.id
527
- }
528
- });
529
- }
530
- await this.notifySubscribers(
531
- (subscriber) => subscriber.onToolExecutionStart?.({
532
- copilotkit: this,
533
- toolCallId: toolCall.id,
534
- agentId: effectiveAgentId,
535
- toolName: toolCall.function.name,
536
- args: parsedArgs
537
- }),
538
- "Subscriber onToolExecutionStart error:"
539
- );
540
- if (!errorMessage) {
541
- try {
542
- const result = await tool.handler(parsedArgs, toolCall);
543
- if (result === void 0 || result === null) {
544
- toolCallResult = "";
545
- } else if (typeof result === "string") {
546
- toolCallResult = result;
547
- } else {
548
- toolCallResult = JSON.stringify(result);
549
- }
550
- } catch (error) {
551
- const handlerError = error instanceof Error ? error : new Error(String(error));
552
- errorMessage = handlerError.message;
553
- await this.emitError({
554
- error: handlerError,
555
- code: "tool_handler_failed" /* TOOL_HANDLER_FAILED */,
556
- context: {
557
- agentId: effectiveAgentId,
558
- toolCallId: toolCall.id,
559
- toolName: toolCall.function.name,
560
- parsedArgs,
561
- toolType: "specific",
562
- messageId: message.id
563
- }
564
- });
565
- }
566
- }
567
- if (errorMessage) {
568
- toolCallResult = `Error: ${errorMessage}`;
569
- }
570
- await this.notifySubscribers(
571
- (subscriber) => subscriber.onToolExecutionEnd?.({
572
- copilotkit: this,
573
- toolCallId: toolCall.id,
574
- agentId: effectiveAgentId,
575
- toolName: toolCall.function.name,
576
- result: errorMessage ? "" : toolCallResult,
577
- error: errorMessage
578
- }),
579
- "Subscriber onToolExecutionEnd error:"
580
- );
581
- if (isArgumentError) {
582
- throw new Error(errorMessage ?? "Tool execution failed");
583
- }
584
- }
585
- if (!errorMessage || !isArgumentError) {
586
- const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
587
- const toolMessage = {
588
- id: randomUUID(),
589
- role: "tool",
590
- toolCallId: toolCall.id,
591
- content: toolCallResult
592
- };
593
- agent.messages.splice(messageIndex + 1, 0, toolMessage);
594
- if (!errorMessage && tool?.followUp !== false) {
595
- needsFollowUp = true;
596
- }
787
+ const followUp = await this.executeSpecificTool(tool, toolCall, message, agent, agentId);
788
+ if (followUp) {
789
+ needsFollowUp = true;
597
790
  }
598
791
  } else {
599
- const wildcardTool = this.getTool({ toolName: "*", agentId });
792
+ const wildcardTool = this.getTool({ toolName: "*", agentId: agent.agentId });
600
793
  if (wildcardTool) {
601
- if (wildcardTool?.agentId && wildcardTool.agentId !== agentId) {
602
- continue;
603
- }
604
- let toolCallResult = "";
605
- let errorMessage;
606
- let isArgumentError = false;
607
- if (wildcardTool?.handler) {
608
- let parsedArgs;
609
- try {
610
- parsedArgs = JSON.parse(toolCall.function.arguments);
611
- } catch (error) {
612
- const parseError = error instanceof Error ? error : new Error(String(error));
613
- errorMessage = parseError.message;
614
- isArgumentError = true;
615
- await this.emitError({
616
- error: parseError,
617
- code: "tool_argument_parse_failed" /* TOOL_ARGUMENT_PARSE_FAILED */,
618
- context: {
619
- agentId: effectiveAgentId,
620
- toolCallId: toolCall.id,
621
- toolName: toolCall.function.name,
622
- rawArguments: toolCall.function.arguments,
623
- toolType: "wildcard",
624
- messageId: message.id
625
- }
626
- });
627
- }
628
- const wildcardArgs = {
629
- toolName: toolCall.function.name,
630
- args: parsedArgs
631
- };
632
- await this.notifySubscribers(
633
- (subscriber) => subscriber.onToolExecutionStart?.({
634
- copilotkit: this,
635
- toolCallId: toolCall.id,
636
- agentId: effectiveAgentId,
637
- toolName: toolCall.function.name,
638
- args: wildcardArgs
639
- }),
640
- "Subscriber onToolExecutionStart error:"
641
- );
642
- if (!errorMessage) {
643
- try {
644
- const result = await wildcardTool.handler(wildcardArgs, toolCall);
645
- if (result === void 0 || result === null) {
646
- toolCallResult = "";
647
- } else if (typeof result === "string") {
648
- toolCallResult = result;
649
- } else {
650
- toolCallResult = JSON.stringify(result);
651
- }
652
- } catch (error) {
653
- const handlerError = error instanceof Error ? error : new Error(String(error));
654
- errorMessage = handlerError.message;
655
- await this.emitError({
656
- error: handlerError,
657
- code: "tool_handler_failed" /* TOOL_HANDLER_FAILED */,
658
- context: {
659
- agentId: effectiveAgentId,
660
- toolCallId: toolCall.id,
661
- toolName: toolCall.function.name,
662
- parsedArgs: wildcardArgs,
663
- toolType: "wildcard",
664
- messageId: message.id
665
- }
666
- });
667
- }
668
- }
669
- if (errorMessage) {
670
- toolCallResult = `Error: ${errorMessage}`;
671
- }
672
- await this.notifySubscribers(
673
- (subscriber) => subscriber.onToolExecutionEnd?.({
674
- copilotkit: this,
675
- toolCallId: toolCall.id,
676
- agentId: effectiveAgentId,
677
- toolName: toolCall.function.name,
678
- result: errorMessage ? "" : toolCallResult,
679
- error: errorMessage
680
- }),
681
- "Subscriber onToolExecutionEnd error:"
682
- );
683
- if (isArgumentError) {
684
- throw new Error(errorMessage ?? "Tool execution failed");
685
- }
686
- }
687
- if (!errorMessage || !isArgumentError) {
688
- const messageIndex = agent.messages.findIndex((m) => m.id === message.id);
689
- const toolMessage = {
690
- id: randomUUID(),
691
- role: "tool",
692
- toolCallId: toolCall.id,
693
- content: toolCallResult
694
- };
695
- agent.messages.splice(messageIndex + 1, 0, toolMessage);
696
- if (!errorMessage && wildcardTool?.followUp !== false) {
697
- needsFollowUp = true;
698
- }
794
+ const followUp = await this.executeWildcardTool(wildcardTool, toolCall, message, agent, agentId);
795
+ if (followUp) {
796
+ needsFollowUp = true;
699
797
  }
700
798
  }
701
799
  }
@@ -704,10 +802,220 @@ var CopilotKitCore = class {
704
802
  }
705
803
  }
706
804
  if (needsFollowUp) {
707
- return await this.runAgent({ agent, agentId });
805
+ return await this.runAgent({ agent });
708
806
  }
807
+ void this.core.suggestionEngine.reloadSuggestions(agentId);
709
808
  return runAgentResult;
710
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
+ */
711
1019
  buildFrontendTools(agentId) {
712
1020
  return this._tools.filter((tool) => !tool.agentId || tool.agentId === agentId).map((tool) => ({
713
1021
  name: tool.name,
@@ -715,13 +1023,16 @@ var CopilotKitCore = class {
715
1023
  parameters: createToolSchema(tool)
716
1024
  }));
717
1025
  }
718
- createAgentErrorSubscriber(agent, agentId) {
1026
+ /**
1027
+ * Create an agent error subscriber
1028
+ */
1029
+ createAgentErrorSubscriber(agent) {
719
1030
  const emitAgentError = async (error, code, extraContext = {}) => {
720
1031
  const context = { ...extraContext };
721
- if (agentId ?? agent.agentId) {
722
- context.agentId = agentId ?? agent.agentId;
1032
+ if (agent.agentId) {
1033
+ context.agentId = agent.agentId;
723
1034
  }
724
- await this.emitError({
1035
+ await this.core.emitError({
725
1036
  error,
726
1037
  code,
727
1038
  context
@@ -777,6 +1088,224 @@ function createToolSchema(tool) {
777
1088
  return schema;
778
1089
  }
779
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
+
780
1309
  // src/types.ts
781
1310
  var ToolCallStatus = /* @__PURE__ */ ((ToolCallStatus2) => {
782
1311
  ToolCallStatus2["InProgress"] = "inProgress";
@@ -1001,10 +1530,14 @@ ${indent}${fence}`;
1001
1530
  return result;
1002
1531
  }
1003
1532
  export {
1533
+ AgentRegistry,
1534
+ ContextStore,
1004
1535
  CopilotKitCore,
1005
1536
  CopilotKitCoreErrorCode,
1006
1537
  CopilotKitCoreRuntimeConnectionStatus,
1007
1538
  ProxiedCopilotRuntimeAgent,
1539
+ RunHandler,
1540
+ SuggestionEngine,
1008
1541
  ToolCallStatus,
1009
1542
  completePartialMarkdown
1010
1543
  };