UNPKG

@phren/cli

Version:

Knowledge layer for AI agents - CLI, MCP server, and data layer

34 lines (33 loc) • 2.65 kB
import { object } from "./protocol.js"; /** Copilot's transcript reader: public message, tool and usage events, and * the reasoning summary Copilot itself prints under "Thought for Ns". Its * encrypted and opaque reasoning stays out. */ export function visibleCopilotEvent(raw) { if (raw.agentId || raw.ephemeral) return undefined; // What a skill loaded: Copilot answers the `skill` call with only "Skill // loaded", then writes the SKILL.md it put in context as `skill.invoked`. if (raw.type === "skill.invoked") { const data = object(raw.data); if (typeof data.name !== "string" || typeof data.content !== "string") return undefined; return { type: raw.type, timestamp: raw.timestamp, data: { name: data.name.slice(0, 200), content: data.content.slice(0, 65_536), ...(typeof data.description === "string" ? { description: data.description.slice(0, 1_000) } : {}) } }; } if (!["user.message", "assistant.message", "assistant.message_delta", "tool.execution_start", "tool.execution_complete", "assistant.turn_start", "assistant.turn_end", "session.idle", "abort", "session.error", "session.usage_info", "assistant.usage"].includes(String(raw.type))) return undefined; const data = object(raw.data); // Copilot writes its reasoning beside the message three ways: the summary // its terminal shows (`reasoningText`), and encrypted/opaque copies for the // model (`encryptedContent`, `reasoningOpaque`, `reasoningBlocks`). Only the // summary the person already sees in the terminal is exported. // `phase` marks Copilot 1.0.87's final answer (it writes no session.idle); // `success` is false on a failed tool run. const allowed = ["content", "source", "messageId", "deltaContent", "toolName", "toolCallId", "arguments", "result", "error", "success", "phase", "aborted", "reasoningText", "mcpServerName", "mcpToolName", "inputTokens", "outputTokens", "cacheReadTokens"]; return { type: raw.type, timestamp: raw.timestamp, data: Object.fromEntries(Object.entries(data) // The MCP server and tool a call went to (`phren` + `get_tasks` for the // `phren-get_tasks` tool), so the phone draws the server's own card. .filter(([key, value]) => allowed.includes(key) && (key !== "reasoningText" || (raw.type === "assistant.message" && typeof value === "string")) && (!["mcpServerName", "mcpToolName"].includes(key) || (typeof value === "string" && value.length <= 200))) .map(([key, value]) => [key, key === "reasoningText" ? String(value).slice(0, 8_000) : value])) }; }