@gajae-code/agent-core 0.12.18 → 0.12.20
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 +4 -0
- package/package.json +4 -4
- package/src/agent-loop.ts +32 -71
package/CHANGELOG.md
CHANGED
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.
|
|
4
|
+
"version": "0.12.20",
|
|
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.
|
|
36
|
-
"@gajae-code/natives": "0.12.
|
|
37
|
-
"@gajae-code/utils": "0.12.
|
|
35
|
+
"@gajae-code/ai": "0.12.20",
|
|
36
|
+
"@gajae-code/natives": "0.12.20",
|
|
37
|
+
"@gajae-code/utils": "0.12.20",
|
|
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
|
|
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
|
-
*
|
|
2516
|
-
*
|
|
2517
|
-
*
|
|
2518
|
-
*
|
|
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
|
|
2522
|
-
|
|
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
|
-
|
|
2535
|
-
|
|
2536
|
-
)
|
|
2537
|
-
|
|
2538
|
-
|
|
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
|
-
*
|
|
2556
|
-
*
|
|
2557
|
-
*
|
|
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
|
|
2560
|
-
tools
|
|
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
|
-
|
|
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
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
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>;
|