@gajae-code/agent-core 0.12.19 → 0.12.21

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/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.12.21] - 2026-08-09
6
+
7
+ ## [0.12.20] - 2026-08-09
8
+
5
9
  ## [0.12.19] - 2026-08-08
6
10
 
7
11
  ## [0.12.18] - 2026-08-08
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/agent-core",
4
- "version": "0.12.19",
4
+ "version": "0.12.21",
5
5
  "description": "General-purpose agent with transport abstraction, state management, and attachment support",
6
6
  "homepage": "https://gajae-code.com",
7
7
  "author": "Yeachan-Heo and Gajae Code Contributors",
@@ -32,9 +32,9 @@
32
32
  "fmt": "biome format --write ."
33
33
  },
34
34
  "dependencies": {
35
- "@gajae-code/ai": "0.12.19",
36
- "@gajae-code/natives": "0.12.19",
37
- "@gajae-code/utils": "0.12.19",
35
+ "@gajae-code/ai": "0.12.21",
36
+ "@gajae-code/natives": "0.12.21",
37
+ "@gajae-code/utils": "0.12.21",
38
38
  "@opentelemetry/api": "^1.9.0"
39
39
  },
40
40
  "devDependencies": {
package/src/agent-loop.ts CHANGED
@@ -2507,66 +2507,35 @@ function toolCallNames(tool: { name: string; customWireName?: string }): string[
2507
2507
 
2508
2508
  /**
2509
2509
  * Wire name of the tool-discovery tool. Sessions that hide discoverable
2510
- * built-ins expose it under this name, or under a bridge alias of it.
2510
+ * built-ins expose it under exactly that name.
2511
2511
  */
2512
2512
  const TOOL_DISCOVERY_NAME = "search_tool_bm25";
2513
2513
 
2514
2514
  /**
2515
- * Split an MCP bridge namespace off a call name so it can be compared against
2516
- * the tool it actually denotes. Bridges expose tools as `mcp__<server>_<tool>`,
2517
- * and proxied bridges add a per-session instance segment
2518
- * (`mcp__<server>__<instance>_<tool>`). A name the model replayed from earlier
2519
- * context therefore differs from the live registry only in that segment.
2515
+ * Active tool a call name dispatches to. Tools emitted via OpenAI's custom-tool
2516
+ * path (e.g. `apply_patch` on GPT-5) come back under their wire-level name,
2517
+ * which may differ from the harness-internal `name`. Match on either, preferring
2518
+ * `name` for determinism if both somehow collide.
2520
2519
  */
2521
- function parseToolCallName(name: string): { server?: string; base: string } {
2522
- const namespace = /^mcp__([^_]+)(?:__[^_]+)?_/.exec(name);
2523
- if (!namespace) return { base: name };
2524
- return { server: namespace[1], base: name.slice(namespace[0].length) };
2525
- }
2526
-
2527
- /**
2528
- * Call names of active tools that denote the same tool as an unresolved call
2529
- * name. Two servers can expose the same tool name, so a namespaced call is only
2530
- * matched against its own server or against an unnamespaced tool.
2531
- */
2532
- function findToolCallNameAliases(
2520
+ function findActiveTool<T extends { name: string; customWireName?: string }>(
2521
+ tools: ReadonlyArray<T> | undefined,
2533
2522
  callName: string,
2534
- tools: ReadonlyArray<{ name: string; customWireName?: string }> | undefined,
2535
- limit = 3,
2536
- ): string[] {
2537
- const target = parseToolCallName(callName);
2538
- if (target.base.length === 0) return [];
2539
- const aliases: string[] = [];
2540
- for (const tool of tools ?? []) {
2541
- for (const candidate of toolCallNames(tool)) {
2542
- if (candidate === callName) continue;
2543
- const parsed = parseToolCallName(candidate);
2544
- if (parsed.base !== target.base) continue;
2545
- if (parsed.server !== undefined && target.server !== undefined && parsed.server !== target.server) continue;
2546
- if (aliases.includes(candidate)) continue;
2547
- aliases.push(candidate);
2548
- if (aliases.length === limit) return aliases;
2549
- }
2550
- }
2551
- return aliases;
2523
+ ): T | undefined {
2524
+ return (
2525
+ tools?.find(tool => tool.name === callName) ??
2526
+ tools?.find(tool => tool.customWireName !== undefined && tool.customWireName === callName)
2527
+ );
2552
2528
  }
2553
2529
 
2554
2530
  /**
2555
- * Resolve how tool discovery is actually callable in this session. Assuming the
2556
- * bare `search_tool_bm25` literal both drops the hint when the discovery tool is
2557
- * bridged and, worse, would name a second non-callable tool if emitted anyway.
2531
+ * Whether tool discovery is callable in this session. A namespaced lookalike
2532
+ * proves nothing: `mcp__srv__x_search_tool_bm25` reads equally well as a bridged
2533
+ * discovery tool and as that server's own `x_search_tool_bm25`, and the registry
2534
+ * cannot tell the two apart. Naming the wrong one sends the model at a tool it
2535
+ * never asked for, so only the literal call name counts.
2558
2536
  */
2559
- function findToolDiscoveryCallName(
2560
- tools: ReadonlyArray<{ name: string; customWireName?: string }> | undefined,
2561
- ): string | undefined {
2562
- let bridged: string | undefined;
2563
- for (const tool of tools ?? []) {
2564
- for (const candidate of toolCallNames(tool)) {
2565
- if (candidate === TOOL_DISCOVERY_NAME) return candidate;
2566
- if (bridged === undefined && parseToolCallName(candidate).base === TOOL_DISCOVERY_NAME) bridged = candidate;
2567
- }
2568
- }
2569
- return bridged;
2537
+ function isToolDiscoveryCallable(tools: ReadonlyArray<{ name: string; customWireName?: string }> | undefined): boolean {
2538
+ return (tools ?? []).some(tool => toolCallNames(tool).includes(TOOL_DISCOVERY_NAME));
2570
2539
  }
2571
2540
 
2572
2541
  /**
@@ -2615,13 +2584,7 @@ async function executeToolCalls(
2615
2584
 
2616
2585
  const records = toolCalls.map(toolCall => ({
2617
2586
  toolCall,
2618
- // Tools emitted via OpenAI's custom-tool path (e.g. `apply_patch` on GPT-5)
2619
- // come back under their wire-level name, which may differ from the
2620
- // harness-internal `name`. Match on either, preferring `name` for
2621
- // determinism if both somehow collide.
2622
- tool:
2623
- tools?.find(t => t.name === toolCall.name) ??
2624
- tools?.find(t => t.customWireName !== undefined && t.customWireName === toolCall.name),
2587
+ tool: findActiveTool(tools, toolCall.name),
2625
2588
  args: toolCall.arguments as Record<string, unknown>,
2626
2589
  started: false,
2627
2590
  result: undefined as AgentToolResult<any> | undefined,
@@ -2769,21 +2732,19 @@ async function executeToolCalls(
2769
2732
  // the tool and retry instead of giving up on the capability. The
2770
2733
  // base wording stays byte-for-byte stable for downstream consumers;
2771
2734
  // the period and hint are appended only when discovery is callable.
2735
+ // The call name is echoed verbatim and no other tool is named or
2736
+ // dispatched to: `mcp__<server>__<x>_<base>` reads equally well as a
2737
+ // stale bridge instance segment in front of `base` and as that
2738
+ // server's own two-segment `<x>_<base>`, and the registry only ever
2739
+ // proves the live name, never the one the model sent. Running or
2740
+ // naming that guess hits a tool the model never asked for, which is
2741
+ // worse than the dead end it would replace.
2772
2742
  const base = `Tool ${toolCall.name} not found`;
2773
- const hints: string[] = [];
2774
- const aliases = findToolCallNameAliases(toolCall.name, tools);
2775
- if (aliases.length > 0) {
2776
- hints.push(
2777
- `It is active as ${aliases.map(name => `\`${name}\``).join(" or ")} — call that name instead.`,
2778
- );
2779
- }
2780
- const discoveryCallName = findToolDiscoveryCallName(tools);
2781
- if (discoveryCallName !== undefined) {
2782
- hints.push(
2783
- `If you are unsure whether this tool exists or how to use it, call \`${discoveryCallName}\` to discover and activate the matching tool, then retry.`,
2784
- );
2785
- }
2786
- throw new Error(hints.length > 0 ? `${base}. ${hints.join(" ")}` : base);
2743
+ throw new Error(
2744
+ isToolDiscoveryCallable(tools)
2745
+ ? `${base}. If you are unsure whether this tool exists or how to use it, call \`${TOOL_DISCOVERY_NAME}\` to discover and activate the matching tool, then retry.`
2746
+ : base,
2747
+ );
2787
2748
  }
2788
2749
 
2789
2750
  let effectiveArgs: Record<string, unknown>;