@parhelia/core 0.1.12737 → 0.1.12744

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.
Files changed (32) hide show
  1. package/dist/editor/ai/AgentTerminal.js +122 -22
  2. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  3. package/dist/editor/ai/AgentTerminalStatusBar.js +62 -7
  4. package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
  5. package/dist/editor/ai/InlineAiDialog.js +6 -2
  6. package/dist/editor/ai/InlineAiDialog.js.map +1 -1
  7. package/dist/editor/ai/ToolCallDisplay.js +15 -8
  8. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  9. package/dist/editor/ai/agentDiagnostics.d.ts +8 -1
  10. package/dist/editor/ai/agentDiagnostics.js +48 -1
  11. package/dist/editor/ai/agentDiagnostics.js.map +1 -1
  12. package/dist/editor/client/EditorShell.d.ts +5 -0
  13. package/dist/editor/client/EditorShell.js.map +1 -1
  14. package/dist/editor/client/hooks/useSocketMessageHandler.d.ts +5 -0
  15. package/dist/editor/client/hooks/useSocketMessageHandler.js +60 -2
  16. package/dist/editor/client/hooks/useSocketMessageHandler.js.map +1 -1
  17. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +1 -2
  18. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  19. package/dist/editor/menubar/toolbar-sections/ViewportControls.js +3 -2
  20. package/dist/editor/menubar/toolbar-sections/ViewportControls.js.map +1 -1
  21. package/dist/editor/settings/WebSocketMessages.js +6 -3
  22. package/dist/editor/settings/WebSocketMessages.js.map +1 -1
  23. package/dist/editor/settings/panels/ProjectTemplatesPanel.js +27 -8
  24. package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
  25. package/dist/editor/sidebar/ComponentTree.js +37 -15
  26. package/dist/editor/sidebar/ComponentTree.js.map +1 -1
  27. package/dist/revision.d.ts +2 -2
  28. package/dist/revision.js +2 -2
  29. package/dist/task-board/components/ProjectDashboard.d.ts +11 -0
  30. package/dist/task-board/components/ProjectDashboard.js +87 -59
  31. package/dist/task-board/components/ProjectDashboard.js.map +1 -1
  32. package/package.json +1 -1
@@ -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 [];
1010
+ if (!normalizedAgentId || !editContext) {
1011
+ return { events: [], totalCount: 0, receivedSeqs: [], missingSeqs: [] };
1011
1012
  }
1012
- if (!editContext) {
1013
- return [];
1014
- }
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,15 +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(-8)
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)
1026
1048
  .map((message) => ({
1027
1049
  timestamp: message.timestamp,
1050
+ direction: message.direction ?? "incoming",
1028
1051
  type: message.type,
1029
- seq: getAgentRunMessageSeq(message.payload),
1030
- detail: getAgentRunMessageDetail(message.type, message.payload),
1052
+ seq: message.seq ?? getAgentRunMessageSeq(message.payload),
1053
+ detail: message.detail ??
1054
+ getAgentRunMessageDetail(message.type, message.payload),
1055
+ rawMessage: message.rawMessage,
1056
+ payloadBytes: message.payloadBytes ?? null,
1031
1057
  }));
1058
+ return {
1059
+ events,
1060
+ totalCount: currentRunMessages.length,
1061
+ receivedSeqs,
1062
+ missingSeqs,
1063
+ };
1032
1064
  }, [currentAgentId, editContext?.webSocketMessages]);
