@copilotkit/core 1.70.0 → 1.70.1

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.umd.js CHANGED
@@ -2491,6 +2491,141 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2491
2491
  return normalizeToolResultContent(content) === FORWARDED_TO_CLIENT;
2492
2492
  }
2493
2493
 
2494
+ //#endregion
2495
+ //#region src/core/tool-schema.ts
2496
+ /**
2497
+ * Empty tool schema constant
2498
+ */
2499
+ const EMPTY_TOOL_SCHEMA = {
2500
+ type: "object",
2501
+ properties: {}
2502
+ };
2503
+ /**
2504
+ * Create a JSON schema from a tool's parameters
2505
+ */
2506
+ function createToolSchema(tool) {
2507
+ if (!tool.parameters) return { ...EMPTY_TOOL_SCHEMA };
2508
+ const rawSchema = (0, _copilotkit_shared.schemaToJsonSchema)(tool.parameters, { zodToJsonSchema: (schema, options) => (0, zod_to_json_schema.zodToJsonSchema)(schema, options) });
2509
+ if (!rawSchema || typeof rawSchema !== "object") return { ...EMPTY_TOOL_SCHEMA };
2510
+ const { $schema: _$schema, ...schema } = rawSchema;
2511
+ if (typeof schema.type !== "string") schema.type = "object";
2512
+ if (typeof schema.properties !== "object" || schema.properties === null) schema.properties = {};
2513
+ stripAdditionalProperties(schema);
2514
+ return schema;
2515
+ }
2516
+ /**
2517
+ * Remove every `additionalProperties` key from a JSON schema tree, in place.
2518
+ * Some LLM providers reject schemas that carry it.
2519
+ */
2520
+ function stripAdditionalProperties(schema) {
2521
+ if (!schema || typeof schema !== "object") return;
2522
+ if (Array.isArray(schema)) {
2523
+ schema.forEach(stripAdditionalProperties);
2524
+ return;
2525
+ }
2526
+ const record = schema;
2527
+ if (record.additionalProperties !== void 0) delete record.additionalProperties;
2528
+ for (const value of Object.values(record)) stripAdditionalProperties(value);
2529
+ }
2530
+
2531
+ //#endregion
2532
+ //#region src/core/webmcp.ts
2533
+ /**
2534
+ * Return the page's `document.modelContext`, or null when WebMCP is not
2535
+ * available (SSR, React Native, browsers without the API enabled).
2536
+ */
2537
+ function getWebMCPModelContext() {
2538
+ if (typeof document === "undefined") return null;
2539
+ const modelContext = document.modelContext;
2540
+ return modelContext !== null && modelContext !== void 0 ? modelContext : null;
2541
+ }
2542
+ /**
2543
+ * Keeps a set of frontend tools registered on the page's WebMCP model context.
2544
+ *
2545
+ * `sync(desired)` reconciles the registrations with the desired set: tools that
2546
+ * are gone (or were re-registered with a new tool object) are unregistered by
2547
+ * aborting their registration signal, and new tools are registered with an
2548
+ * `execute` that delegates to the frontend tool's own handler.
2549
+ *
2550
+ * All WebMCP-API constraints live here so callers only supply the desired set:
2551
+ * a tool without a description is skipped with a warning (the spec rejects
2552
+ * empty descriptions), and failures from `registerTool` (duplicate name,
2553
+ * unsupported context, ...) are warned rather than thrown.
2554
+ */
2555
+ var WebMCPRegistry = class {
2556
+ constructor() {
2557
+ _defineProperty(this, "entries", /* @__PURE__ */ new Map());
2558
+ _defineProperty(this, "warnedNames", /* @__PURE__ */ new Set());
2559
+ }
2560
+ /** Names currently kept registered on the model context. */
2561
+ get registeredNames() {
2562
+ return [...this.entries.keys()];
2563
+ }
2564
+ /** Reconcile the registered tools with the desired set. */
2565
+ sync(desired) {
2566
+ const modelContext = getWebMCPModelContext();
2567
+ if (!modelContext) return;
2568
+ for (const [name, entry] of [...this.entries]) if (desired.get(name) !== entry.tool) {
2569
+ entry.controller.abort();
2570
+ this.entries.delete(name);
2571
+ }
2572
+ for (const [name, tool] of desired) {
2573
+ if (this.entries.has(name)) continue;
2574
+ if (!tool.description) {
2575
+ this.warnOnce(name, `Skipping WebMCP registration for tool '${name}': WebMCP requires a description.`);
2576
+ continue;
2577
+ }
2578
+ const controller = new AbortController();
2579
+ this.entries.set(name, {
2580
+ tool,
2581
+ controller
2582
+ });
2583
+ modelContext.registerTool(this.buildModelContextTool(tool), { signal: controller.signal }).catch((error) => {
2584
+ var _this$entries$get;
2585
+ if (((_this$entries$get = this.entries.get(name)) === null || _this$entries$get === void 0 ? void 0 : _this$entries$get.controller) !== controller) return;
2586
+ this.entries.delete(name);
2587
+ const message = error instanceof Error ? error.message : String(error);
2588
+ this.warnOnce(name, `WebMCP registration failed for tool '${name}': ${message}`);
2589
+ });
2590
+ }
2591
+ }
2592
+ /**
2593
+ * Build the WebMCP model-context tool for a frontend tool. The `execute`
2594
+ * callback delegates to the frontend tool's own handler, so a browser agent
2595
+ * call runs the same application code as an agent call.
2596
+ */
2597
+ buildModelContextTool(tool) {
2598
+ const annotations = typeof tool.webmcp === "object" ? tool.webmcp.annotations : void 0;
2599
+ return {
2600
+ name: tool.name,
2601
+ description: tool.description,
2602
+ inputSchema: createToolSchema(tool),
2603
+ execute: async (args, options) => {
2604
+ if (!tool.handler) return "";
2605
+ const toolCall = {
2606
+ id: (0, _copilotkit_shared.randomUUID)(),
2607
+ type: "function",
2608
+ function: {
2609
+ name: tool.name,
2610
+ arguments: JSON.stringify(args !== null && args !== void 0 ? args : {})
2611
+ }
2612
+ };
2613
+ return await tool.handler(args, {
2614
+ toolCall,
2615
+ signal: options === null || options === void 0 ? void 0 : options.signal
2616
+ });
2617
+ },
2618
+ ...annotations ? { annotations } : {}
2619
+ };
2620
+ }
2621
+ /** Log `message` once per tool name. Repeated syncs must not spam the log. */
2622
+ warnOnce(name, message) {
2623
+ if (this.warnedNames.has(name)) return;
2624
+ this.warnedNames.add(name);
2625
+ _copilotkit_shared.logger.warn(`[CopilotKit] ${message}`);
2626
+ }
2627
+ };
2628
+
2494
2629
  //#endregion
