Files
tkmind_go/ui/goose2/src/shared/api/acpToolCallIdentity.ts
T
Taylor Ho dd95b7bb85 feat(chat): group consecutive tool calls into one summarized chain card (#8995)
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
Co-authored-by: Matt Toohey <contact@matttoohey.com>
2026-05-05 18:37:13 +00:00

69 lines
1.9 KiB
TypeScript

import type { SessionUpdate } from "@agentclientprotocol/sdk";
export interface ToolCallIdentity {
toolName?: string;
extensionName?: string;
}
export interface ToolChainSummary {
summary: string;
count: number;
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
}
export function getToolCallIdentity(update: SessionUpdate): ToolCallIdentity {
if (!isRecord(update._meta)) {
return {};
}
const goose = update._meta.goose;
if (!isRecord(goose)) {
return {};
}
const toolCall = isRecord(goose.mcpApp)
? goose.mcpApp
: isRecord(goose.toolCall)
? goose.toolCall
: null;
if (!toolCall) return {};
return {
...(typeof toolCall.toolName === "string"
? { toolName: toolCall.toolName }
: {}),
...(typeof toolCall.extensionName === "string"
? { extensionName: toolCall.extensionName }
: {}),
};
}
/**
* Extract a chain summary from `_meta.goose.toolChainSummary` of a tool-call
* SessionUpdate. Returns `undefined` when the meta is missing, malformed, or
* carries a non-positive count.
*
* The server attaches this to the FIRST tool call in a multi-tool chain once
* every step has completed; replays after reload re-emit it on the initial
* `ToolCall` notification so the chain header is correct on first paint.
*/
export function getToolChainSummary(
update: SessionUpdate,
): ToolChainSummary | undefined {
if (!isRecord(update._meta)) return undefined;
const goose = update._meta.goose;
if (!isRecord(goose)) return undefined;
const chain = goose.toolChainSummary;
if (!isRecord(chain)) return undefined;
const summary = chain.summary;
const count = chain.count;
if (typeof summary !== "string" || summary.length === 0) return undefined;
if (typeof count !== "number" || !Number.isFinite(count) || count <= 0) {
return undefined;
}
return { summary, count: Math.trunc(count) };
}