forked from bai/curriculum-project-hub
feat: streaming agent card with tool-use trace, reasoning panel, and tool results
Replace text-only PatchableTextStream with a unified StreamingAgentCard that renders the full agent run lifecycle in a single Feishu interactive card: - Tool-use panel: collapsible, shows each tool call with status (running/ success/error), input summary, and result/error in code blocks - Reasoning panel: collapsible, streams thinking text inline then collapses on completion - Answer text: rendered via native Feishu markdown component (no post-row round-trip) Runner now captures previously-discarded SDK events: - thinking_delta → reasoning stream - tool_use id + input on content_block_start / assistant message - tool_result from user messages (was entirely unhandled) New files: - card/trace-store.ts: per-run in-memory tool-use trace with secret redaction - card/builder.ts: Feishu card JSON builder (tool panel + reasoning + text) - card/streaming-card.ts: StreamingAgentCard controller (400ms throttled) client.ts: add sendCard/patchCard raw JSON primitives
This commit is contained in:
@@ -277,6 +277,38 @@ export async function patchTextMessage(rt: FeishuRuntime, messageId: string, tex
|
||||
}
|
||||
}
|
||||
|
||||
/** Send a raw interactive card (arbitrary JSON). Returns message_id on success. */
|
||||
export async function sendCard(rt: FeishuRuntime, chatId: string, card: Record<string, unknown>): Promise<string | null> {
|
||||
const client = rt.client as unknown as {
|
||||
im: { v1: { message: { create: (p: unknown) => Promise<MessageCreateResponse> } } };
|
||||
};
|
||||
try {
|
||||
const res = await client.im.v1.message.create({
|
||||
params: { receive_id_type: "chat_id" },
|
||||
data: { receive_id: chatId, msg_type: "interactive", content: JSON.stringify(card) },
|
||||
});
|
||||
return messageIdFromResponse(res);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Patch an interactive card in-place with raw JSON. */
|
||||
export async function patchCard(rt: FeishuRuntime, messageId: string, card: Record<string, unknown>): Promise<void> {
|
||||
const client = rt.client as unknown as {
|
||||
im: { v1: { message: { patch: (p: unknown) => Promise<unknown> } } };
|
||||
};
|
||||
try {
|
||||
await client.im.v1.message.patch({
|
||||
path: { message_id: messageId },
|
||||
params: { msg_type: "interactive" },
|
||||
data: { content: JSON.stringify(card) },
|
||||
});
|
||||
} catch (e) {
|
||||
rt.logger.warn({ messageId, err: e instanceof Error ? e.message : String(e) }, "patchCard failed");
|
||||
}
|
||||
}
|
||||
|
||||
/** Send an approval card with buttons. Returns message_id on success. */
|
||||
export async function sendApprovalCard(
|
||||
rt: FeishuRuntime,
|
||||
|
||||
Reference in New Issue
Block a user