@parhelia/core 0.1.12741 → 0.1.12749

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.
@@ -33,6 +33,7 @@ import { Splitter } from "../ui/Splitter";
33
33
  import { ScrollingContentTree } from "../ScrollingContentTree";
34
34
  import { MarkdownDisplay, } from "../../components/MarkdownDisplay";
35
35
  const AGENT_HISTORY_LIMIT = 1000;
36
+ const RECENT_RUN_EVENTS_LIMIT = 50;
36
37
  function mergeAgentOperationHistory(existing, incoming, limit = AGENT_HISTORY_LIMIT) {
37
38
  const merged = new Map(existing.map((operation) => [operation.id, operation]));
38
39
  for (const operation of incoming) {
@@ -1004,16 +1005,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1004
1005
  return current.some((m) => !m.isCompleted && m.messageType === "streaming");
1005
1006
  }, []);
1006
1007
  const currentAgentId = agent?.id || agentStub.id;
1007
- const recentAgentRunEvents = useMemo(() => {
1008
+ const currentRunDiagnostics = useMemo(() => {
1008
1009
  const normalizedAgentId = normalizeDialogAgentId(currentAgentId);
1009
- if (!normalizedAgentId) {
1010
- return [];
1011
- }
1012
- if (!editContext) {
1013
- return [];
1010
+ if (!normalizedAgentId || !editContext) {
1011
+ return { events: [], totalCount: 0, receivedSeqs: [], missingSeqs: [] };
1014
1012
  }
1015
- return (editContext.webSocketMessages || [])
1016
- .filter((message) => {
1013
+ const agentRunMessages = (editContext.webSocketMessages || []).filter((message) => {
1017
1014
  if (!message?.type?.startsWith("agent:run:")) {
1018
1015
  return false;
1019
1016
  }
@@ -1021,19 +1018,51 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1021
1018
  return false;
1022
1019
  }
1023
1020
  return getAgentRunMessageAgentId(message.payload) === normalizedAgentId;
1024
- })
1025
- .slice(0, 8)
1026
- .reverse()
1021
+ });
1022
+ let runStartIndex = 0;
1023
+ for (let i = agentRunMessages.length - 1; i >= 0; i--) {
1024
+ if (agentRunMessages[i]?.type === "agent:run:start") {
1025
+ runStartIndex = i;
1026
+ break;
1027
+ }
1028
+ }
1029
+ const currentRunMessages = agentRunMessages.slice(runStartIndex);
1030
+ const seqsSet = new Set();
1031
+ for (const message of currentRunMessages) {
1032
+ const seq = message.seq ?? getAgentRunMessageSeq(message.payload);
1033
+ if (typeof seq === "number" && seq > 0) {
1034
+ seqsSet.add(seq);
1035
+ }
1036
+ }
1037
+ const receivedSeqs = [...seqsSet].sort((a, b) => a - b);
1038
+ const missingSeqs = [];
1039
+ const max = receivedSeqs[receivedSeqs.length - 1];
1040
+ if (max !== undefined) {
1041
+ for (let i = 1; i <= max; i++) {
1042
+ if (!seqsSet.has(i))
1043
+ missingSeqs.push(i);
1044
+ }
1045
+ }
1046
+ const events = currentRunMessages
1047
+ .slice(-RECENT_RUN_EVENTS_LIMIT)
1027
1048
  .map((message) => ({
1028
1049
  timestamp: message.timestamp,
1029
1050
  direction: message.direction ?? "incoming",
1030
1051
  type: message.type,
1031
1052
  seq: message.seq ?? getAgentRunMessageSeq(message.payload),
1032
- detail: message.detail ?? getAgentRunMessageDetail(message.type, message.payload),
1053
+ detail: message.detail ??
1054
+ getAgentRunMessageDetail(message.type, message.payload),
1033
1055
  rawMessage: message.rawMessage,
1034
1056
  payloadBytes: message.payloadBytes ?? null,
1035
1057
  }));
1058
+ return {
1059
+ events,
1060
+ totalCount: currentRunMessages.length,
1061
+ receivedSeqs,
1062
+ missingSeqs,
1063
+ };
1036
1064
  }, [currentAgentId, editContext?.webSocketMessages]);
1065
+ const recentAgentRunEvents = currentRunDiagnostics.events;
1037
1066
  const appendToolUiEvent = useCallback((type, detail, seq) => {
1038
1067
  const timestamp = new Date().toISOString();
1039
1068
  setRecentToolUiEvents((prev) => {
@@ -2199,7 +2228,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2199
2228
  const settleCompletedRun = useCallback((finalStatus = "completed") => {
2200
2229
  clearStopGuard();
2201
2230
  setError(null);
2202
- lastSeqRef.current = 0;
2231
+ // Keep lastSeqRef populated so the diagnostic popover can show the
2232
+ // last observed seq after the run finishes. The next agent:run:start
2233
+ // resets it for the new run.
2203
2234
  setLastRunStatusReason(null);
2204
2235
  clearHeartbeatMessages();
2205
2236
  setMessages((prev) => {
@@ -3927,8 +3958,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3927
3958
  "AI could not complete this request.";
3928
3959
  clearHeartbeatMessages();
3929
3960
  setLastRunStatusReason(null);
3930
- // Reset deduplication for the next run after an error
3931
- lastSeqRef.current = 0;
3961
+ // Keep lastSeqRef populated so the diagnostic popover can show the
3962
+ // last observed seq after the error. The next agent:run:start resets
3963
+ // it for the new run.
3932
3964
  setError(errorMsg);
3933
3965
  // Update agent status to error
3934
3966
  setAgent((prev) => prev ? { ...prev, status: "error", statusMessage: errorMsg } : prev);
@@ -5455,6 +5487,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5455
5487
  const assistantGroupCount = useMemo(() => groupConsecutiveMessages(messages).filter((group) => group.type === "assistant-group").length, [messages]);
5456
5488
  const runDiagnosticsSnapshot = useMemo(() => {
5457
5489
  const lastEvent = recentAgentRunEvents[recentAgentRunEvents.length - 1];
5490
+ const { receivedSeqs, missingSeqs, totalCount } = currentRunDiagnostics;
5491
+ const observedMaxSeq = receivedSeqs[receivedSeqs.length - 1] ?? 0;
5458
5492
  return {
5459
5493
  agentId: currentAgentId,
5460
5494
  isSubmitting,
@@ -5467,10 +5501,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5467
5501
  hasActiveStreaming: hasActiveStreaming(),
5468
5502
  isSubscribed: normalizeDialogAgentId(subscribedAgentIdRef.current) ===
5469
5503
  normalizeDialogAgentId(currentAgentId),
5470
- lastSeq: lastSeqRef.current,
5504
+ lastSeq: Math.max(lastSeqRef.current, observedMaxSeq),
5471
5505
  lastEventType: lastEvent?.type ?? null,
5472
5506
  lastEventAt: lastEvent?.timestamp ?? null,
5473
5507
  recentEvents: recentAgentRunEvents,
5508
+ currentRunReceivedSeqs: receivedSeqs,
5509
+ currentRunMissingSeqs: missingSeqs,
5510
+ currentRunTotalEventCount: totalCount,
5474
5511
  assistantMessageCount,
5475
5512
  assistantGroupCount,
5476
5513
  assistantGroupsWithRenderableToolCalls,
@@ -5484,6 +5521,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5484
5521
  assistantGroupsWithRenderableToolCalls,
5485
5522
  assistantMessageCount,
5486
5523
  currentAgentId,
5524
+ currentRunDiagnostics,
5487
5525
  hasActiveStreaming,
5488
5526
  incompleteToolCallCount,
5489
5527
  isAgentThinking,
@@ -5516,6 +5554,25 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5516
5554
  : "completed");
5517
5555
  return;
5518
5556
  }
5557
+ // Backstop: if the backend reports an error but we never received the
5558
+ // agent:run:error lifecycle event (e.g. it was missed or the broadcast
5559
+ // path failed to fire), surface the error locally instead of waiting
5560
+ // for the user to reload.
5561
+ if (normalizeServerExecutionStatus(serverStatus) === "error") {
5562
+ const rawError = diagnostics.execution?.error ?? null;
5563
+ const errorMsg = toUserFacingAgentErrorMessage(rawError) ||
5564
+ rawError ||
5565
+ "AI could not complete this request.";
5566
+ clearHeartbeatMessages();
5567
+ setLastRunStatusReason(null);
5568
+ setError(errorMsg);
5569
+ setAgent((prev) => prev ? { ...prev, status: "error", statusMessage: errorMsg } : prev);
5570
+ setIsWaitingForResponse(false);
5571
+ isWaitingRef.current = false;
5572
+ setIsConnecting(false);
5573
+ setIsAgentThinking(false);
5574
+ return;
5575
+ }
5519
5576
  const serverLastSeq = diagnostics.currentSession?.lastDelivery?.lastSeq ??
5520
5577
  diagnostics.transport?.lastSeq ??
5521
5578
  null;
@@ -5583,6 +5640,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5583
5640
  isActive,
5584
5641
  isExecuting,
5585
5642
  appendToolUiEvent,
5643
+ clearHeartbeatMessages,
5586
5644
  loadAgent,
5587
5645
  settleCompletedRun,
5588
5646
  ]);