2495
2630
  //#region src/core/run-handler.ts
2496
2631
  /**
@@ -2527,6 +2662,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2527
2662
  _defineProperty(this, "_disabledToolKeys", /* @__PURE__ */ new Set());
2528
2663
  _defineProperty(this, "_runAbortController", null);
2529
2664
  _defineProperty(this, "_runDepth", 0);
2665
+ _defineProperty(this, "_webmcpRegistry", new WebMCPRegistry());
2530
2666
  _defineProperty(this, "_lastConnectedThreadIdsByAgent", /* @__PURE__ */ new Map());
2531
2667
  _defineProperty(this, "_anonymousAgentIds", /* @__PURE__ */ new WeakMap());
2532
2668
  _defineProperty(this, "_nextAnonymousAgentId", 0);
@@ -2581,6 +2717,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2581
2717
  initialize(tools) {
2582
2718
  this._propTools = [...tools];
2583
2719
  this._cachedMergedTools = null;
2720
+ this.syncWebMCP();
2584
2721
  }
2585
2722
  /**
2586
2723
  * Add a tool to the registry. Survives provider re-syncs ({@link setTools}),
@@ -2594,6 +2731,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2594
2731
  }
2595
2732
  this._hookTools.set(key, tool);
2596
2733
  this._cachedMergedTools = null;
2734
+ this.syncWebMCP();
2597
2735
  }
2598
2736
  /**
2599
2737
  * Remove a tool by name and optionally by agentId
@@ -2605,6 +2743,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2605
2743
  return !(tool.name === id && !tool.agentId);
2606
2744
  });
2607
2745
  this._cachedMergedTools = null;
2746
+ this.syncWebMCP();
2608
2747
  }
2609
2748
  /**
2610
2749
  * Get a tool by name and optionally by agentId.
@@ -2627,6 +2766,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2627
2766
  setTools(tools) {
2628
2767
  this._propTools = [...tools];
2629
2768
  this._cachedMergedTools = null;
2769
+ this.syncWebMCP();
2630
2770
  }
2631
2771
  /**
2632
2772
  * Return the registered A2UI catalog components (readonly).
@@ -3172,6 +3312,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3172
3312
  const key = this.capabilityKey(name, agentId);
3173
3313
  if (enabled) this._disabledToolKeys.delete(key);
3174
3314
  else this._disabledToolKeys.add(key);
3315
+ this.syncWebMCP();
3175
3316
  }
3176
3317
  /** Whether a tool is currently enabled (not overridden off). Defaults true. */
3177
3318
  isToolEnabled(name, agentId) {
@@ -3191,6 +3332,32 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3191
3332
  });
