@runtypelabs/persona 1.41.0 → 1.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -1
- package/dist/index.cjs +30 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +307 -1
- package/dist/index.d.ts +307 -1
- package/dist/index.global.js +71 -71
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +30 -30
- package/dist/index.js.map +1 -1
- package/dist/widget.css +185 -0
- package/package.json +2 -1
- package/src/client.test.ts +645 -0
- package/src/client.ts +373 -0
- package/src/components/event-stream-view.test.ts +1233 -0
- package/src/components/event-stream-view.ts +1179 -0
- package/src/index.ts +19 -1
- package/src/plugins/types.ts +34 -1
- package/src/runtime/init.ts +5 -0
- package/src/session.ts +104 -1
- package/src/styles/widget.css +185 -0
- package/src/types.ts +252 -0
- package/src/ui.ts +281 -4
- package/src/utils/event-stream-buffer.test.ts +268 -0
- package/src/utils/event-stream-buffer.ts +112 -0
- package/src/utils/event-stream-capture.test.ts +539 -0
- package/src/utils/event-stream-controller.test.ts +445 -0
- package/src/utils/event-stream-store.test.ts +181 -0
- package/src/utils/event-stream-store.ts +182 -0
- package/src/utils/virtual-scroller.test.ts +449 -0
- package/src/utils/virtual-scroller.ts +151 -0
package/src/client.ts
CHANGED
|
@@ -6,10 +6,12 @@ import {
|
|
|
6
6
|
AgentWidgetContextProvider,
|
|
7
7
|
AgentWidgetRequestMiddleware,
|
|
8
8
|
AgentWidgetRequestPayload,
|
|
9
|
+
AgentWidgetAgentRequestPayload,
|
|
9
10
|
AgentWidgetCustomFetch,
|
|
10
11
|
AgentWidgetSSEEventParser,
|
|
11
12
|
AgentWidgetHeadersFunction,
|
|
12
13
|
AgentWidgetSSEEventResult as _AgentWidgetSSEEventResult,
|
|
14
|
+
AgentExecutionState,
|
|
13
15
|
ClientSession,
|
|
14
16
|
ClientInitResponse,
|
|
15
17
|
ClientChatRequest,
|
|
@@ -79,6 +81,8 @@ function getParserFromType(parserType?: "plain" | "json" | "regex-json" | "xml")
|
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
export type SSEEventCallback = (eventType: string, payload: unknown) => void;
|
|
85
|
+
|
|
82
86
|
export class AgentWidgetClient {
|
|
83
87
|
private readonly apiUrl: string;
|
|
84
88
|
private readonly headers: Record<string, string>;
|
|
@@ -89,6 +93,7 @@ export class AgentWidgetClient {
|
|
|
89
93
|
private readonly customFetch?: AgentWidgetCustomFetch;
|
|
90
94
|
private readonly parseSSEEvent?: AgentWidgetSSEEventParser;
|
|
91
95
|
private readonly getHeaders?: AgentWidgetHeadersFunction;
|
|
96
|
+
private onSSEEvent?: SSEEventCallback;
|
|
92
97
|
|
|
93
98
|
// Client token mode properties
|
|
94
99
|
private clientSession: ClientSession | null = null;
|
|
@@ -110,6 +115,13 @@ export class AgentWidgetClient {
|
|
|
110
115
|
this.getHeaders = config.getHeaders;
|
|
111
116
|
}
|
|
112
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Set callback for capturing raw SSE events
|
|
120
|
+
*/
|
|
121
|
+
public setSSEEventCallback(callback: SSEEventCallback): void {
|
|
122
|
+
this.onSSEEvent = callback;
|
|
123
|
+
}
|
|
124
|
+
|
|
113
125
|
/**
|
|
114
126
|
* Check if running in client token mode
|
|
115
127
|
*/
|
|
@@ -117,6 +129,13 @@ export class AgentWidgetClient {
|
|
|
117
129
|
return !!this.config.clientToken;
|
|
118
130
|
}
|
|
119
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Check if operating in agent execution mode
|
|
134
|
+
*/
|
|
135
|
+
public isAgentMode(): boolean {
|
|
136
|
+
return !!this.config.agent;
|
|
137
|
+
}
|
|
138
|
+
|
|
120
139
|
/**
|
|
121
140
|
* Get the appropriate API URL based on mode
|
|
122
141
|
*/
|
|
@@ -384,6 +403,9 @@ export class AgentWidgetClient {
|
|
|
384
403
|
* Send a message - handles both proxy and client token modes
|
|
385
404
|
*/
|
|
386
405
|
public async dispatch(options: DispatchOptions, onEvent: SSEHandler) {
|
|
406
|
+
if (this.isAgentMode()) {
|
|
407
|
+
return this.dispatchAgent(options, onEvent);
|
|
408
|
+
}
|
|
387
409
|
if (this.isClientTokenMode()) {
|
|
388
410
|
return this.dispatchClientToken(options, onEvent);
|
|
389
411
|
}
|
|
@@ -579,6 +601,146 @@ export class AgentWidgetClient {
|
|
|
579
601
|
}
|
|
580
602
|
}
|
|
581
603
|
|
|
604
|
+
/**
|
|
605
|
+
* Agent mode dispatch
|
|
606
|
+
*/
|
|
607
|
+
private async dispatchAgent(options: DispatchOptions, onEvent: SSEHandler) {
|
|
608
|
+
const controller = new AbortController();
|
|
609
|
+
if (options.signal) {
|
|
610
|
+
options.signal.addEventListener("abort", () => controller.abort());
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
onEvent({ type: "status", status: "connecting" });
|
|
614
|
+
|
|
615
|
+
const payload = await this.buildAgentPayload(options.messages);
|
|
616
|
+
|
|
617
|
+
if (this.debug) {
|
|
618
|
+
// eslint-disable-next-line no-console
|
|
619
|
+
console.debug("[AgentWidgetClient] agent dispatch payload", payload);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Build headers - merge static headers with dynamic headers if provided
|
|
623
|
+
let headers = { ...this.headers };
|
|
624
|
+
if (this.getHeaders) {
|
|
625
|
+
try {
|
|
626
|
+
const dynamicHeaders = await this.getHeaders();
|
|
627
|
+
headers = { ...headers, ...dynamicHeaders };
|
|
628
|
+
} catch (error) {
|
|
629
|
+
if (typeof console !== "undefined") {
|
|
630
|
+
// eslint-disable-next-line no-console
|
|
631
|
+
console.error("[AgentWidget] getHeaders error:", error);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Use customFetch if provided, otherwise use default fetch
|
|
637
|
+
let response: Response;
|
|
638
|
+
if (this.customFetch) {
|
|
639
|
+
try {
|
|
640
|
+
response = await this.customFetch(
|
|
641
|
+
this.apiUrl,
|
|
642
|
+
{
|
|
643
|
+
method: "POST",
|
|
644
|
+
headers,
|
|
645
|
+
body: JSON.stringify(payload),
|
|
646
|
+
signal: controller.signal
|
|
647
|
+
},
|
|
648
|
+
payload as unknown as AgentWidgetRequestPayload
|
|
649
|
+
);
|
|
650
|
+
} catch (error) {
|
|
651
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
652
|
+
onEvent({ type: "error", error: err });
|
|
653
|
+
throw err;
|
|
654
|
+
}
|
|
655
|
+
} else {
|
|
656
|
+
response = await fetch(this.apiUrl, {
|
|
657
|
+
method: "POST",
|
|
658
|
+
headers,
|
|
659
|
+
body: JSON.stringify(payload),
|
|
660
|
+
signal: controller.signal
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (!response.ok || !response.body) {
|
|
665
|
+
const error = new Error(
|
|
666
|
+
`Agent execution request failed: ${response.status} ${response.statusText}`
|
|
667
|
+
);
|
|
668
|
+
onEvent({ type: "error", error });
|
|
669
|
+
throw error;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
onEvent({ type: "status", status: "connected" });
|
|
673
|
+
try {
|
|
674
|
+
await this.streamResponse(response.body, onEvent, options.assistantMessageId);
|
|
675
|
+
} finally {
|
|
676
|
+
onEvent({ type: "status", status: "idle" });
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
private async buildAgentPayload(
|
|
681
|
+
messages: AgentWidgetMessage[]
|
|
682
|
+
): Promise<AgentWidgetAgentRequestPayload> {
|
|
683
|
+
if (!this.config.agent) {
|
|
684
|
+
throw new Error('Agent configuration required for agent mode');
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// Filter out messages with empty content and normalize
|
|
688
|
+
const normalizedMessages = messages
|
|
689
|
+
.slice()
|
|
690
|
+
.filter(hasValidContent)
|
|
691
|
+
.filter(m => m.role === "user" || m.role === "assistant" || m.role === "system")
|
|
692
|
+
.filter(m => !m.variant || m.variant === "assistant")
|
|
693
|
+
.sort((a, b) => {
|
|
694
|
+
const timeA = new Date(a.createdAt).getTime();
|
|
695
|
+
const timeB = new Date(b.createdAt).getTime();
|
|
696
|
+
return timeA - timeB;
|
|
697
|
+
})
|
|
698
|
+
.map((message) => ({
|
|
699
|
+
role: message.role,
|
|
700
|
+
content: message.contentParts ?? message.llmContent ?? message.rawContent ?? message.content,
|
|
701
|
+
createdAt: message.createdAt
|
|
702
|
+
}));
|
|
703
|
+
|
|
704
|
+
const payload: AgentWidgetAgentRequestPayload = {
|
|
705
|
+
agent: this.config.agent,
|
|
706
|
+
messages: normalizedMessages,
|
|
707
|
+
options: {
|
|
708
|
+
streamResponse: true,
|
|
709
|
+
recordMode: 'virtual',
|
|
710
|
+
...this.config.agentOptions
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
// Add context from providers
|
|
715
|
+
if (this.contextProviders.length) {
|
|
716
|
+
const contextAggregate: Record<string, unknown> = {};
|
|
717
|
+
await Promise.all(
|
|
718
|
+
this.contextProviders.map(async (provider) => {
|
|
719
|
+
try {
|
|
720
|
+
const result = await provider({
|
|
721
|
+
messages,
|
|
722
|
+
config: this.config
|
|
723
|
+
});
|
|
724
|
+
if (result && typeof result === "object") {
|
|
725
|
+
Object.assign(contextAggregate, result);
|
|
726
|
+
}
|
|
727
|
+
} catch (error) {
|
|
728
|
+
if (typeof console !== "undefined") {
|
|
729
|
+
// eslint-disable-next-line no-console
|
|
730
|
+
console.warn("[AgentWidget] Context provider failed:", error);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
})
|
|
734
|
+
);
|
|
735
|
+
|
|
736
|
+
if (Object.keys(contextAggregate).length) {
|
|
737
|
+
payload.context = contextAggregate;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
return payload;
|
|
742
|
+
}
|
|
743
|
+
|
|
582
744
|
private async buildPayload(
|
|
583
745
|
messages: AgentWidgetMessage[]
|
|
584
746
|
): Promise<AgentWidgetRequestPayload> {
|
|
@@ -981,6 +1143,12 @@ export class AgentWidgetClient {
|
|
|
981
1143
|
// Track accumulated raw content for structured formats (JSON, XML, etc.)
|
|
982
1144
|
const rawContentBuffers = new Map<string, string>();
|
|
983
1145
|
|
|
1146
|
+
// Agent execution state tracking
|
|
1147
|
+
let agentExecution: AgentExecutionState | null = null;
|
|
1148
|
+
// Track assistant messages per agent iteration for 'separate' mode
|
|
1149
|
+
const agentIterationMessages = new Map<number, AgentWidgetMessage>();
|
|
1150
|
+
const iterationDisplay = this.config.iterationDisplay ?? 'separate';
|
|
1151
|
+
|
|
984
1152
|
// eslint-disable-next-line no-constant-condition
|
|
985
1153
|
while (true) {
|
|
986
1154
|
const { done, value } = await reader.read();
|
|
@@ -1021,6 +1189,9 @@ export class AgentWidgetClient {
|
|
|
1021
1189
|
const payloadType =
|
|
1022
1190
|
eventType !== "message" ? eventType : payload.type ?? "message";
|
|
1023
1191
|
|
|
1192
|
+
// Tap: capture raw SSE event for event stream inspector
|
|
1193
|
+
this.onSSEEvent?.(payloadType, payload);
|
|
1194
|
+
|
|
1024
1195
|
// If custom SSE event parser is provided, try it first
|
|
1025
1196
|
if (this.parseSSEEvent) {
|
|
1026
1197
|
// Keep assistant message ref in sync
|
|
@@ -1579,6 +1750,208 @@ export class AgentWidgetClient {
|
|
|
1579
1750
|
}
|
|
1580
1751
|
}
|
|
1581
1752
|
onEvent({ type: "status", status: "idle" });
|
|
1753
|
+
// ================================================================
|
|
1754
|
+
// Agent Loop Execution Events
|
|
1755
|
+
// ================================================================
|
|
1756
|
+
} else if (payloadType === "agent_start") {
|
|
1757
|
+
agentExecution = {
|
|
1758
|
+
executionId: payload.executionId,
|
|
1759
|
+
agentId: payload.agentId ?? 'virtual',
|
|
1760
|
+
agentName: payload.agentName ?? '',
|
|
1761
|
+
status: 'running',
|
|
1762
|
+
currentIteration: 0,
|
|
1763
|
+
maxIterations: payload.maxIterations ?? 1,
|
|
1764
|
+
startedAt: resolveTimestamp(payload.startedAt)
|
|
1765
|
+
};
|
|
1766
|
+
} else if (payloadType === "agent_iteration_start") {
|
|
1767
|
+
if (agentExecution) {
|
|
1768
|
+
agentExecution.currentIteration = payload.iteration;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
// In 'separate' mode, finalize previous iteration's message and create a new one
|
|
1772
|
+
if (iterationDisplay === 'separate' && payload.iteration > 1) {
|
|
1773
|
+
const prevMsg = assistantMessage as AgentWidgetMessage | null;
|
|
1774
|
+
if (prevMsg) {
|
|
1775
|
+
prevMsg.streaming = false;
|
|
1776
|
+
emitMessage(prevMsg);
|
|
1777
|
+
// Store the completed message for this iteration
|
|
1778
|
+
agentIterationMessages.set(payload.iteration - 1, prevMsg);
|
|
1779
|
+
// Reset assistant message so ensureAssistantMessage creates a new one
|
|
1780
|
+
assistantMessage = null;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
} else if (payloadType === "agent_turn_start") {
|
|
1784
|
+
// Nothing to do - turn tracking is handled by deltas
|
|
1785
|
+
} else if (payloadType === "agent_turn_delta") {
|
|
1786
|
+
if (payload.contentType === 'text') {
|
|
1787
|
+
// Stream text to assistant message
|
|
1788
|
+
const assistant = ensureAssistantMessage();
|
|
1789
|
+
assistant.content += payload.delta ?? '';
|
|
1790
|
+
assistant.agentMetadata = {
|
|
1791
|
+
executionId: payload.executionId,
|
|
1792
|
+
iteration: payload.iteration,
|
|
1793
|
+
turnId: payload.turnId,
|
|
1794
|
+
agentName: agentExecution?.agentName
|
|
1795
|
+
};
|
|
1796
|
+
emitMessage(assistant);
|
|
1797
|
+
} else if (payload.contentType === 'thinking') {
|
|
1798
|
+
// Stream thinking content to a reasoning message
|
|
1799
|
+
const reasoningId = payload.turnId ?? `agent-think-${payload.iteration}`;
|
|
1800
|
+
const reasoningMessage = ensureReasoningMessage(reasoningId);
|
|
1801
|
+
reasoningMessage.reasoning = reasoningMessage.reasoning ?? {
|
|
1802
|
+
id: reasoningId,
|
|
1803
|
+
status: "streaming",
|
|
1804
|
+
chunks: []
|
|
1805
|
+
};
|
|
1806
|
+
reasoningMessage.reasoning.chunks.push(payload.delta ?? '');
|
|
1807
|
+
reasoningMessage.agentMetadata = {
|
|
1808
|
+
executionId: payload.executionId,
|
|
1809
|
+
iteration: payload.iteration,
|
|
1810
|
+
turnId: payload.turnId
|
|
1811
|
+
};
|
|
1812
|
+
emitMessage(reasoningMessage);
|
|
1813
|
+
} else if (payload.contentType === 'tool_input') {
|
|
1814
|
+
// Stream tool input to current tool message
|
|
1815
|
+
const toolId = payload.toolCallId ?? toolContext.lastId;
|
|
1816
|
+
if (toolId) {
|
|
1817
|
+
const toolMessage = toolMessages.get(toolId);
|
|
1818
|
+
if (toolMessage?.toolCall) {
|
|
1819
|
+
toolMessage.toolCall.chunks = toolMessage.toolCall.chunks ?? [];
|
|
1820
|
+
toolMessage.toolCall.chunks.push(payload.delta ?? '');
|
|
1821
|
+
emitMessage(toolMessage);
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
} else if (payloadType === "agent_turn_complete") {
|
|
1826
|
+
// Mark any active reasoning for this turn as complete
|
|
1827
|
+
const reasoningId = payload.turnId;
|
|
1828
|
+
if (reasoningId) {
|
|
1829
|
+
const reasoningMessage = reasoningMessages.get(reasoningId);
|
|
1830
|
+
if (reasoningMessage?.reasoning) {
|
|
1831
|
+
reasoningMessage.reasoning.status = "complete";
|
|
1832
|
+
reasoningMessage.reasoning.completedAt = resolveTimestamp(payload.completedAt);
|
|
1833
|
+
const start = reasoningMessage.reasoning.startedAt ?? Date.now();
|
|
1834
|
+
reasoningMessage.reasoning.durationMs = Math.max(
|
|
1835
|
+
0,
|
|
1836
|
+
(reasoningMessage.reasoning.completedAt ?? Date.now()) - start
|
|
1837
|
+
);
|
|
1838
|
+
reasoningMessage.streaming = false;
|
|
1839
|
+
emitMessage(reasoningMessage);
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
} else if (payloadType === "agent_tool_start") {
|
|
1843
|
+
const toolId = payload.toolCallId ?? `agent-tool-${nextSequence()}`;
|
|
1844
|
+
trackToolId(getToolCallKey(payload), toolId);
|
|
1845
|
+
const toolMessage = ensureToolMessage(toolId);
|
|
1846
|
+
const tool = toolMessage.toolCall ?? {
|
|
1847
|
+
id: toolId, status: "pending" as const,
|
|
1848
|
+
name: undefined, args: undefined, chunks: undefined,
|
|
1849
|
+
result: undefined, duration: undefined, startedAt: undefined,
|
|
1850
|
+
completedAt: undefined, durationMs: undefined
|
|
1851
|
+
};
|
|
1852
|
+
tool.name = payload.toolName ?? tool.name;
|
|
1853
|
+
tool.status = "running";
|
|
1854
|
+
if (payload.parameters !== undefined) {
|
|
1855
|
+
tool.args = payload.parameters;
|
|
1856
|
+
}
|
|
1857
|
+
tool.startedAt = resolveTimestamp(payload.startedAt ?? payload.timestamp);
|
|
1858
|
+
toolMessage.toolCall = tool;
|
|
1859
|
+
toolMessage.streaming = true;
|
|
1860
|
+
toolMessage.agentMetadata = {
|
|
1861
|
+
executionId: payload.executionId,
|
|
1862
|
+
iteration: payload.iteration
|
|
1863
|
+
};
|
|
1864
|
+
emitMessage(toolMessage);
|
|
1865
|
+
} else if (payloadType === "agent_tool_delta") {
|
|
1866
|
+
const toolId = payload.toolCallId ?? toolContext.lastId;
|
|
1867
|
+
if (toolId) {
|
|
1868
|
+
const toolMessage = toolMessages.get(toolId) ?? ensureToolMessage(toolId);
|
|
1869
|
+
if (toolMessage.toolCall) {
|
|
1870
|
+
toolMessage.toolCall.chunks = toolMessage.toolCall.chunks ?? [];
|
|
1871
|
+
toolMessage.toolCall.chunks.push(payload.delta ?? '');
|
|
1872
|
+
toolMessage.toolCall.status = "running";
|
|
1873
|
+
toolMessage.streaming = true;
|
|
1874
|
+
emitMessage(toolMessage);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
} else if (payloadType === "agent_tool_complete") {
|
|
1878
|
+
const toolId = payload.toolCallId ?? toolContext.lastId;
|
|
1879
|
+
if (toolId) {
|
|
1880
|
+
const toolMessage = toolMessages.get(toolId) ?? ensureToolMessage(toolId);
|
|
1881
|
+
if (toolMessage.toolCall) {
|
|
1882
|
+
toolMessage.toolCall.status = "complete";
|
|
1883
|
+
if (payload.result !== undefined) {
|
|
1884
|
+
toolMessage.toolCall.result = payload.result;
|
|
1885
|
+
}
|
|
1886
|
+
if (typeof payload.executionTime === "number") {
|
|
1887
|
+
toolMessage.toolCall.durationMs = payload.executionTime;
|
|
1888
|
+
}
|
|
1889
|
+
toolMessage.toolCall.completedAt = resolveTimestamp(payload.completedAt ?? payload.timestamp);
|
|
1890
|
+
toolMessage.streaming = false;
|
|
1891
|
+
emitMessage(toolMessage);
|
|
1892
|
+
const callKey = getToolCallKey(payload);
|
|
1893
|
+
if (callKey) {
|
|
1894
|
+
toolContext.byCall.delete(callKey);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
} else if (payloadType === "agent_iteration_complete") {
|
|
1899
|
+
// Iteration complete - no special handling needed
|
|
1900
|
+
// In 'separate' mode, message finalization happens at next iteration_start
|
|
1901
|
+
} else if (payloadType === "agent_reflection") {
|
|
1902
|
+
// Create a reasoning message for reflection content
|
|
1903
|
+
const reflectionId = `agent-reflection-${payload.executionId}-${payload.iteration}`;
|
|
1904
|
+
const reflectionMessage: AgentWidgetMessage = {
|
|
1905
|
+
id: reflectionId,
|
|
1906
|
+
role: "assistant",
|
|
1907
|
+
content: payload.reflection ?? '',
|
|
1908
|
+
createdAt: new Date().toISOString(),
|
|
1909
|
+
streaming: false,
|
|
1910
|
+
variant: "reasoning",
|
|
1911
|
+
sequence: nextSequence(),
|
|
1912
|
+
reasoning: {
|
|
1913
|
+
id: reflectionId,
|
|
1914
|
+
status: "complete",
|
|
1915
|
+
chunks: [payload.reflection ?? '']
|
|
1916
|
+
},
|
|
1917
|
+
agentMetadata: {
|
|
1918
|
+
executionId: payload.executionId,
|
|
1919
|
+
iteration: payload.iteration
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
emitMessage(reflectionMessage);
|
|
1923
|
+
} else if (payloadType === "agent_complete") {
|
|
1924
|
+
if (agentExecution) {
|
|
1925
|
+
agentExecution.status = payload.success ? 'complete' : 'error';
|
|
1926
|
+
agentExecution.completedAt = resolveTimestamp(payload.completedAt);
|
|
1927
|
+
agentExecution.stopReason = payload.stopReason;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
// Finalize the current assistant message
|
|
1931
|
+
const finalMsg = assistantMessage as AgentWidgetMessage | null;
|
|
1932
|
+
if (finalMsg) {
|
|
1933
|
+
finalMsg.streaming = false;
|
|
1934
|
+
emitMessage(finalMsg);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
onEvent({ type: "status", status: "idle" });
|
|
1938
|
+
} else if (payloadType === "agent_error") {
|
|
1939
|
+
const errorMessage = typeof payload.error === 'string'
|
|
1940
|
+
? payload.error
|
|
1941
|
+
: payload.error?.message ?? 'Agent execution error';
|
|
1942
|
+
if (payload.recoverable) {
|
|
1943
|
+
if (typeof console !== "undefined") {
|
|
1944
|
+
// eslint-disable-next-line no-console
|
|
1945
|
+
console.warn("[AgentWidget] Recoverable agent error:", errorMessage);
|
|
1946
|
+
}
|
|
1947
|
+
} else {
|
|
1948
|
+
onEvent({
|
|
1949
|
+
type: "error",
|
|
1950
|
+
error: new Error(errorMessage)
|
|
1951
|
+
});
|
|
1952
|
+
}
|
|
1953
|
+
} else if (payloadType === "agent_ping") {
|
|
1954
|
+
// Keep-alive heartbeat - no action needed
|
|
1582
1955
|
} else if (payloadType === "error" && payload.error) {
|
|
1583
1956
|
onEvent({
|
|
1584
1957
|
type: "error",
|