Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/provider-acp/src/server/runtimeModel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1505,7 +1505,12 @@ describe("extractMcpToolCallIdentity", () => {
't3-code__delegate_task: {"mode":"async"}',
"delegate_task_t3-code",
"t3-code/delegate_task",
"delegate_task",
'delegate_task: {"mode":"async"}',
// Kimi retitles on every streamed args chunk with the raw partial JSON,
// and middle-truncates args longer than 50 characters.
'delegate_task: {"mode":"async","task":"Ru',
'delegate_task: {"mode":"async","task":"Ru...cho child-result-42\'"}',
]) {
const toolCall = toolCallFromUpdate({
sessionUpdate: "tool_call",
Expand All @@ -1521,6 +1526,18 @@ describe("extractMcpToolCallIdentity", () => {
}
});

it("does not claim an agent's own tool that shares a T3 tool name", () => {
// Hermes' built-in delegate_task, titled "<tool>: <description>" like all its calls.
const toolCall = toolCallFromUpdate({
sessionUpdate: "tool_call",
toolCallId: "own-delegate-1",
kind: "execute",
title: "delegate_task: 1 tasks: Run the terminal command 'echo child-result-42'",
status: "pending",
});
expect(extractMcpToolCallIdentity(toolCall)).toBeUndefined();
});

it("recovers T3 identity from goose _meta despite LLM-rewritten titles", () => {
// goose enriches titles asynchronously, so only _meta.goose.toolCall is
// stable; shape from crates/goose/src/acp/server/tool_calls/conversion.rs.
Expand Down
10 changes: 7 additions & 3 deletions packages/provider-acp/src/server/runtimeModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1062,10 +1062,14 @@ const T3_MCP_TITLE_SUFFIX_CALL =

/**
* glm-acp-agent and Kimi CLI register injected MCP tools under their bare
* names; Kimi additionally appends ": <raw args json>". Safe only because the
* match is gated on the known T3 tool inventory.
* names; Kimi additionally appends ": <raw args json>". The T3 inventory gate
* alone is not enough: an agent can have its own tool of the same name (Hermes
* has a built-in `delegate_task`) and title it "<tool>: <description>", so only
* a bare name or a JSON object after the colon counts. The match stops at the
* opening brace because Kimi retitles on every streamed args chunk, so the
* JSON is often still incomplete.
*/
const T3_MCP_BARE_TITLE_CALL = /^(?<tool>[A-Za-z0-9_]+)(?::\s|$)/;
const T3_MCP_BARE_TITLE_CALL = /^(?<tool>[A-Za-z0-9_]+)(?::\s*\{|$)/;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* ACP has no typed MCP tool-call item, so agents surface MCP calls in agent-specific shapes:
Expand Down
Loading