3192
3333
  }
3193
3334
  /**
3335
+ * Reconcile the WebMCP registrations with the current tool registry. Tools
3336
+ * opt in via `webmcp: true` or `webmcp: { annotations }`; the same
3337
+ * availability rules as `buildFrontendTools` apply. WebMCP tools are
3338
+ * page-level, so unlike the agent tool list they are not filtered by
3339
+ * agentId — a name collision across agentIds keeps the first registration.
3340
+ */
3341
+ syncWebMCP() {
3342
+ const desired = /* @__PURE__ */ new Map();
3343
+ const warnedCollisions = /* @__PURE__ */ new Set();
3344
+ for (const tool of this.tools) {
3345
+ if (!tool.webmcp) continue;
3346
+ if (tool.name === WILDCARD_TOOL_NAME) continue;
3347
+ if (tool.available === false || tool.available === "disabled") continue;
3348
+ if (!this.isToolEnabled(tool.name, tool.agentId)) continue;
3349
+ if (desired.has(tool.name)) {
3350
+ if (!warnedCollisions.has(tool.name)) {
3351
+ warnedCollisions.add(tool.name);
3352
+ _copilotkit_shared.logger.warn(`[CopilotKit] Multiple WebMCP tools share the name '${tool.name}'. Only the first registration is exposed to browser agents.`);
3353
+ }
3354
+ continue;
3355
+ }
3356
+ desired.set(tool.name, tool);
3357
+ }
3358
+ this._webmcpRegistry.sync(desired);
3359
+ }
3360
+ /**
3194
3361
  * Create an agent error subscriber
3195
3362
  */
3196
3363
  createAgentErrorSubscriber(agent) {
@@ -3224,36 +3391,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
3224
3391
  }
3225
3392
  };
3226
3393
  /**
3227
- * Empty tool schema constant
3228
- */
3229
- const EMPTY_TOOL_SCHEMA = {
3230
- type: "object",
3231
- properties: {}
3232
- };
3233
- /**
3234
- * Create a JSON schema from a tool's parameters
3235
- */
3236
- function createToolSchema(tool) {
3237
- if (!tool.parameters) return { ...EMPTY_TOOL_SCHEMA };
3238
- const rawSchema = (0, _copilotkit_shared.schemaToJsonSchema)(tool.parameters, { zodToJsonSchema: (schema, options) => (0, zod_to_json_schema.zodToJsonSchema)(schema, options) });
3239
- if (!rawSchema || typeof rawSchema !== "object") return { ...EMPTY_TOOL_SCHEMA };
3240
- const { $schema: _$schema, ...schema } = rawSchema;
3241
- if (typeof schema.type !== "string") schema.type = "object";
3242
- if (typeof schema.properties !== "object" || schema.properties === null) schema.properties = {};
3243
- stripAdditionalProperties(schema);
3244
- return schema;
3245
- }
3246
- function stripAdditionalProperties(schema) {
3247
- if (!schema || typeof schema !== "object") return;
3248
- if (Array.isArray(schema)) {
3249
- schema.forEach(stripAdditionalProperties);
3250
- return;
3251
- }
3252
- const record = schema;
3253
- if (record.additionalProperties !== void 0) delete record.additionalProperties;
3254
- for (const value of Object.values(record)) stripAdditionalProperties(value);
3255
- }
3256
- /**
3257
3394
  * Ensures parsed tool arguments are a plain object.
3258
3395
  * Throws for non-object values so the caller's catch block can emit
3259
3396
  * a structured TOOL_ARGUMENT_PARSE_FAILED error.
@@ -6781,6 +6918,7 @@ exports.RunHandler = RunHandler;
6781
6918
  exports.StateManager = StateManager;
6782
6919
  exports.SuggestionEngine = SuggestionEngine;
6783
6920
  exports.ToolCallStatus = ToolCallStatus;
6921
+ exports.WebMCPRegistry = WebMCPRegistry;
6784
6922
  exports.completePartialMarkdown = completePartialMarkdown;
6785
6923
  exports.createActionGroup = createActionGroup;
6786
6924
  exports.createEffect = createEffect;
@@ -6794,6 +6932,7 @@ exports.emitInspectorViewThread = emitInspectorViewThread;
6794
6932
  exports.emitInspectorViewThreadResult = emitInspectorViewThreadResult;
6795
6933
  exports.empty = empty;
6796
6934
  exports.ensureObjectArgs = ensureObjectArgs;
6935
+ exports.getWebMCPModelContext = getWebMCPModelContext;
6797
6936
  exports.isAbortError = isAbortError;
6798
6937
  exports.isInspectorThreadBridgeEnabled = isInspectorThreadBridgeEnabled;
6799
6938
  exports.isRunCompletionAware = isRunCompletionAware;