@alpaca-editor/core 1.0.4145 → 1.0.4146

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.
@@ -1000,18 +1000,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1000
1000
  messagesRef.current = updated;
1001
1001
  return updated;
1002
1002
  });
1003
- // Dispatch a local event so the UI can attempt reconnect when approvals get resolved while paused
1004
- try {
1005
- const ev = new CustomEvent("agent:toolApprovalResolved", {
1006
- detail: {
1007
- messageId: resultMessageId,
1008
- toolCallId: resultToolCallId,
1009
- approved: !(message.data?.functionError || message.data?.error),
1010
- },
1011
- });
1012
- window.dispatchEvent(ev);
1013
- }
1014
- catch { }
1015
1003
  // Tool result activity; reset idle timer
1016
1004
  resetDotsTimer();
1017
1005
  }, [resetDotsTimer]);
@@ -1047,6 +1035,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1047
1035
  case "statusUpdate":
1048
1036
  try {
1049
1037
  const kind = message?.data?.kind;
1038
+ if (kind === "streamOpen") {
1039
+ setIsConnecting(false);
1040
+ setIsWaitingForResponse(false);
1041
+ break;
1042
+ }
1050
1043
  // Live token usage totals update from backend
1051
1044
  if (kind === "tokenUsage") {
1052
1045
  const totals = message?.data?.totals;
@@ -1106,10 +1099,8 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1106
1099
  return updated;
1107
1100
  });
1108
1101
  }
1109
- // Proactively stop the current stream so that the page can be reloaded safely or stay idle until approval
1102
+ // Keep the stream open; just clear waiting flags so UI reflects pause state
1110
1103
  try {
1111
- abortControllerRef.current?.abort();
1112
- abortControllerRef.current = null;
1113
1104
  setIsConnecting(false);
1114
1105
  setIsWaitingForResponse(false);
1115
1106
  }
@@ -1196,7 +1187,11 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1196
1187
  const suffix = kind === "toolApprovalGranted"
1197
1188
  ? " (approved)"
1198
1189
  : " (rejected)";
1199
- const newFunctionName = (tc.functionName || "") + suffix;
1190
+ // Remove "(pending approval)" suffix before adding new suffix
1191
+ const baseFunctionName = (tc.functionName || "")
1192
+ .replace(" (pending approval)", "")
1193
+ .trim();
1194
+ const newFunctionName = baseFunctionName + suffix;
1200
1195
  // Update function name with approval suffix
1201
1196
  return {
1202
1197
  ...tc,
@@ -1398,27 +1393,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1398
1393
  setIsWaitingForResponse(false);
1399
1394
  }
1400
1395
  }, [agent?.id, handleContentChunk, handleToolCall, handleToolResult]);
