@agent-native/core 0.84.49 → 0.84.52
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +20 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/run-store.ts +33 -6
- package/corpus/core/src/client/AssistantChat.tsx +84 -23
- package/corpus/core/src/client/agent-chat-adapter.ts +59 -9
- package/corpus/core/src/client/blocks/library/DiffBlock.tsx +7 -5
- package/corpus/core/src/client/session-replay.ts +170 -47
- package/corpus/core/src/client/sse-event-processor.ts +106 -29
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +5 -0
- package/corpus/templates/analytics/changelog/2026-07-02-agent-chat-can-keep-working-through-longer-data-queries-inst.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-02-session-replay-playback-recovers-from-failed-snapshot-uploads.md +6 -0
- package/corpus/templates/analytics/netlify.toml +3 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +9 -5
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +3 -0
- package/corpus/templates/plan/changelog/2026-07-02-agent-chat-can-keep-working-through-longer-visual-plan-updat.md +6 -0
- package/corpus/templates/plan/netlify.toml +3 -0
- package/corpus/templates/plan/server/plugins/agent-chat.ts +4 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +30 -6
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +64 -13
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +50 -9
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +6 -5
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +134 -39
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +29 -3
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +72 -18
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +7 -7
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @agent-native/core
|
|
2
2
|
|
|
3
|
+
## 0.84.52
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- ca38cc7: Carry action-preparation stall detection across reconnect reads for the same run so zero-byte tool input retries cannot keep background chats alive indefinitely.
|
|
8
|
+
- 899ebf1: Hide the diff "Show all lines" footer when annotation anchoring already renders every line.
|
|
9
|
+
- c1e18fb: Make session replay uploads retry failed batches without advancing sequence ids.
|
|
10
|
+
|
|
11
|
+
## 0.84.51
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 55e4678: Recover durable background chat runs when action input preparation stops making byte progress, and avoid duplicate tool cards when reconnects replay completed tool events.
|
|
16
|
+
|
|
17
|
+
## 0.84.50
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- 24a6bd2: Retry agent chat startup timeouts before showing an error when no run stream starts.
|
|
22
|
+
|
|
3
23
|
## 0.84.49
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.84.
|
|
3
|
+
"version": "0.84.52",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -763,9 +763,9 @@ export async function reconcileTerminalRunFromEvents(
|
|
|
763
763
|
sql: `UPDATE agent_runs
|
|
764
764
|
SET status = ?,
|
|
765
765
|
completed_at = COALESCE(completed_at, ?, ${livenessBasisSql()}),
|
|
766
|
-
error_code =
|
|
767
|
-
error_detail =
|
|
768
|
-
terminal_reason =
|
|
766
|
+
error_code = ?,
|
|
767
|
+
error_detail = ?,
|
|
768
|
+
terminal_reason = ?
|
|
769
769
|
WHERE id = ?
|
|
770
770
|
AND (
|
|
771
771
|
status = 'running'
|
|
@@ -775,8 +775,6 @@ export async function reconcileTerminalRunFromEvents(
|
|
|
775
775
|
status,
|
|
776
776
|
latest.eventAt,
|
|
777
777
|
errorCode,
|
|
778
|
-
errorCode,
|
|
779
|
-
errorDetail,
|
|
780
778
|
errorDetail,
|
|
781
779
|
terminalReason,
|
|
782
780
|
runId,
|
|
@@ -1248,7 +1246,7 @@ export async function listRunsForThread(
|
|
|
1248
1246
|
await ensureRunTables();
|
|
1249
1247
|
const limit = Math.min(Math.max(options.limit ?? 10, 1), 50);
|
|
1250
1248
|
const client = getDbExec();
|
|
1251
|
-
|
|
1249
|
+
let { rows } = await client.execute({
|
|
1252
1250
|
sql: `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, error_code, abort_reason, dispatch_mode, terminal_reason, diag_stage
|
|
1253
1251
|
FROM agent_runs
|
|
1254
1252
|
WHERE thread_id = ?
|
|
@@ -1256,6 +1254,35 @@ export async function listRunsForThread(
|
|
|
1256
1254
|
LIMIT ?`,
|
|
1257
1255
|
args: [threadId, limit],
|
|
1258
1256
|
});
|
|
1257
|
+
let repairedTerminalRow = false;
|
|
1258
|
+
for (const r of rows) {
|
|
1259
|
+
const row = r as {
|
|
1260
|
+
id?: string;
|
|
1261
|
+
status?: string;
|
|
1262
|
+
error_code?: string | null;
|
|
1263
|
+
};
|
|
1264
|
+
const runId = row.id;
|
|
1265
|
+
if (!runId) continue;
|
|
1266
|
+
const canReconcileFromEvents =
|
|
1267
|
+
row.status === "running" ||
|
|
1268
|
+
(row.status === "errored" &&
|
|
1269
|
+
row.error_code === STALE_RUN_ERROR_EVENT.errorCode);
|
|
1270
|
+
if (!canReconcileFromEvents) continue;
|
|
1271
|
+
repairedTerminalRow =
|
|
1272
|
+
(await reconcileTerminalRunFromEvents(runId).catch(() => false)) ||
|
|
1273
|
+
repairedTerminalRow;
|
|
1274
|
+
}
|
|
1275
|
+
if (repairedTerminalRow) {
|
|
1276
|
+
const refreshed = await client.execute({
|
|
1277
|
+
sql: `SELECT id, thread_id, turn_id, status, started_at, heartbeat_at, completed_at, last_progress_at, error_code, abort_reason, dispatch_mode, terminal_reason, diag_stage
|
|
1278
|
+
FROM agent_runs
|
|
1279
|
+
WHERE thread_id = ?
|
|
1280
|
+
ORDER BY started_at DESC
|
|
1281
|
+
LIMIT ?`,
|
|
1282
|
+
args: [threadId, limit],
|
|
1283
|
+
});
|
|
1284
|
+
rows = refreshed.rows;
|
|
1285
|
+
}
|
|
1259
1286
|
return rows.map((r) => {
|
|
1260
1287
|
const row = r as {
|
|
1261
1288
|
id: string;
|
|
@@ -153,6 +153,7 @@ import {
|
|
|
153
153
|
import {
|
|
154
154
|
AgentAutoContinueSignal,
|
|
155
155
|
type ContentPart,
|
|
156
|
+
type PreparingActionState,
|
|
156
157
|
readSSEStreamRaw,
|
|
157
158
|
settleInterruptedToolCalls,
|
|
158
159
|
} from "./sse-event-processor.js";
|
|
@@ -675,7 +676,7 @@ export function resolveAssistantChatRunningStatusLabel({
|
|
|
675
676
|
}): string {
|
|
676
677
|
if (runningActivityLabel) return runningActivityLabel;
|
|
677
678
|
if (isAutoResuming) return "Resuming";
|
|
678
|
-
if (isReconnecting && hasReconnectContent) return "
|
|
679
|
+
if (isReconnecting && hasReconnectContent) return "Still working";
|
|
679
680
|
return "Thinking";
|
|
680
681
|
}
|
|
681
682
|
|
|
@@ -1516,7 +1517,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1516
1517
|
const textStreaming = showRunningInUI || externalStreaming;
|
|
1517
1518
|
// A revealed activity label wins; otherwise keep recovery states calm and
|
|
1518
1519
|
// product-facing. Reconnect is transport machinery, so normal replay reads as
|
|
1519
|
-
//
|
|
1520
|
+
// ongoing work instead of exposing "Reconnecting" mid-chat.
|
|
1520
1521
|
const runningStatusLabel = resolveAssistantChatRunningStatusLabel({
|
|
1521
1522
|
runningActivityLabel,
|
|
1522
1523
|
isAutoResuming,
|
|
@@ -1863,6 +1864,27 @@ const AssistantChatInner = forwardRef<
|
|
|
1863
1864
|
const streamReconnect = async () => {
|
|
1864
1865
|
let noProgressDuringReconnect = false;
|
|
1865
1866
|
let latestContent: ContentPart[] = [];
|
|
1867
|
+
const preparingActionState: PreparingActionState = {};
|
|
1868
|
+
const sameRunStillActive = async (): Promise<
|
|
1869
|
+
"active" | "inactive" | "unknown"
|
|
1870
|
+
> => {
|
|
1871
|
+
try {
|
|
1872
|
+
const res = await fetch(
|
|
1873
|
+
`${apiUrl}/runs/active?threadId=${encodeURIComponent(threadId)}`,
|
|
1874
|
+
{ signal: abortCtrl.signal },
|
|
1875
|
+
);
|
|
1876
|
+
if (!res.ok) return "unknown";
|
|
1877
|
+
const info = (await res.json()) as ActiveRunLookup;
|
|
1878
|
+
return info.active === true &&
|
|
1879
|
+
String(info.runId ?? "") === runId &&
|
|
1880
|
+
info.status === "running" &&
|
|
1881
|
+
!activeRunLooksStale(info)
|
|
1882
|
+
? "active"
|
|
1883
|
+
: "inactive";
|
|
1884
|
+
} catch {
|
|
1885
|
+
return "unknown";
|
|
1886
|
+
}
|
|
1887
|
+
};
|
|
1866
1888
|
const threadPollInterval =
|
|
1867
1889
|
afterSeq > 0
|
|
1868
1890
|
? window.setInterval(() => {
|
|
@@ -1871,15 +1893,28 @@ const AssistantChatInner = forwardRef<
|
|
|
1871
1893
|
}, 2000)
|
|
1872
1894
|
: undefined;
|
|
1873
1895
|
try {
|
|
1874
|
-
const
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
);
|
|
1878
|
-
if (sseRes.ok && sseRes.body) {
|
|
1879
|
-
const content: ContentPart[] = [];
|
|
1880
|
-
latestContent = content;
|
|
1881
|
-
const toolCallCounter = { value: 0 };
|
|
1896
|
+
const content: ContentPart[] = [];
|
|
1897
|
+
latestContent = content;
|
|
1898
|
+
const toolCallCounter = { value: 0 };
|
|
1882
1899
|
|
|
1900
|
+
while (
|
|
1901
|
+
reconnectRunIdRef.current === runId &&
|
|
1902
|
+
!abortCtrl.signal.aborted
|
|
1903
|
+
) {
|
|
1904
|
+
const reconnectAfterSeq = resolveReconnectAfterSeq(threadId, runId);
|
|
1905
|
+
reconnectTailOnlyRef.current = reconnectAfterSeq > 0;
|
|
1906
|
+
const sseRes = await fetch(
|
|
1907
|
+
`${apiUrl}/runs/${encodeURIComponent(runId)}/events?after=${reconnectAfterSeq}`,
|
|
1908
|
+
{ signal: abortCtrl.signal },
|
|
1909
|
+
);
|
|
1910
|
+
if (!sseRes.ok || !sseRes.body) {
|
|
1911
|
+
const activeState = await sameRunStillActive();
|
|
1912
|
+
if (activeState !== "inactive") {
|
|
1913
|
+
await new Promise((resolve) => window.setTimeout(resolve, 250));
|
|
1914
|
+
continue;
|
|
1915
|
+
}
|
|
1916
|
+
break;
|
|
1917
|
+
}
|
|
1883
1918
|
let rafPending = false;
|
|
1884
1919
|
let latestSnapshot: ContentPart[] = [];
|
|
1885
1920
|
const scheduleUpdate = (snapshot: ContentPart[]) => {
|
|
@@ -1892,21 +1927,47 @@ const AssistantChatInner = forwardRef<
|
|
|
1892
1927
|
});
|
|
1893
1928
|
};
|
|
1894
1929
|
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1930
|
+
try {
|
|
1931
|
+
await readSSEStreamRaw(
|
|
1932
|
+
sseRes.body,
|
|
1933
|
+
content,
|
|
1934
|
+
toolCallCounter,
|
|
1935
|
+
tabId,
|
|
1936
|
+
scheduleUpdate,
|
|
1937
|
+
(seq) => {
|
|
1938
|
+
markReconnectProgress();
|
|
1939
|
+
updateActiveRunSeq(seq);
|
|
1940
|
+
},
|
|
1941
|
+
{ preparingActionState },
|
|
1942
|
+
);
|
|
1943
|
+
if (reconnectAfterSeq === 0) {
|
|
1944
|
+
setReconnectContent([...content]);
|
|
1945
|
+
}
|
|
1946
|
+
break;
|
|
1947
|
+
} catch (err) {
|
|
1948
|
+
if (
|
|
1949
|
+
err instanceof AgentAutoContinueSignal &&
|
|
1950
|
+
err.reason === "stream_ended"
|
|
1951
|
+
) {
|
|
1952
|
+
if (reconnectAfterSeq === 0) {
|
|
1953
|
+
setReconnectContent([...content]);
|
|
1954
|
+
}
|
|
1955
|
+
const activeState = await sameRunStillActive();
|
|
1956
|
+
if (activeState !== "inactive") {
|
|
1957
|
+
await new Promise((resolve) =>
|
|
1958
|
+
window.setTimeout(resolve, 250),
|
|
1959
|
+
);
|
|
1960
|
+
continue;
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
throw err;
|
|
1908
1964
|
}
|
|
1909
1965
|
}
|
|
1966
|
+
if (reconnectTimedOut && abortCtrl.signal.aborted) {
|
|
1967
|
+
const timeoutError = new Error("Reconnect timed out");
|
|
1968
|
+
timeoutError.name = "AbortError";
|
|
1969
|
+
throw timeoutError;
|
|
1970
|
+
}
|
|
1910
1971
|
} catch (err) {
|
|
1911
1972
|
if (
|
|
1912
1973
|
err instanceof AgentAutoContinueSignal &&
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type AgentActivityTrailEntry,
|
|
21
21
|
type AgentAutoContinueErrorInfo,
|
|
22
22
|
type ContentPart,
|
|
23
|
+
type PreparingActionState,
|
|
23
24
|
readSSEStream,
|
|
24
25
|
settleInterruptedToolCalls,
|
|
25
26
|
} from "./sse-event-processor.js";
|
|
@@ -1380,6 +1381,11 @@ export function createAgentChatAdapter(
|
|
|
1380
1381
|
const turnId = generateTurnId();
|
|
1381
1382
|
let runId: string | null = null;
|
|
1382
1383
|
let lastSeq = -1;
|
|
1384
|
+
const seenRunSeqs = new Map<string, number>();
|
|
1385
|
+
const preparingActionStatesByRun = new Map<
|
|
1386
|
+
string,
|
|
1387
|
+
PreparingActionState
|
|
1388
|
+
>();
|
|
1383
1389
|
let currentRunDispatchMode: string | null = null;
|
|
1384
1390
|
let currentMessageText = normalizeMentions(
|
|
1385
1391
|
recoveryMessageText.trim() || userMessageText,
|
|
@@ -1548,9 +1554,44 @@ export function createAgentChatAdapter(
|
|
|
1548
1554
|
if (mode) currentRunDispatchMode = mode;
|
|
1549
1555
|
};
|
|
1550
1556
|
|
|
1557
|
+
const rememberRunSeq = (seq: number) => {
|
|
1558
|
+
lastSeq = seq;
|
|
1559
|
+
if (runId) {
|
|
1560
|
+
seenRunSeqs.set(runId, seq);
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
|
|
1564
|
+
const reconnectCursorForRun = (
|
|
1565
|
+
nextRunId: string,
|
|
1566
|
+
previousRunId: string | null,
|
|
1567
|
+
) => {
|
|
1568
|
+
const rememberedSeq = seenRunSeqs.get(nextRunId);
|
|
1569
|
+
if (rememberedSeq !== undefined) {
|
|
1570
|
+
lastSeq = rememberedSeq;
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
if (previousRunId !== nextRunId) {
|
|
1574
|
+
lastSeq = -1;
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
|
|
1578
|
+
const preparingActionStateForRun = (
|
|
1579
|
+
id: string | null,
|
|
1580
|
+
): PreparingActionState | undefined => {
|
|
1581
|
+
if (!id) return undefined;
|
|
1582
|
+
const existing = preparingActionStatesByRun.get(id);
|
|
1583
|
+
if (existing) return existing;
|
|
1584
|
+
const state: PreparingActionState = {};
|
|
1585
|
+
preparingActionStatesByRun.set(id, state);
|
|
1586
|
+
return state;
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1551
1589
|
const currentSSEOptions = () => ({
|
|
1552
1590
|
durableBackgroundRun:
|
|
1553
1591
|
currentRunDispatchMode?.startsWith("background") === true,
|
|
1592
|
+
...(runId
|
|
1593
|
+
? { preparingActionState: preparingActionStateForRun(runId) }
|
|
1594
|
+
: {}),
|
|
1554
1595
|
});
|
|
1555
1596
|
|
|
1556
1597
|
const captureChatClientError = (
|
|
@@ -1671,7 +1712,7 @@ export function createAgentChatAdapter(
|
|
|
1671
1712
|
toolCallCounter,
|
|
1672
1713
|
tabId,
|
|
1673
1714
|
(seq) => {
|
|
1674
|
-
|
|
1715
|
+
rememberRunSeq(seq);
|
|
1675
1716
|
if (threadId) updateActiveRunSeq(seq);
|
|
1676
1717
|
},
|
|
1677
1718
|
runId,
|
|
@@ -1765,12 +1806,13 @@ export function createAgentChatAdapter(
|
|
|
1765
1806
|
return false;
|
|
1766
1807
|
}
|
|
1767
1808
|
const activeRunId = String(active.runId);
|
|
1809
|
+
const previousRunId = runId;
|
|
1768
1810
|
runId = activeRunId;
|
|
1769
1811
|
if (!attemptedRunIds.includes(activeRunId)) {
|
|
1770
1812
|
attemptedRunIds.push(activeRunId);
|
|
1771
1813
|
}
|
|
1772
|
-
|
|
1773
|
-
setActiveRun({ threadId, runId: activeRunId, lastSeq
|
|
1814
|
+
reconnectCursorForRun(activeRunId, previousRunId);
|
|
1815
|
+
setActiveRun({ threadId, runId: activeRunId, lastSeq });
|
|
1774
1816
|
const reconnected = yield* reconnectCurrentRun();
|
|
1775
1817
|
if (reconnected) return true;
|
|
1776
1818
|
}
|
|
@@ -1844,12 +1886,13 @@ export function createAgentChatAdapter(
|
|
|
1844
1886
|
if (activeStatus !== "running" && activeStatus !== "starting") {
|
|
1845
1887
|
return false;
|
|
1846
1888
|
}
|
|
1889
|
+
const previousRunId = runId;
|
|
1847
1890
|
runId = activeRunId;
|
|
1848
1891
|
if (!attemptedRunIds.includes(activeRunId)) {
|
|
1849
1892
|
attemptedRunIds.push(activeRunId);
|
|
1850
1893
|
}
|
|
1851
|
-
|
|
1852
|
-
setActiveRun({ threadId, runId: activeRunId, lastSeq
|
|
1894
|
+
reconnectCursorForRun(activeRunId, previousRunId);
|
|
1895
|
+
setActiveRun({ threadId, runId: activeRunId, lastSeq });
|
|
1853
1896
|
const reconnected = yield* reconnectCurrentRun();
|
|
1854
1897
|
if (reconnected) return true;
|
|
1855
1898
|
} catch (activeErr: unknown) {
|
|
@@ -2290,13 +2333,14 @@ export function createAgentChatAdapter(
|
|
|
2290
2333
|
}
|
|
2291
2334
|
if (activeRunId) {
|
|
2292
2335
|
try {
|
|
2336
|
+
const previousRunId = runId;
|
|
2293
2337
|
runId = activeRunId;
|
|
2294
2338
|
if (!attemptedRunIds.includes(runId)) {
|
|
2295
2339
|
attemptedRunIds.push(runId);
|
|
2296
2340
|
}
|
|
2297
|
-
|
|
2341
|
+
reconnectCursorForRun(activeRunId, previousRunId);
|
|
2298
2342
|
if (threadId) {
|
|
2299
|
-
setActiveRun({ threadId, runId, lastSeq
|
|
2343
|
+
setActiveRun({ threadId, runId, lastSeq });
|
|
2300
2344
|
}
|
|
2301
2345
|
const reconnected = yield* reconnectCurrentRun();
|
|
2302
2346
|
if (reconnected) return;
|
|
@@ -2423,7 +2467,7 @@ export function createAgentChatAdapter(
|
|
|
2423
2467
|
toolCallCounter,
|
|
2424
2468
|
tabId,
|
|
2425
2469
|
(seq) => {
|
|
2426
|
-
|
|
2470
|
+
rememberRunSeq(seq);
|
|
2427
2471
|
if (runId && threadId) {
|
|
2428
2472
|
updateActiveRunSeq(seq);
|
|
2429
2473
|
}
|
|
@@ -2616,10 +2660,16 @@ export function createAgentChatAdapter(
|
|
|
2616
2660
|
if (activeReconnected) return;
|
|
2617
2661
|
|
|
2618
2662
|
if (err instanceof AgentStartupTimeoutError) {
|
|
2663
|
+
if (startupRecoveryAttempts < MAX_STARTUP_RECOVERY_ATTEMPTS) {
|
|
2664
|
+
await retryDelay(startupRecoveryAttempts++, abortSignal);
|
|
2665
|
+
if (abortSignal.aborted) return;
|
|
2666
|
+
continue;
|
|
2667
|
+
}
|
|
2619
2668
|
const message =
|
|
2620
|
-
"The agent chat endpoint
|
|
2669
|
+
"The agent chat endpoint did not start streaming in time after several recovery attempts. This usually means prompt setup, the LLM gateway, or the provider is stalled.";
|
|
2621
2670
|
captureChatClientError(err, "startup-timeout", {
|
|
2622
2671
|
timeoutMs: err.timeoutMs,
|
|
2672
|
+
startupRecoveryAttempts,
|
|
2623
2673
|
});
|
|
2624
2674
|
const runError = {
|
|
2625
2675
|
message,
|
|
@@ -838,8 +838,8 @@ function DiffRead({
|
|
|
838
838
|
effectiveMode === "split" ? splitLineCount : rows.length;
|
|
839
839
|
const shouldLimitRows = totalVisibleLineCount > DEFAULT_VISIBLE_DIFF_LINES;
|
|
840
840
|
// Never truncate away an annotated row: extend the window past the last one.
|
|
841
|
-
const
|
|
842
|
-
if (
|
|
841
|
+
const collapsedRowLimit = useMemo(() => {
|
|
842
|
+
if (!shouldLimitRows) return undefined;
|
|
843
843
|
let limit = DEFAULT_VISIBLE_DIFF_LINES;
|
|
844
844
|
if (hasAnnotations) {
|
|
845
845
|
for (let idx = rows.length - 1; idx >= limit; idx -= 1) {
|
|
@@ -850,8 +850,10 @@ function DiffRead({
|
|
|
850
850
|
}
|
|
851
851
|
}
|
|
852
852
|
return limit;
|
|
853
|
-
}, [
|
|
854
|
-
const
|
|
853
|
+
}, [shouldLimitRows, hasAnnotations, rows, markersForRow]);
|
|
854
|
+
const hasHiddenRows =
|
|
855
|
+
collapsedRowLimit != null && collapsedRowLimit < totalVisibleLineCount;
|
|
856
|
+
const rowLimit = showAllRows ? undefined : collapsedRowLimit;
|
|
855
857
|
const displayedRows =
|
|
856
858
|
effectiveMode === "unified" && rowLimit ? rows.slice(0, rowLimit) : rows;
|
|
857
859
|
|
|
@@ -965,7 +967,7 @@ function DiffRead({
|
|
|
965
967
|
ctx={ctx}
|
|
966
968
|
/>
|
|
967
969
|
)}
|
|
968
|
-
{!unchanged &&
|
|
970
|
+
{!unchanged && hasHiddenRows && (
|
|
969
971
|
<button
|
|
970
972
|
type="button"
|
|
971
973
|
data-plan-interactive
|