UNPKG

@tanstack/ai

Version:

Type-safe TypeScript AI SDK for streaming chat, tool calling, agents, structured outputs, and multimodal generation.

131 lines (130 loc) • 4.48 kB
//#region src/utilities/subagent-wire.ts function isRecord(value) { return typeof value === "object" && value !== null && !Array.isArray(value); } function wireSubagentRunId(message) { if (!isRecord(message) || "parts" in message) return void 0; const id = message.subagentRunId; return typeof id === "string" && id !== "" ? id : void 0; } function wireSubagentInfo(message) { if (!isRecord(message) || !isRecord(message.metadata)) return void 0; const tanstack = message.metadata.tanstack; if (!isRecord(tanstack) || !isRecord(tanstack.subagent)) return void 0; const { name, status, error, interruptIds } = tanstack.subagent; if (typeof name !== "string" || !isStatus(status)) return void 0; const { description, parentSubagentRunId, parentToolCallId, metadata, placeholder } = tanstack.subagent; return { name, status, ...typeof description === "string" && { description }, ...isRecord(error) && typeof error.message === "string" && { error: { message: error.message, ...typeof error.code === "string" && { code: error.code } } }, ...Array.isArray(interruptIds) && { interruptIds: interruptIds.filter((id) => typeof id === "string") }, ...typeof parentSubagentRunId === "string" && { parentSubagentRunId }, ...typeof parentToolCallId === "string" && { parentToolCallId }, ...isRecord(metadata) && { metadata }, ...placeholder === true && { placeholder: true } }; } function isStatus(value) { return value === "running" || value === "finished" || value === "error" || value === "suspended"; } function untag(message) { if (!isRecord(message)) return message; const { subagentRunId: _id, ...rest } = message; const metadata = isRecord(rest.metadata) ? rest.metadata : void 0; const tanstack = isRecord(metadata?.tanstack) ? metadata.tanstack : void 0; if (!metadata || !tanstack || !("subagent" in tanstack)) return rest; const { subagent: _info, ...tanstackRest } = tanstack; return { ...rest, metadata: { ...metadata, tanstack: tanstackRest } }; } /** * Split wire messages into the parent's own messages and one group per direct * child. A nested child's messages stay inside its parent's group. */ function splitSubagentWire(messages) { const parentOf = /* @__PURE__ */ new Map(); for (const message of messages) { const id = wireSubagentRunId(message); if (id !== void 0 && !parentOf.has(id)) parentOf.set(id, wireSubagentInfo(message)?.parentSubagentRunId); } const rootOf = (id) => { let current = id; for (let depth = 0; depth < 64; depth++) { const parent = parentOf.get(current); if (parent === void 0 || !parentOf.has(parent)) return current; current = parent; } return current; }; const top = []; const groups = /* @__PURE__ */ new Map(); for (const message of messages) { const id = wireSubagentRunId(message); if (id === void 0) { top.push(message); continue; } const root = rootOf(id); let group = groups.get(root); if (!group) { group = { id: root, info: { name: "subagent", status: "finished" }, messages: [], hostIndex: top.length - 1 }; groups.set(root, group); } if (id !== root) { group.messages.push(message); continue; } const info = wireSubagentInfo(message); if (info) group.info = info; if (!info?.placeholder) group.messages.push(untag(message)); } return { top, groups: [...groups.values()] }; } /** Text a child wrote, for the parent model and for a later child. */ function subagentWireText(messages) { const blocks = []; for (const message of messages) { if (!isRecord(message) || message.role !== "assistant") continue; if (wireSubagentRunId(message) !== void 0) continue; const content = message.content; if (typeof content === "string" && content.trim() !== "") blocks.push(content.trim()); } const nested = splitSubagentWire(messages).groups; for (const group of nested) { const text = subagentWireText(group.messages); if (text !== "") blocks.push(`${group.info.name}:\n${text}`); } return blocks.join("\n\n"); } /** * The id of the parent assistant message that hosts a routed turn's cards. * The persistence recorder writes that message, and a handoff run passes the * same id so the stored thread keeps one copy. */ function subagentHostMessageId(runId) { return `assistant:${runId}`; } //#endregion export { splitSubagentWire, subagentHostMessageId, subagentWireText, wireSubagentInfo, wireSubagentRunId }; //# sourceMappingURL=subagent-wire.js.map