1401
- // Attempt to reconnect stream when all pending approvals are resolved
1402
- const attemptReconnectIfNoPending = useCallback(async () => {
1403
- try {
1404
- const currentAgent = agent;
1405
- if (!currentAgent)
1406
- return;
1407
- // Check if we're already connected
1408
- if (abortControllerRef.current) {
1409
- return;
1410
- }
1411
- const msgs = messagesRef.current || [];
1412
- const hasPending = msgs.some((m) => (m.toolCalls || []).some((tc) => (tc.functionName || "").includes("(pending approval)")));
1413
- if (!hasPending) {
1414
- await connectToStream(currentAgent);
1415
- }
1416
- }
1417
- catch (err) {
1418
- console.error("❌ Error attempting reconnect:", err);
1419
- }
1420
- }, [agent, connectToStream]);
1421
- // Listen for local approval resolution (when stream is paused) to update UI and reconnect
1396
+ // Listen for local approval resolution to update UI
1422
1397
  useEffect(() => {
1423
1398
  const onApprovalResolved = (ev) => {
1424
1399
  try {
@@ -1428,7 +1403,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1428
1403
  const approved = !!detail.approved;
1429
1404
  if (!messageId || !toolCallId)
1430
1405
  return;
1431
- // Approval resolved; update local state
1406
+ // Update local state to reflect approval status
1432
1407
  setMessages((prev) => {
1433
1408
  const updated = prev.map((m) => {
1434
1409
  if (m.id !== messageId)
@@ -1450,10 +1425,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1450
1425
  messagesRef.current = updated;
1451
1426
  return updated;
1452
1427
  });
1453
- // Try to reconnect if no more pending approvals remain
1454
- setTimeout(() => {
1455
- attemptReconnectIfNoPending();
1456
- }, 100);
1457
1428
  }
1458
1429
  catch (err) {
1459
1430
  console.error("❌ Error handling approval resolution:", err);
@@ -1461,7 +1432,7 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1461
1432
  };
1462
1433
  window.addEventListener("agent:toolApprovalResolved", onApprovalResolved);
1463
1434
  return () => window.removeEventListener("agent:toolApprovalResolved", onApprovalResolved);
1464
- }, [attemptReconnectIfNoPending]);
1435
+ }, []);
1465
1436
  // Load agent data and messages
1466
1437
  const loadAgent = useCallback(async () => {
1467
1438
  try {
@@ -1627,7 +1598,42 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1627
1598
  setError(null);
1628
1599
  const agentData = await getAgent(agentStub.id);
1629
1600
  setAgent(agentData);
1630
- setMessages(agentData.messages || []);
1601
+ // Merge database messages with any existing streaming messages
1602
+ // to prevent losing responses that are currently being streamed
1603
+ setMessages((prevMessages) => {
1604
+ const dbMessages = agentData.messages || [];
1605
+ // Find any streaming (incomplete) messages that exist locally but not in DB
1606
+ const streamingMessages = prevMessages.filter((msg) => !msg.isCompleted && msg.role === "assistant");
1607
+ // If we have streaming messages, preserve them
1608
+ if (streamingMessages.length > 0) {
1609
+ // Get all completed messages from DB
1610
+ const completedDbMessages = dbMessages.filter((msg) => {
1611
+ // Check if this message is one of our streaming messages
1612
+ const isStreamingMessage = streamingMessages.some((sm) => sm.id === msg.id);
1613
+ return !isStreamingMessage;
1614
+ });
1615
+ // Merge: use DB messages but keep streaming ones
1616
+ const merged = [...completedDbMessages];
1617
+ // Add back the streaming messages at the end
1618
+ for (const streamingMsg of streamingMessages) {
1619
+ // Check if this message ID exists in DB messages
1620
+ const dbVersion = dbMessages.find((m) => m.id === streamingMsg.id);
1621
+ if (dbVersion &&
1622
+ dbVersion.content &&
1623
+ dbVersion.content.length > streamingMsg.content.length) {
1624
+ // DB has more content, skip the streaming version
1625
+ merged.push(dbVersion);
1626
+ }
1627
+ else {
1628
+ // Keep the streaming version
1629
+ merged.push(streamingMsg);
1630
+ }
1631
+ }
1632
+ return merged;
1633
+ }
1634
+ // No streaming messages, just use DB messages
1635
+ return dbMessages;
1636
+ });
1631
1637
  // Set agent ID for existing agents too
1632
1638
  window.currentAgentId = agentData.id;
1633
1639
  // Check if cost limit was exceeded (detect from existing messages)
@@ -1686,8 +1692,9 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1686
1692
  const isRunning = agentData.status === "running" || agentData.status === 1;
1687
1693
  const isWaitingForApproval = agentData.status === "waitingForApproval" ||
1688
1694
  agentData.status === 2;
1689
- if (isRunning) {
1690
- // Only auto-reconnect for running agents, not waiting for approval
1695
+ if (isRunning || isWaitingForApproval) {
1696
+ // Auto-connect for running agents AND agents waiting for approval
1697
+ // (stream stays open during approval now)
1691
1698
  setTimeout(async () => {
1692
1699
  if (abortControllerRef.current) {
1693
1700
  return;
@@ -1710,11 +1717,6 @@ export function AgentTerminal({ agentStub, initialMetadata, profiles, }) {
1710
1717
  await connectToStream(agentData);
1711
1718
  }, 100);
1712
1719
  }
1713
- else if (isWaitingForApproval) {
1714
- // For agents waiting for approval, just show the UI
1715
- // The approval button handler will restart the agent if needed
1716
- console.log("Agent is waiting for approval - showing approval UI without auto-reconnect");
1717
- }
1718
1720
  }
1719
1721
  catch (err) {
1720
1722
  console.error("❌ Failed to load agent:", err);