feat: reconcile tool result echoes
This commit is contained in:
@@ -35,6 +35,7 @@ export type TaskInteractionHandlers = {
|
||||
export class TrustedDOMRendererContext {
|
||||
private thinking: HTMLElement | undefined;
|
||||
private readonly userRows = new Map<string, HTMLElement>();
|
||||
private readonly toolCards = new Map<string, { card: HTMLElement; status: HTMLElement; controls: readonly (HTMLButtonElement | HTMLInputElement | HTMLTextAreaElement)[] }>();
|
||||
private readonly taskCards = new Map<string, { card: HTMLElement; title: HTMLElement; progress: HTMLProgressElement; status: HTMLElement; cancel: HTMLButtonElement }>();
|
||||
private readonly expirationTimers = new Set<number>();
|
||||
|
||||
@@ -48,6 +49,7 @@ export class TrustedDOMRendererContext {
|
||||
public reset(): void {
|
||||
this.thinking = undefined;
|
||||
this.userRows.clear();
|
||||
this.toolCards.clear();
|
||||
this.taskCards.clear();
|
||||
for (const timer of this.expirationTimers) window.clearTimeout(timer);
|
||||
this.expirationTimers.clear();
|
||||
@@ -107,11 +109,15 @@ export class TrustedDOMRendererContext {
|
||||
}
|
||||
|
||||
public renderToolResult(item: Extract<ConversationItem, { kind: "tool-result" }>): void {
|
||||
this.renderSystemMessage(`收到工具调用结果(${item.result.call_id} · ${item.result.status})。`);
|
||||
const entry = this.toolCards.get(item.result.call_id);
|
||||
if (!entry) return;
|
||||
this.updateToolCardFinal(entry, item.result.status, item.result.error);
|
||||
}
|
||||
|
||||
public renderToolCancel(item: Extract<ConversationItem, { kind: "tool-cancel" }>): void {
|
||||
this.renderSystemMessage(`工具调用已取消(${item.cancel.call_id})。`);
|
||||
const entry = this.toolCards.get(item.cancel.call_id);
|
||||
if (!entry) return;
|
||||
this.updateToolCardFinal(entry, "cancelled", item.cancel.reason);
|
||||
}
|
||||
|
||||
public renderSurface(item: Extract<ConversationItem, { kind: "surface" }>): void {
|
||||
@@ -286,6 +292,7 @@ export class TrustedDOMRendererContext {
|
||||
card.append(heading, detail, actions);
|
||||
if (group.mode === "multi-choice") card.append(submitButton);
|
||||
card.append(status);
|
||||
this.toolCards.set(call.call_id, { card, status, controls: buttons });
|
||||
this.messages.append(card);
|
||||
this.scheduleExpiry(card, status, call, buttons);
|
||||
this.scrollLatest();
|
||||
@@ -323,6 +330,7 @@ export class TrustedDOMRendererContext {
|
||||
});
|
||||
actions.append(approve, cancel);
|
||||
card.append(heading, detail, actions, status);
|
||||
this.toolCards.set(call.call_id, { card, status, controls: buttons });
|
||||
this.messages.append(card);
|
||||
this.scheduleExpiry(card, status, call, buttons);
|
||||
this.scrollLatest();
|
||||
@@ -387,6 +395,7 @@ export class TrustedDOMRendererContext {
|
||||
actions.append(submit, cancel);
|
||||
nativeForm.append(errors, actions);
|
||||
card.append(heading, detail, nativeForm, status);
|
||||
this.toolCards.set(call.call_id, { card, status, controls });
|
||||
this.messages.append(card);
|
||||
this.scheduleExpiry(card, status, call, controls);
|
||||
this.scrollLatest();
|
||||
@@ -432,6 +441,18 @@ export class TrustedDOMRendererContext {
|
||||
status.textContent = "该交互已结束或已提交";
|
||||
}
|
||||
|
||||
private updateToolCardFinal(entry: { card: HTMLElement; status: HTMLElement; controls: readonly (HTMLButtonElement | HTMLInputElement | HTMLTextAreaElement)[] }, finalStatus: "completed" | "failed" | "cancelled" | "expired", error?: string): void {
|
||||
entry.card.dataset.status = finalStatus;
|
||||
entry.status.textContent = finalStatus === "completed"
|
||||
? "已完成"
|
||||
: finalStatus === "cancelled"
|
||||
? "已取消"
|
||||
: finalStatus === "expired"
|
||||
? "该交互已过期"
|
||||
: `处理失败${error ? `:${error}` : ""}`;
|
||||
for (const control of entry.controls) control.disabled = true;
|
||||
}
|
||||
|
||||
private showThinking(detail: string): void {
|
||||
this.thinking?.remove();
|
||||
this.thinking = this.appendBubble("agent", detail);
|
||||
|
||||
Reference in New Issue
Block a user