@cubicecho/agent-core 2.0.7 → 2.0.8

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.
@@ -232,6 +232,84 @@ function mergeRootUnion(out) {
232
232
  }
233
233
  }
234
234
  }
235
+ /**
236
+ * Every `$ref` string anywhere under a node, walked as arbitrary JSON rather than as a schema.
237
+ *
238
+ * The keyword-aware walk `strip` does is the wrong way round for this one. Missing a pointer
239
+ * here means deleting a definition that something still refers to, which breaks the schema;
240
+ * finding one that was really a string sitting in a `default` or an `enum` costs a definition
241
+ * that outlives its last real reference. So this errs the cheap way and reads every position.
242
+ */
243
+ function collectRefs(node, into) {
244
+ if (Array.isArray(node)) {
245
+ for (const item of node)
246
+ collectRefs(item, into);
247
+ return;
248
+ }
249
+ if (!isObject(node))
250
+ return;
251
+ for (const [key, value] of Object.entries(node)) {
252
+ if (key === "$ref" && typeof value === "string")
253
+ into.add(value);
254
+ else
255
+ collectRefs(value, into);
256
+ }
257
+ }
258
+ /**
259
+ * Drops the `definitions` and `$defs` entries that nothing points at any more.
260
+ *
261
+ * The rewrites above delete whole subtrees — a root combinator once its branches are folded in,
262
+ * every sibling of a `$ref`, the branch of a union that was only ever `null` — and the pointers
263
+ * go with them while the pools they named stay behind. On a real Gmail or filesystem schema
264
+ * those pools are most of the parameter bytes, re-sent for every tool on every turn of every
265
+ * run, describing shapes the request no longer mentions anywhere.
266
+ *
267
+ * Reachability rather than a single pass, because a definition that is still pointed at can
268
+ * name another; a cycle among them terminates on the `has` check, whether or not anything
269
+ * outside it still refers in.
270
+ */
271
+ function pruneDefs(out) {
272
+ const pools = ["definitions", "$defs"].filter((key) => isObject(out[key]));
273
+ if (!pools.length)
274
+ return;
275
+ const live = {};
276
+ const visit = (node) => {
277
+ const pointers = new Set();
278
+ collectRefs(node, pointers);
279
+ for (const pointer of pointers) {
280
+ const target = LOCAL_POINTER.exec(pointer);
281
+ if (!target)
282
+ continue;
283
+ const [, poolKey, name] = target;
284
+ const pool = out[poolKey];
285
+ if (!isObject(pool) || !(name in pool))
286
+ continue;
287
+ live[poolKey] ??= new Set();
288
+ const names = live[poolKey];
289
+ if (names.has(name))
290
+ continue;
291
+ names.add(name);
292
+ visit(pool[name]);
293
+ }
294
+ };
295
+ // The pools themselves are not roots: a definition is reached from the schema body, or by
296
+ // another definition that was, or not at all.
297
+ const body = { ...out };
298
+ for (const key of pools)
299
+ delete body[key];
300
+ visit(body);
301
+ for (const key of pools) {
302
+ const names = live[key];
303
+ if (!names?.size) {
304
+ delete out[key];
305
+ continue;
306
+ }
307
+ const pool = out[key];
308
+ if (names.size === Object.keys(pool).length)
309
+ continue;
310
+ out[key] = Object.fromEntries(Object.entries(pool).filter(([name]) => names.has(name)));
311
+ }
312
+ }
235
313
  /**
236
314
  * A required argument that is not in `properties` is one no caller can supply and no strict
237
315
  * validator will accept. Anything the rewrites above removed, `required` may still name.
@@ -259,6 +337,7 @@ function sanitizeParameters(parameters) {
259
337
  if (!isObject(out.properties))
260
338
  out.properties = {};
261
339
  pruneRequired(out);
340
+ pruneDefs(out);
262
341
  return out;
263
342
  }
264
343
  /** Rewrites one tool's parameters, leaving a non-function tool alone. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cubicecho/agent-core",
3
- "version": "2.0.7",
3
+ "version": "2.0.8",
4
4
  "description": "The endpoint-agnostic half of an OpenAI-compatible agent loop: tool-schema compatibility, on-demand tool loading, one-shot side tasks, run events, and a pooled client.",
5
5
  "keywords": [
6
6
  "openai",