Files
wehub-resource-sync e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:00:43 +08:00

77 lines
2.1 KiB
TypeScript

import type { StreamEvent } from "./unified-ws";
type MessageWithEvents = {
events?: StreamEvent[];
};
function asRecord(value: unknown): Record<string, unknown> | null {
return value && typeof value === "object" && !Array.isArray(value)
? (value as Record<string, unknown>)
: null;
}
function eventBelongsToTurn(
event: StreamEvent,
turnId?: string | null,
): boolean {
if (!turnId) return true;
return !event.turn_id || event.turn_id === turnId;
}
function askUserPayloadFrom(
event: StreamEvent,
): Record<string, unknown> | null {
const meta = asRecord(event.metadata);
const toolMetadata = asRecord(meta?.tool_metadata);
return asRecord(toolMetadata?.ask_user) ?? asRecord(meta?.ask_user);
}
function askUserToolCallId(event: StreamEvent): string {
const meta = asRecord(event.metadata);
return typeof meta?.tool_call_id === "string" ? meta.tool_call_id.trim() : "";
}
/**
* Returns true when a stream contains an ask_user card that has not yet
* emitted the matching ask_user_resolved progress event.
*/
export function hasPendingAskUser(
events: StreamEvent[] | undefined,
turnId?: string | null,
): boolean {
const pending = new Set<string>();
let anonymousCount = 0;
for (const event of events ?? []) {
if (!eventBelongsToTurn(event, turnId)) continue;
const meta = asRecord(event.metadata);
if (event.type === "tool_result" && askUserPayloadFrom(event)) {
const toolCallId = askUserToolCallId(event);
pending.add(toolCallId ? `id:${toolCallId}` : `anon:${anonymousCount++}`);
continue;
}
if (event.type === "progress" && meta?.ask_user_resolved === true) {
const resolvedId =
typeof meta.ask_user_tool_call_id === "string"
? meta.ask_user_tool_call_id.trim()
: "";
if (resolvedId) {
pending.delete(`id:${resolvedId}`);
} else {
pending.clear();
}
}
}
return pending.size > 0;
}
export function hasPendingAskUserInMessages(
messages: MessageWithEvents[],
turnId?: string | null,
): boolean {
return messages.some((message) => hasPendingAskUser(message.events, turnId));
}