1065
+ const recentAgentRunEvents = currentRunDiagnostics.events;
1033
1066
  const appendToolUiEvent = useCallback((type, detail, seq) => {
1034
1067
  const timestamp = new Date().toISOString();
1035
1068
  setRecentToolUiEvents((prev) => {
@@ -1834,6 +1867,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
1834
1867
  const lastSeqRef = useRef(0);
1835
1868
  const subscribedAgentIdRef = useRef(null);
1836
1869
  const reconcileServerStateInFlightRef = useRef(false);
1870
+ const streamRecoveryInFlightRef = useRef(false);
1871
+ const lastStreamRecoveryAtRef = useRef(0);
1837
1872
  const toolCallFirstSeenAtRef = useRef({});
1838
1873
  const pendingToolCompletionTimersRef = useRef({});
1839
1874
  // Cache mode/model/profile changes made while the agent is still "new" (not yet persisted)
@@ -2193,7 +2228,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
2193
2228
  const settleCompletedRun = useCallback((finalStatus = "completed") => {
2194
2229
  clearStopGuard();
2195
2230
  setError(null);
2196
- 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.
2197
2234
  setLastRunStatusReason(null);
2198
2235
  clearHeartbeatMessages();
2199
2236
  setMessages((prev) => {
@@ -3921,8 +3958,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3921
3958
  "AI could not complete this request.";
3922
3959
  clearHeartbeatMessages();
3923
3960
  setLastRunStatusReason(null);
3924
- // Reset deduplication for the next run after an error
3925
- 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.
3926
3964
  setError(errorMsg);
3927
3965
  // Update agent status to error
3928
3966
  setAgent((prev) => prev ? { ...prev, status: "error", statusMessage: errorMsg } : prev);
@@ -3961,10 +3999,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3961
3999
  if (subscribedAgentIdRef.current) {
3962
4000
  const socket = globalThis.editorSocket;
3963
4001
  if (socket && socket.readyState === WebSocket.OPEN) {
3964
- socket.send(JSON.stringify({
4002
+ const payload = {
3965
4003
  type: "agent:unsubscribe",
3966
4004
  agentId: subscribedAgentIdRef.current,
3967
- }));
4005
+ };
4006
+ console.debug("[AgentWebSocket] sent", payload);
4007
+ socket.send(JSON.stringify(payload));
3968
4008
  }
3969
4009
  subscribedAgentIdRef.current = null;
3970
4010
  }
@@ -3973,10 +4013,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
3973
4013
  // Send subscription message to server
3974
4014
  const socket = globalThis.editorSocket;
3975
4015
  if (socket && socket.readyState === WebSocket.OPEN) {
3976
- socket.send(JSON.stringify({
4016
+ const payload = {
3977
4017
  type: "agent:subscribe",
3978
4018
  agentId: agentStub.id,
3979
- }));
4019
+ };
4020
+ console.debug("[AgentWebSocket] sent", payload);
4021
+ socket.send(JSON.stringify(payload));
3980
4022
  }
3981
4023
  // Use the addSocketMessageListener helper from editContext
3982
4024
  // Wrap the handler in a stable function that uses the ref
@@ -4021,10 +4063,12 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
4021
4063
  if (socket &&
4022
4064
  socket.readyState === WebSocket.OPEN &&
4023
4065
  subscribedAgentIdRef.current) {
4024
- socket.send(JSON.stringify({
4066
+ const payload = {
4025
4067
  type: "agent:unsubscribe",
4026
4068
  agentId: subscribedAgentIdRef.current,
4027
- }));
4069
+ };
4070
+ console.debug("[AgentWebSocket] sent", payload);
4071
+ socket.send(JSON.stringify(payload));
4028
4072
  }
4029
4073
  unsubscribe();
4030
4074
  subscribedAgentIdRef.current = null;
@@ -5443,6 +5487,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5443
5487
  const assistantGroupCount = useMemo(() => groupConsecutiveMessages(messages).filter((group) => group.type === "assistant-group").length, [messages]);
5444
5488
  const runDiagnosticsSnapshot = useMemo(() => {
5445
5489
  const lastEvent = recentAgentRunEvents[recentAgentRunEvents.length - 1];
5490
+ const { receivedSeqs, missingSeqs, totalCount } = currentRunDiagnostics;
5491
+ const observedMaxSeq = receivedSeqs[receivedSeqs.length - 1] ?? 0;
5446
5492
  return {
5447
5493
  agentId: currentAgentId,
5448
5494
  isSubmitting,
@@ -5455,10 +5501,13 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5455
5501
  hasActiveStreaming: hasActiveStreaming(),
5456
5502
  isSubscribed: normalizeDialogAgentId(subscribedAgentIdRef.current) ===
5457
5503
  normalizeDialogAgentId(currentAgentId),
5458
- lastSeq: lastSeqRef.current,
5504
+ lastSeq: Math.max(lastSeqRef.current, observedMaxSeq),
5459
5505
  lastEventType: lastEvent?.type ?? null,
5460
5506
  lastEventAt: lastEvent?.timestamp ?? null,
5461
5507
  recentEvents: recentAgentRunEvents,
5508
+ currentRunReceivedSeqs: receivedSeqs,
5509
+ currentRunMissingSeqs: missingSeqs,
5510
+ currentRunTotalEventCount: totalCount,
5462
5511
  assistantMessageCount,
5463
5512
  assistantGroupCount,
5464
5513
  assistantGroupsWithRenderableToolCalls,
@@ -5472,6 +5521,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5472
5521
  assistantGroupsWithRenderableToolCalls,
5473
5522
  assistantMessageCount,
5474
5523
  currentAgentId,
5524
+ currentRunDiagnostics,
5475
5525
  hasActiveStreaming,
5476
5526
  incompleteToolCallCount,
5477
5527
  isAgentThinking,
@@ -5502,10 +5552,58 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5502
5552
  settleCompletedRun(normalizeServerExecutionStatus(serverStatus) === "cancelled"
5503
5553
  ? "cancelled"
5504
5554
  : "completed");
5555
+ return;
5556
+ }
5557
+ const serverLastSeq = diagnostics.currentSession?.lastDelivery?.lastSeq ??
5558
+ diagnostics.transport?.lastSeq ??
5559
+ null;
5560
+ const serverLastSuccessAt = diagnostics.currentSession?.lastDelivery?.lastSuccessAt ??
5561
+ diagnostics.transport?.lastBroadcastAt ??
5562
+ diagnostics.transport?.lastProducedAt ??
5563
+ null;
5564
+ const serverLastSuccessTime = serverLastSuccessAt
5565
+ ? Date.parse(serverLastSuccessAt)
5566
+ : Number.NaN;
5567
+ const now = Date.now();
5568
+ const serverDeliveryOldEnough = Number.isFinite(serverLastSuccessTime) &&
5569
+ now - serverLastSuccessTime > 8_000;
5570
+ const recoveryCooldownElapsed = now - lastStreamRecoveryAtRef.current > 30_000;
5571
+ const localLastSeq = lastSeqRef.current;
5572
+ if (diagnostics.currentSession?.isConnected &&
5573
+ diagnostics.currentSession?.isSubscribed &&
5574
+ typeof serverLastSeq === "number" &&
5575
+ serverLastSeq > localLastSeq &&
5576
+ serverDeliveryOldEnough &&
5577
+ recoveryCooldownElapsed &&
5578
+ !streamRecoveryInFlightRef.current) {
5579
+ streamRecoveryInFlightRef.current = true;
5580
+ lastStreamRecoveryAtRef.current = now;
5581
+ appendToolUiEvent("ui:stream-recovery", `server seq ${serverLastSeq} is ahead of local seq ${localLastSeq}; reconnecting socket`, serverLastSeq);
5582
+ try {
5583
+ await loadAgent();
5584
+ }
5585
+ catch (loadError) {
5586
+ console.warn("[AgentTerminal] Failed to reload agent during stream recovery", loadError);
5587
+ }
5588
+ try {
5589
+ const socket = globalThis.editorSocket;
5590
+ if (socket && socket.readyState === WebSocket.OPEN) {
5591
+ socket.close(4000, "agent-stream-lag");
5592
+ }
5593
+ }
5594
+ catch (socketError) {
5595
+ console.warn("[AgentTerminal] Failed to reconnect stale agent stream socket", socketError);
5596
+ }
5597
+ finally {
5598
+ window.setTimeout(() => {
5599
+ streamRecoveryInFlightRef.current = false;
5600
+ }, 5_000);
5601
+ }
5505
5602
  }
5506
5603
  }
5507
5604
  catch (error) {
5508
5605
  console.warn("[AgentTerminal] Failed to reconcile agent run status", error);
5606
+ streamRecoveryInFlightRef.current = false;
5509
5607
  }
5510
5608
  };
5511
5609
  // Avoid racing a freshly submitted run before the backend has registered it.
@@ -5522,6 +5620,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, isActive =
5522
5620
  editContext?.socketConnectionVersion,
5523
5621
  isActive,
5524
5622
  isExecuting,
5623
+ appendToolUiEvent,
5624
+ loadAgent,
5525
5625
  settleCompletedRun,
5526
5626
  ]);
5527
5627
  const showInitialThinkingSplash = messages.length === 0 &&