fix(hub): render Feishu checklist from TaskCreate/TaskUpdate

Headless Claud agents expose TaskCreate/TaskUpdate rather than TodoWrite.
Fold those tool events into the live card checklist, pass real tool names
and inputs through tool-result, and hide Task* noise once the panel is up.
This commit is contained in:
2026-07-23 23:21:34 +08:00
parent 326224b778
commit 74f5c4a02e
6 changed files with 282 additions and 27 deletions
+11 -3
View File
@@ -47,7 +47,7 @@ export type StreamEvent =
| { readonly type: "thinking-delta"; readonly text: string }
| { readonly type: "tool-start"; readonly toolName: string; readonly toolUseId: string }
| { readonly type: "tool-end"; readonly toolName: string; readonly toolUseId: string; readonly input: unknown; readonly durationMs?: number }
| { readonly type: "tool-result"; readonly toolUseId: string; readonly toolName: string; readonly result: string; readonly isError: boolean; readonly durationMs?: number }
| { readonly type: "tool-result"; readonly toolUseId: string; readonly toolName: string; readonly result: string; readonly isError: boolean; readonly input?: unknown; readonly durationMs?: number }
| { readonly type: "finish" };
export type StreamCallback = (event: StreamEvent) => void;
@@ -232,8 +232,9 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
options,
});
// Track tool start timestamps for duration calculation
// Track tool start timestamps and names/inputs for duration + tool-result attribution.
const toolStartTimestamps = new Map<string, number>();
const toolMetaByUseId = new Map<string, { readonly name: string; readonly input: unknown }>();
for await (const message of conversation) {
switch (message.type) {
@@ -254,6 +255,10 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
if (evt.type === "content_block_start" && evt.content_block.type === "tool_use") {
const toolUseId = evt.content_block.id;
toolStartTimestamps.set(toolUseId, Date.now());
toolMetaByUseId.set(toolUseId, {
name: evt.content_block.name,
input: undefined,
});
onStream?.({ type: "tool-start", toolName: evt.content_block.name, toolUseId });
}
if (evt.type === "content_block_stop") {
@@ -275,6 +280,7 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
const durationMs = toolStartTimestamps.has(block.id)
? Date.now() - (toolStartTimestamps.get(block.id) ?? 0)
: undefined;
toolMetaByUseId.set(block.id, { name: block.name, input: block.input });
onStream?.({
type: "tool-end",
toolName: block.name,
@@ -308,12 +314,14 @@ export async function runAgent(req: RunRequest): Promise<RunResult> {
const isError = block.is_error === true;
const resultText = extractToolResultText(block.content);
const durationMs = toolStartTimestamps.get(toolUseId);
const meta = toolMetaByUseId.get(toolUseId);
onStream?.({
type: "tool-result",
toolUseId,
toolName: toolUseId,
toolName: meta?.name ?? toolUseId,
result: resultText,
isError,
...(meta?.input !== undefined ? { input: meta.input } : {}),
...(durationMs !== undefined ? { durationMs: Date.now() - durationMs } : {}),
});
}