@agent-native/core 0.84.36 → 0.84.38
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/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/getting-started.mdx +3 -3
- package/corpus/core/docs/content/locales/ar-SA/getting-started.mdx +2 -2
- package/corpus/core/docs/content/locales/de-DE/getting-started.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/getting-started.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/getting-started.mdx +2 -2
- package/corpus/core/docs/content/locales/hi-IN/getting-started.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/getting-started.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/getting-started.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/getting-started.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/getting-started.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/getting-started.mdx +2 -2
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +2 -0
- package/corpus/core/src/client/AssistantChat.tsx +27 -13
- package/corpus/core/src/client/RunStuckBanner.tsx +23 -3
- package/corpus/core/src/client/agent-chat-adapter.ts +20 -0
- package/corpus/core/src/client/sse-event-processor.ts +31 -6
- package/corpus/core/src/client/use-run-stuck-detection.ts +14 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +13 -2
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +2 -0
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +6 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +19 -13
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/RunStuckBanner.d.ts.map +1 -1
- package/dist/client/RunStuckBanner.js +16 -1
- package/dist/client/RunStuckBanner.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +17 -2
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +11 -2
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +15 -10
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-run-stuck-detection.d.ts +4 -0
- package/dist/client/use-run-stuck-detection.d.ts.map +1 -1
- package/dist/client/use-run-stuck-detection.js +7 -1
- package/dist/client/use-run-stuck-detection.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +5 -2
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/docs/content/getting-started.mdx +3 -3
- package/docs/content/locales/ar-SA/getting-started.mdx +2 -2
- package/docs/content/locales/de-DE/getting-started.mdx +2 -2
- package/docs/content/locales/es-ES/getting-started.mdx +2 -2
- package/docs/content/locales/fr-FR/getting-started.mdx +2 -2
- package/docs/content/locales/hi-IN/getting-started.mdx +2 -2
- package/docs/content/locales/ja-JP/getting-started.mdx +2 -2
- package/docs/content/locales/ko-KR/getting-started.mdx +2 -2
- package/docs/content/locales/pt-BR/getting-started.mdx +2 -2
- package/docs/content/locales/zh-CN/getting-started.mdx +2 -2
- package/docs/content/locales/zh-TW/getting-started.mdx +2 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunStuckBanner.d.ts","sourceRoot":"","sources":["../../src/client/RunStuckBanner.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAGtC;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACpD;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"RunStuckBanner.d.ts","sourceRoot":"","sources":["../../src/client/RunStuckBanner.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAGtC;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACpC,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACpD;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA2FD,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,MAAM,EACN,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EAClB,SAAiB,EACjB,gBAAgB,EAChB,SAAS,GACV,EAAE,mBAAmB,sCAiMrB"}
|
|
@@ -5,6 +5,7 @@ import { trackEvent } from "./analytics.js";
|
|
|
5
5
|
import { useRunStuckDetection, useAbortRun, } from "./use-run-stuck-detection.js";
|
|
6
6
|
import { cn } from "./utils.js";
|
|
7
7
|
const AUTO_RETRY_CLAIM_TTL_MS = 5 * 60 * 1000;
|
|
8
|
+
const BACKGROUND_WORKER_FRESH_HEARTBEAT_MS = 30_000;
|
|
8
9
|
function createAutoRetryOwnerId() {
|
|
9
10
|
const cryptoApi = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function"
|
|
10
11
|
? crypto
|
|
@@ -14,6 +15,13 @@ function createAutoRetryOwnerId() {
|
|
|
14
15
|
function autoRetryClaimKey(threadId, runId) {
|
|
15
16
|
return `agent-native:stuck-auto-retry:${threadId}:${runId}`;
|
|
16
17
|
}
|
|
18
|
+
function isFreshBackgroundWorker(state) {
|
|
19
|
+
return Boolean(state.status === "running" &&
|
|
20
|
+
state.dispatchMode === "background-processing" &&
|
|
21
|
+
state.heartbeatSinceMs != null &&
|
|
22
|
+
state.heartbeatSinceMs >= 0 &&
|
|
23
|
+
state.heartbeatSinceMs < BACKGROUND_WORKER_FRESH_HEARTBEAT_MS);
|
|
24
|
+
}
|
|
17
25
|
function markAutoRetryClaim(key, ownerId) {
|
|
18
26
|
if (typeof window === "undefined")
|
|
19
27
|
return true;
|
|
@@ -63,6 +71,7 @@ export function RunStuckBanner({ threadId, apiUrl, stuckThresholdMs, onRetry, on
|
|
|
63
71
|
generatedOwnerIdRef.current = createAutoRetryOwnerId();
|
|
64
72
|
}
|
|
65
73
|
const ownerId = autoRetryOwnerId ?? generatedOwnerIdRef.current;
|
|
74
|
+
const backgroundWorkerStillAlive = isFreshBackgroundWorker(state);
|
|
66
75
|
const lastReportedRef = useRef({ isStuck: false, runId: null });
|
|
67
76
|
useEffect(() => {
|
|
68
77
|
const last = lastReportedRef.current;
|
|
@@ -98,6 +107,7 @@ export function RunStuckBanner({ threadId, apiUrl, stuckThresholdMs, onRetry, on
|
|
|
98
107
|
}, [state.runId, state.status]);
|
|
99
108
|
useEffect(() => {
|
|
100
109
|
if (!autoRetry ||
|
|
110
|
+
backgroundWorkerStillAlive ||
|
|
101
111
|
!state.isStuck ||
|
|
102
112
|
!state.runId ||
|
|
103
113
|
busy.type !== "none" ||
|
|
@@ -128,6 +138,7 @@ export function RunStuckBanner({ threadId, apiUrl, stuckThresholdMs, onRetry, on
|
|
|
128
138
|
}, [
|
|
129
139
|
abortRun,
|
|
130
140
|
autoRetry,
|
|
141
|
+
backgroundWorkerStillAlive,
|
|
131
142
|
busy,
|
|
132
143
|
onRetry,
|
|
133
144
|
ownerId,
|
|
@@ -166,7 +177,11 @@ export function RunStuckBanner({ threadId, apiUrl, stuckThresholdMs, onRetry, on
|
|
|
166
177
|
};
|
|
167
178
|
const busyType = busy.type;
|
|
168
179
|
const stuckSeconds = state.stuckSinceMs != null ? Math.floor(state.stuckSinceMs / 1000) : null;
|
|
169
|
-
return (_jsxs("div", { role: "status", "aria-live": "polite", className: cn("mx-3 mt-2 flex items-start gap-2.5 rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2.5 text-xs text-foreground", className), children: [_jsx(IconAlertTriangle, { size: 16, className: "mt-0.5 shrink-0 text-amber-500", "aria-hidden": "true" }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [_jsxs("div", { className: "leading-snug", children: [_jsx("span", { className: "font-medium", children:
|
|
180
|
+
return (_jsxs("div", { role: "status", "aria-live": "polite", className: cn("mx-3 mt-2 flex items-start gap-2.5 rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2.5 text-xs text-foreground", className), children: [_jsx(IconAlertTriangle, { size: 16, className: "mt-0.5 shrink-0 text-amber-500", "aria-hidden": "true" }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1.5", children: [_jsxs("div", { className: "leading-snug", children: [_jsx("span", { className: "font-medium", children: backgroundWorkerStillAlive
|
|
181
|
+
? "The agent is still working."
|
|
182
|
+
: "This chat looks stuck." }), " ", _jsxs("span", { className: "text-muted-foreground", children: ["No progress", stuckSeconds != null ? ` for ${stuckSeconds}s` : "", ".", " ", backgroundWorkerStillAlive
|
|
183
|
+
? "The background worker is still alive; large updates can take a few minutes."
|
|
184
|
+
: "The agent may have hit a server timeout or lost its connection.", autoRetry && busyType === "retry"
|
|
170
185
|
? " Retrying automatically now."
|
|
171
186
|
: ""] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("button", { type: "button", onClick: handleRetry, disabled: busyType !== "none", className: "inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md bg-foreground px-2.5 text-[11px] font-medium text-background transition-colors hover:bg-foreground/90 disabled:cursor-not-allowed disabled:opacity-60", children: [busyType === "retry" ? (_jsx(IconLoader2, { size: 12, className: "animate-spin", "aria-hidden": "true" })) : null, "Retry"] }), _jsxs("button", { type: "button", onClick: handleCancel, disabled: busyType !== "none", className: "inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-60", children: [busyType === "cancel" ? (_jsx(IconLoader2, { size: 12, className: "animate-spin", "aria-hidden": "true" })) : null, "Cancel"] })] })] })] }));
|
|
172
187
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RunStuckBanner.js","sourceRoot":"","sources":["../../src/client/RunStuckBanner.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEpD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EACL,oBAAoB,EACpB,WAAW,GAEZ,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AA+ChC,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAY9C,SAAS,sBAAsB;IAC7B,MAAM,SAAS,GACb,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU;QACtE,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,IAAI,CAAC;IACX,OAAO,SAAS,EAAE,UAAU,EAAE,IAAI,SAAS,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;AAC1E,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAgB,EAAE,KAAa;IACxD,OAAO,iCAAiC,QAAQ,IAAI,KAAK,EAAE,CAAC;AAC9D,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW,EAAE,OAAe;IACtD,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7C,IAAI,GAAG,EAAE,CAAC;YACR,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAG9B,CAAC;YACF,MAAM,SAAS,GACb,OAAO,QAAQ,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,IAAI,SAAS,GAAG,GAAG,IAAI,QAAQ,CAAC,OAAO,KAAK,OAAO;gBAAE,OAAO,KAAK,CAAC;QACpE,CAAC;QACD,MAAM,CAAC,YAAY,CAAC,OAAO,CACzB,GAAG,EACH,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,GAAG,uBAAuB,EAAE,CAAC,CACtE,CAAC;QACF,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAEpE,CAAC;QACF,OAAO,SAAS,CAAC,OAAO,KAAK,OAAO,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;QACvE,oEAAoE;QACpE,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,QAAmC,EACnC,KAAa,EACb,OAAe;IAEf,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,GAAG,GAAG,iBAAiB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC/C,MAAM,KAAK,GACT,OAAO,SAAS,KAAK,WAAW;QAC9B,CAAC,CAAE,SAAsD,CAAC,KAAK;QAC/D,CAAC,CAAC,SAAS,CAAC;IAChB,IAAI,KAAK,EAAE,OAAO,EAAE,CAAC;QACnB,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,OAAO,CACxB,GAAG,EACH,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,EAAE,EACxC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAC5D,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,OAAO,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAC7B,QAAQ,EACR,MAAM,EACN,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EAClB,SAAS,GAAG,KAAK,EACjB,gBAAgB,EAChB,SAAS,GACW;IACpB,MAAM,KAAK,GAAG,oBAAoB,CAAC,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAY,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9D,MAAM,oBAAoB,GAAG,MAAM,CAAc,IAAI,GAAG,EAAE,CAAC,CAAC;IAC5D,MAAM,mBAAmB,GAAG,MAAM,CAAgB,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,CAAC;QACjC,mBAAmB,CAAC,OAAO,GAAG,sBAAsB,EAAE,CAAC;IACzD,CAAC;IACD,MAAM,OAAO,GAAG,gBAAgB,IAAI,mBAAmB,CAAC,OAAO,CAAC;IAEhE,MAAM,eAAe,GAAG,MAAM,CAG3B,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,eAAe,CAAC,OAAO,CAAC;QACrC,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,KAAK;YAAE,OAAO;QACzE,eAAe,CAAC,OAAO,GAAG,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QACzE,kBAAkB,EAAE,CAAC,KAAK,CAAC,CAAC;QAC5B,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YACjC,UAAU,CAAC,2BAA2B,EAAE;gBACtC,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,QAAQ,EAAE,QAAQ,IAAI,IAAI;gBAC1B,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;gBACxC,aAAa,EACX,KAAK,CAAC,YAAY,IAAI,IAAI;oBACxB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC;oBACvC,CAAC,CAAC,IAAI;gBACV,SAAS,EAAE,KAAK,CAAC,MAAM;aACxB,CAAC,CAAC;QACL,CAAC;IACH,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE1C,yEAAyE;IACzE,0EAA0E;IAC1E,gDAAgD;IAChD,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAClB,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM;gBAAE,OAAO,OAAO,CAAC;YAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;gBAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YACxD,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,KAAK;gBAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YAC1E,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAEhC,SAAS,CAAC,GAAG,EAAE;QACb,IACE,CAAC,SAAS;YACV,CAAC,KAAK,CAAC,OAAO;YACd,CAAC,KAAK,CAAC,KAAK;YACZ,IAAI,CAAC,IAAI,KAAK,MAAM;YACpB,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAC7C,CAAC;YACD,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,KAAK,qBAAqB,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACpE,oBAAoB,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;YAClC,UAAU,CAAC,6BAA6B,EAAE;gBACxC,KAAK;gBACL,QAAQ,EAAE,QAAQ,IAAI,IAAI;gBAC1B,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;aACzC,CAAC,CAAC;YACH,KAAK,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;gBACxD,IAAI,OAAO,EAAE,CAAC;oBACZ,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;oBACnB,OAAO;gBACT,CAAC;gBACD,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAClB,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK;oBAChD,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE;oBAClB,CAAC,CAAC,OAAO,CACZ,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,EAAE;QACD,QAAQ;QACR,SAAS;QACT,IAAI;QACJ,OAAO;QACP,OAAO;QACP,KAAK,CAAC,OAAO;QACb,KAAK,CAAC,KAAK;QACX,KAAK,CAAC,YAAY;QAClB,QAAQ;KACT,CAAC,CAAC;IAEH,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAEhD,MAAM,YAAY,GAAG,KAAK,IAAI,EAAE;QAC9B,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO;QACjD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;QACnC,UAAU,CAAC,yBAAyB,EAAE;YACpC,KAAK;YACL,QAAQ,EAAE,QAAQ,IAAI,IAAI;YAC1B,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;SACzC,CAAC,CAAC;QACH,MAAM,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;IAC7C,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;QAC7B,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,OAAO;QACjD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC1B,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QAClC,UAAU,CAAC,wBAAwB,EAAE;YACnC,KAAK;YACL,QAAQ,EAAE,QAAQ,IAAI,IAAI;YAC1B,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;SACzC,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC;QAC1D,IAAI,OAAO;YAAE,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC;IAE3B,MAAM,YAAY,GAChB,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE5E,OAAO,CACL,eACE,IAAI,EAAC,QAAQ,eACH,QAAQ,EAClB,SAAS,EAAE,EAAE,CACX,8HAA8H,EAC9H,SAAS,CACV,aAED,KAAC,iBAAiB,IAChB,IAAI,EAAE,EAAE,EACR,SAAS,EAAC,gCAAgC,iBAC9B,MAAM,GAClB,EACF,eAAK,SAAS,EAAC,sCAAsC,aACnD,eAAK,SAAS,EAAC,cAAc,aAC3B,eAAM,SAAS,EAAC,aAAa,uCAA8B,EAAC,GAAG,EAC/D,gBAAM,SAAS,EAAC,uBAAuB,4BAEpC,YAAY,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,YAAY,GAAG,CAAC,CAAC,CAAC,EAAE,uEAEnD,SAAS,IAAI,QAAQ,KAAK,OAAO;wCAChC,CAAC,CAAC,8BAA8B;wCAChC,CAAC,CAAC,EAAE,IACD,IACH,EACN,eAAK,SAAS,EAAC,mCAAmC,aAChD,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,QAAQ,KAAK,MAAM,EAC7B,SAAS,EAAC,sNAAsN,aAE/N,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CACtB,KAAC,WAAW,IACV,IAAI,EAAE,EAAE,EACR,SAAS,EAAC,cAAc,iBACZ,MAAM,GAClB,CACH,CAAC,CAAC,CAAC,IAAI,aAED,EACT,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,YAAY,EACrB,QAAQ,EAAE,QAAQ,KAAK,MAAM,EAC7B,SAAS,EAAC,oOAAoO,aAE7O,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CACvB,KAAC,WAAW,IACV,IAAI,EAAE,EAAE,EACR,SAAS,EAAC,cAAc,iBACZ,MAAM,GAClB,CACH,CAAC,CAAC,CAAC,IAAI,cAED,IACL,IACF,IACF,CACP,CAAC;AACJ,CAAC","sourcesContent":["import { IconAlertTriangle, IconLoader2 } from \"@tabler/icons-react\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { trackEvent } from \"./analytics.js\";\nimport {\n useRunStuckDetection,\n useAbortRun,\n type RunStuckState,\n} from \"./use-run-stuck-detection.js\";\nimport { cn } from \"./utils.js\";\n\n/**\n * Surface a user-visible affordance when a chat run hasn't emitted any\n * events for an unusually long time. The adapter's silent reconnect logic\n * keeps trying in the background; this banner is the fallback when those\n * attempts haven't restored progress and the user is staring at a frozen\n * spinner.\n */\nexport interface RunStuckBannerProps {\n /** The thread to monitor. Pass null/undefined to disable. */\n threadId: string | null | undefined;\n /** API base path. Default `/_agent-native/agent-chat`. */\n apiUrl?: string;\n /**\n * Threshold above which an in-flight run is considered stuck.\n * Defaults to 90s (after the adapter's 75s no-progress reconnect\n * has had a chance to recover).\n */\n stuckThresholdMs?: number;\n /**\n * Called when the user clicks Retry. Implementations should re-prompt\n * the agent (typically via `chatHandle.sendMessage(...)`) — the banner\n * itself only handles aborting the prior run.\n */\n onRetry?: (runId: string) => void;\n /**\n * Called whenever the stuck state transitions. Useful for surfacing\n * observability events (Sentry, PostHog) at the call site.\n */\n onStuckStateChange?: (state: RunStuckState) => void;\n /**\n * Automatically abort and retry once when the server reports a run is\n * alive but has stopped making real progress. Manual controls remain visible\n * as the fallback if the automatic recovery cannot clear the run.\n */\n autoRetry?: boolean;\n /**\n * Stable browser-tab/window id used to coordinate automatic retries across\n * multiple mounted chat views for the same thread. A local id is generated\n * when omitted.\n */\n autoRetryOwnerId?: string;\n /** Extra class on the outer container. */\n className?: string;\n}\n\nconst AUTO_RETRY_CLAIM_TTL_MS = 5 * 60 * 1000;\n\ntype BusyState = { type: \"none\" } | { type: \"cancel\" | \"retry\"; runId: string };\n\ntype MaybeLockManager = {\n request<T>(\n name: string,\n options: { mode?: \"exclusive\" | \"shared\"; ifAvailable?: boolean },\n callback: (lock: unknown) => T | Promise<T>,\n ): Promise<T>;\n};\n\nfunction createAutoRetryOwnerId() {\n const cryptoApi =\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\n ? crypto\n : null;\n return cryptoApi?.randomUUID() ?? `owner-${Math.random().toString(36)}`;\n}\n\nfunction autoRetryClaimKey(threadId: string, runId: string) {\n return `agent-native:stuck-auto-retry:${threadId}:${runId}`;\n}\n\nfunction markAutoRetryClaim(key: string, ownerId: string) {\n if (typeof window === \"undefined\") return true;\n const now = Date.now();\n try {\n const raw = window.localStorage.getItem(key);\n if (raw) {\n const existing = JSON.parse(raw) as {\n ownerId?: unknown;\n expiresAt?: unknown;\n };\n const expiresAt =\n typeof existing.expiresAt === \"number\" ? existing.expiresAt : 0;\n if (expiresAt > now && existing.ownerId !== ownerId) return false;\n }\n window.localStorage.setItem(\n key,\n JSON.stringify({ ownerId, expiresAt: now + AUTO_RETRY_CLAIM_TTL_MS }),\n );\n const confirmed = JSON.parse(window.localStorage.getItem(key) ?? \"{}\") as {\n ownerId?: unknown;\n };\n return confirmed.ownerId === ownerId;\n } catch {\n // Private browsing or disabled storage: fall back to in-tab retry. The\n // server abort is idempotent, and manual controls remain available.\n return true;\n }\n}\n\nasync function claimAutoRetryAttempt(\n threadId: string | null | undefined,\n runId: string,\n ownerId: string,\n) {\n if (!threadId) return true;\n const key = autoRetryClaimKey(threadId, runId);\n const locks =\n typeof navigator !== \"undefined\"\n ? (navigator as Navigator & { locks?: MaybeLockManager }).locks\n : undefined;\n if (locks?.request) {\n try {\n return await locks.request(\n key,\n { mode: \"exclusive\", ifAvailable: true },\n (lock) => (lock ? markAutoRetryClaim(key, ownerId) : false),\n );\n } catch {\n return markAutoRetryClaim(key, ownerId);\n }\n }\n return markAutoRetryClaim(key, ownerId);\n}\n\nexport function RunStuckBanner({\n threadId,\n apiUrl,\n stuckThresholdMs,\n onRetry,\n onStuckStateChange,\n autoRetry = false,\n autoRetryOwnerId,\n className,\n}: RunStuckBannerProps) {\n const state = useRunStuckDetection({ threadId, stuckThresholdMs, apiUrl });\n const abortRun = useAbortRun(apiUrl);\n const [busy, setBusy] = useState<BusyState>({ type: \"none\" });\n const autoRetriedRunIdsRef = useRef<Set<string>>(new Set());\n const generatedOwnerIdRef = useRef<string | null>(null);\n if (!generatedOwnerIdRef.current) {\n generatedOwnerIdRef.current = createAutoRetryOwnerId();\n }\n const ownerId = autoRetryOwnerId ?? generatedOwnerIdRef.current;\n\n const lastReportedRef = useRef<{\n isStuck: boolean;\n runId: string | null;\n }>({ isStuck: false, runId: null });\n useEffect(() => {\n const last = lastReportedRef.current;\n if (last.isStuck === state.isStuck && last.runId === state.runId) return;\n lastReportedRef.current = { isStuck: state.isStuck, runId: state.runId };\n onStuckStateChange?.(state);\n if (state.isStuck && state.runId) {\n trackEvent(\"agent_chat_stuck_detected\", {\n runId: state.runId,\n threadId: threadId ?? null,\n stuckSinceMs: state.stuckSinceMs ?? null,\n stuckSinceSec:\n state.stuckSinceMs != null\n ? Math.floor(state.stuckSinceMs / 1000)\n : null,\n runStatus: state.status,\n });\n }\n }, [state, onStuckStateChange, threadId]);\n\n // Reset the busy spinner once the underlying run is no longer the one we\n // acted on. A recovery continuation can start quickly enough that polling\n // never observes an idle state between run ids.\n useEffect(() => {\n setBusy((current) => {\n if (current.type === \"none\") return current;\n if (state.status !== \"running\") return { type: \"none\" };\n if (state.runId && state.runId !== current.runId) return { type: \"none\" };\n return current;\n });\n }, [state.runId, state.status]);\n\n useEffect(() => {\n if (\n !autoRetry ||\n !state.isStuck ||\n !state.runId ||\n busy.type !== \"none\" ||\n autoRetriedRunIdsRef.current.has(state.runId)\n ) {\n return;\n }\n\n const runId = state.runId;\n void claimAutoRetryAttempt(threadId, runId, ownerId).then((claimed) => {\n autoRetriedRunIdsRef.current.add(runId);\n if (!claimed) return;\n setBusy({ type: \"retry\", runId });\n trackEvent(\"agent_chat_stuck_auto_retry\", {\n runId,\n threadId: threadId ?? null,\n stuckSinceMs: state.stuckSinceMs ?? null,\n });\n void abortRun(runId, \"auto_stuck_retry\").then((aborted) => {\n if (aborted) {\n onRetry?.(aborted);\n return;\n }\n setBusy((current) =>\n current.type !== \"none\" && current.runId === runId\n ? { type: \"none\" }\n : current,\n );\n });\n });\n }, [\n abortRun,\n autoRetry,\n busy,\n onRetry,\n ownerId,\n state.isStuck,\n state.runId,\n state.stuckSinceMs,\n threadId,\n ]);\n\n if (!state.isStuck || !state.runId) return null;\n\n const handleCancel = async () => {\n if (!state.runId || busy.type !== \"none\") return;\n const runId = state.runId;\n setBusy({ type: \"cancel\", runId });\n trackEvent(\"agent_chat_stuck_cancel\", {\n runId,\n threadId: threadId ?? null,\n stuckSinceMs: state.stuckSinceMs ?? null,\n });\n await abortRun(runId, \"user_stuck_cancel\");\n };\n\n const handleRetry = async () => {\n if (!state.runId || busy.type !== \"none\") return;\n const runId = state.runId;\n setBusy({ type: \"retry\", runId });\n trackEvent(\"agent_chat_stuck_retry\", {\n runId,\n threadId: threadId ?? null,\n stuckSinceMs: state.stuckSinceMs ?? null,\n });\n const aborted = await abortRun(runId, \"user_stuck_retry\");\n if (aborted) onRetry?.(aborted);\n };\n\n const busyType = busy.type;\n\n const stuckSeconds =\n state.stuckSinceMs != null ? Math.floor(state.stuckSinceMs / 1000) : null;\n\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={cn(\n \"mx-3 mt-2 flex items-start gap-2.5 rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2.5 text-xs text-foreground\",\n className,\n )}\n >\n <IconAlertTriangle\n size={16}\n className=\"mt-0.5 shrink-0 text-amber-500\"\n aria-hidden=\"true\"\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <div className=\"leading-snug\">\n <span className=\"font-medium\">This chat looks stuck.</span>{\" \"}\n <span className=\"text-muted-foreground\">\n No progress\n {stuckSeconds != null ? ` for ${stuckSeconds}s` : \"\"}. The agent may\n have hit a server timeout or lost its connection.\n {autoRetry && busyType === \"retry\"\n ? \" Retrying automatically now.\"\n : \"\"}\n </span>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleRetry}\n disabled={busyType !== \"none\"}\n className=\"inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md bg-foreground px-2.5 text-[11px] font-medium text-background transition-colors hover:bg-foreground/90 disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {busyType === \"retry\" ? (\n <IconLoader2\n size={12}\n className=\"animate-spin\"\n aria-hidden=\"true\"\n />\n ) : null}\n Retry\n </button>\n <button\n type=\"button\"\n onClick={handleCancel}\n disabled={busyType !== \"none\"}\n className=\"inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {busyType === \"cancel\" ? (\n <IconLoader2\n size={12}\n className=\"animate-spin\"\n aria-hidden=\"true\"\n />\n ) : null}\n Cancel\n </button>\n </div>\n </div>\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"RunStuckBanner.js","sourceRoot":"","sources":["../../src/client/RunStuckBanner.tsx"],"names":[],"mappings":"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","sourcesContent":["import { IconAlertTriangle, IconLoader2 } from \"@tabler/icons-react\";\nimport { useEffect, useRef, useState } from \"react\";\n\nimport { trackEvent } from \"./analytics.js\";\nimport {\n useRunStuckDetection,\n useAbortRun,\n type RunStuckState,\n} from \"./use-run-stuck-detection.js\";\nimport { cn } from \"./utils.js\";\n\n/**\n * Surface a user-visible affordance when a chat run hasn't emitted any\n * events for an unusually long time. The adapter's silent reconnect logic\n * keeps trying in the background; this banner is the fallback when those\n * attempts haven't restored progress and the user is staring at a frozen\n * spinner.\n */\nexport interface RunStuckBannerProps {\n /** The thread to monitor. Pass null/undefined to disable. */\n threadId: string | null | undefined;\n /** API base path. Default `/_agent-native/agent-chat`. */\n apiUrl?: string;\n /**\n * Threshold above which an in-flight run is considered stuck.\n * Defaults to 90s (after the adapter's 75s no-progress reconnect\n * has had a chance to recover).\n */\n stuckThresholdMs?: number;\n /**\n * Called when the user clicks Retry. Implementations should re-prompt\n * the agent (typically via `chatHandle.sendMessage(...)`) — the banner\n * itself only handles aborting the prior run.\n */\n onRetry?: (runId: string) => void;\n /**\n * Called whenever the stuck state transitions. Useful for surfacing\n * observability events (Sentry, PostHog) at the call site.\n */\n onStuckStateChange?: (state: RunStuckState) => void;\n /**\n * Automatically abort and retry once when the server reports a run is\n * alive but has stopped making real progress. Manual controls remain visible\n * as the fallback if the automatic recovery cannot clear the run.\n */\n autoRetry?: boolean;\n /**\n * Stable browser-tab/window id used to coordinate automatic retries across\n * multiple mounted chat views for the same thread. A local id is generated\n * when omitted.\n */\n autoRetryOwnerId?: string;\n /** Extra class on the outer container. */\n className?: string;\n}\n\nconst AUTO_RETRY_CLAIM_TTL_MS = 5 * 60 * 1000;\nconst BACKGROUND_WORKER_FRESH_HEARTBEAT_MS = 30_000;\n\ntype BusyState = { type: \"none\" } | { type: \"cancel\" | \"retry\"; runId: string };\n\ntype MaybeLockManager = {\n request<T>(\n name: string,\n options: { mode?: \"exclusive\" | \"shared\"; ifAvailable?: boolean },\n callback: (lock: unknown) => T | Promise<T>,\n ): Promise<T>;\n};\n\nfunction createAutoRetryOwnerId() {\n const cryptoApi =\n typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\"\n ? crypto\n : null;\n return cryptoApi?.randomUUID() ?? `owner-${Math.random().toString(36)}`;\n}\n\nfunction autoRetryClaimKey(threadId: string, runId: string) {\n return `agent-native:stuck-auto-retry:${threadId}:${runId}`;\n}\n\nfunction isFreshBackgroundWorker(state: RunStuckState): boolean {\n return Boolean(\n state.status === \"running\" &&\n state.dispatchMode === \"background-processing\" &&\n state.heartbeatSinceMs != null &&\n state.heartbeatSinceMs >= 0 &&\n state.heartbeatSinceMs < BACKGROUND_WORKER_FRESH_HEARTBEAT_MS,\n );\n}\n\nfunction markAutoRetryClaim(key: string, ownerId: string) {\n if (typeof window === \"undefined\") return true;\n const now = Date.now();\n try {\n const raw = window.localStorage.getItem(key);\n if (raw) {\n const existing = JSON.parse(raw) as {\n ownerId?: unknown;\n expiresAt?: unknown;\n };\n const expiresAt =\n typeof existing.expiresAt === \"number\" ? existing.expiresAt : 0;\n if (expiresAt > now && existing.ownerId !== ownerId) return false;\n }\n window.localStorage.setItem(\n key,\n JSON.stringify({ ownerId, expiresAt: now + AUTO_RETRY_CLAIM_TTL_MS }),\n );\n const confirmed = JSON.parse(window.localStorage.getItem(key) ?? \"{}\") as {\n ownerId?: unknown;\n };\n return confirmed.ownerId === ownerId;\n } catch {\n // Private browsing or disabled storage: fall back to in-tab retry. The\n // server abort is idempotent, and manual controls remain available.\n return true;\n }\n}\n\nasync function claimAutoRetryAttempt(\n threadId: string | null | undefined,\n runId: string,\n ownerId: string,\n) {\n if (!threadId) return true;\n const key = autoRetryClaimKey(threadId, runId);\n const locks =\n typeof navigator !== \"undefined\"\n ? (navigator as Navigator & { locks?: MaybeLockManager }).locks\n : undefined;\n if (locks?.request) {\n try {\n return await locks.request(\n key,\n { mode: \"exclusive\", ifAvailable: true },\n (lock) => (lock ? markAutoRetryClaim(key, ownerId) : false),\n );\n } catch {\n return markAutoRetryClaim(key, ownerId);\n }\n }\n return markAutoRetryClaim(key, ownerId);\n}\n\nexport function RunStuckBanner({\n threadId,\n apiUrl,\n stuckThresholdMs,\n onRetry,\n onStuckStateChange,\n autoRetry = false,\n autoRetryOwnerId,\n className,\n}: RunStuckBannerProps) {\n const state = useRunStuckDetection({ threadId, stuckThresholdMs, apiUrl });\n const abortRun = useAbortRun(apiUrl);\n const [busy, setBusy] = useState<BusyState>({ type: \"none\" });\n const autoRetriedRunIdsRef = useRef<Set<string>>(new Set());\n const generatedOwnerIdRef = useRef<string | null>(null);\n if (!generatedOwnerIdRef.current) {\n generatedOwnerIdRef.current = createAutoRetryOwnerId();\n }\n const ownerId = autoRetryOwnerId ?? generatedOwnerIdRef.current;\n const backgroundWorkerStillAlive = isFreshBackgroundWorker(state);\n\n const lastReportedRef = useRef<{\n isStuck: boolean;\n runId: string | null;\n }>({ isStuck: false, runId: null });\n useEffect(() => {\n const last = lastReportedRef.current;\n if (last.isStuck === state.isStuck && last.runId === state.runId) return;\n lastReportedRef.current = { isStuck: state.isStuck, runId: state.runId };\n onStuckStateChange?.(state);\n if (state.isStuck && state.runId) {\n trackEvent(\"agent_chat_stuck_detected\", {\n runId: state.runId,\n threadId: threadId ?? null,\n stuckSinceMs: state.stuckSinceMs ?? null,\n stuckSinceSec:\n state.stuckSinceMs != null\n ? Math.floor(state.stuckSinceMs / 1000)\n : null,\n runStatus: state.status,\n });\n }\n }, [state, onStuckStateChange, threadId]);\n\n // Reset the busy spinner once the underlying run is no longer the one we\n // acted on. A recovery continuation can start quickly enough that polling\n // never observes an idle state between run ids.\n useEffect(() => {\n setBusy((current) => {\n if (current.type === \"none\") return current;\n if (state.status !== \"running\") return { type: \"none\" };\n if (state.runId && state.runId !== current.runId) return { type: \"none\" };\n return current;\n });\n }, [state.runId, state.status]);\n\n useEffect(() => {\n if (\n !autoRetry ||\n backgroundWorkerStillAlive ||\n !state.isStuck ||\n !state.runId ||\n busy.type !== \"none\" ||\n autoRetriedRunIdsRef.current.has(state.runId)\n ) {\n return;\n }\n\n const runId = state.runId;\n void claimAutoRetryAttempt(threadId, runId, ownerId).then((claimed) => {\n autoRetriedRunIdsRef.current.add(runId);\n if (!claimed) return;\n setBusy({ type: \"retry\", runId });\n trackEvent(\"agent_chat_stuck_auto_retry\", {\n runId,\n threadId: threadId ?? null,\n stuckSinceMs: state.stuckSinceMs ?? null,\n });\n void abortRun(runId, \"auto_stuck_retry\").then((aborted) => {\n if (aborted) {\n onRetry?.(aborted);\n return;\n }\n setBusy((current) =>\n current.type !== \"none\" && current.runId === runId\n ? { type: \"none\" }\n : current,\n );\n });\n });\n }, [\n abortRun,\n autoRetry,\n backgroundWorkerStillAlive,\n busy,\n onRetry,\n ownerId,\n state.isStuck,\n state.runId,\n state.stuckSinceMs,\n threadId,\n ]);\n\n if (!state.isStuck || !state.runId) return null;\n\n const handleCancel = async () => {\n if (!state.runId || busy.type !== \"none\") return;\n const runId = state.runId;\n setBusy({ type: \"cancel\", runId });\n trackEvent(\"agent_chat_stuck_cancel\", {\n runId,\n threadId: threadId ?? null,\n stuckSinceMs: state.stuckSinceMs ?? null,\n });\n await abortRun(runId, \"user_stuck_cancel\");\n };\n\n const handleRetry = async () => {\n if (!state.runId || busy.type !== \"none\") return;\n const runId = state.runId;\n setBusy({ type: \"retry\", runId });\n trackEvent(\"agent_chat_stuck_retry\", {\n runId,\n threadId: threadId ?? null,\n stuckSinceMs: state.stuckSinceMs ?? null,\n });\n const aborted = await abortRun(runId, \"user_stuck_retry\");\n if (aborted) onRetry?.(aborted);\n };\n\n const busyType = busy.type;\n\n const stuckSeconds =\n state.stuckSinceMs != null ? Math.floor(state.stuckSinceMs / 1000) : null;\n\n return (\n <div\n role=\"status\"\n aria-live=\"polite\"\n className={cn(\n \"mx-3 mt-2 flex items-start gap-2.5 rounded-md border border-amber-500/40 bg-amber-500/10 px-3 py-2.5 text-xs text-foreground\",\n className,\n )}\n >\n <IconAlertTriangle\n size={16}\n className=\"mt-0.5 shrink-0 text-amber-500\"\n aria-hidden=\"true\"\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-1.5\">\n <div className=\"leading-snug\">\n <span className=\"font-medium\">\n {backgroundWorkerStillAlive\n ? \"The agent is still working.\"\n : \"This chat looks stuck.\"}\n </span>{\" \"}\n <span className=\"text-muted-foreground\">\n No progress\n {stuckSeconds != null ? ` for ${stuckSeconds}s` : \"\"}.{\" \"}\n {backgroundWorkerStillAlive\n ? \"The background worker is still alive; large updates can take a few minutes.\"\n : \"The agent may have hit a server timeout or lost its connection.\"}\n {autoRetry && busyType === \"retry\"\n ? \" Retrying automatically now.\"\n : \"\"}\n </span>\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleRetry}\n disabled={busyType !== \"none\"}\n className=\"inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md bg-foreground px-2.5 text-[11px] font-medium text-background transition-colors hover:bg-foreground/90 disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {busyType === \"retry\" ? (\n <IconLoader2\n size={12}\n className=\"animate-spin\"\n aria-hidden=\"true\"\n />\n ) : null}\n Retry\n </button>\n <button\n type=\"button\"\n onClick={handleCancel}\n disabled={busyType !== \"none\"}\n className=\"inline-flex h-7 cursor-pointer items-center gap-1.5 rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-60\"\n >\n {busyType === \"cancel\" ? (\n <IconLoader2\n size={12}\n className=\"animate-spin\"\n aria-hidden=\"true\"\n />\n ) : null}\n Cancel\n </button>\n </div>\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-chat-adapter.d.ts","sourceRoot":"","sources":["../../src/client/agent-chat-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,qBAAqB,CAAC;AAMhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAkBrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,oBAAoB;AAC9B;;;;GAIG;AACD,KAAK;AACP,0EAA0E;GACxE,WAAW,CAAC;AAssChB;;;;GAIG;AACH;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CACpC,OAAO,CAAC,EAAE,6BAA6B,GACtC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"agent-chat-adapter.d.ts","sourceRoot":"","sources":["../../src/client/agent-chat-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAsB,MAAM,qBAAqB,CAAC;AAMhF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAkBrE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,MAAM,oBAAoB;AAC9B;;;;GAIG;AACD,KAAK;AACP,0EAA0E;GACxE,WAAW,CAAC;AAssChB;;;;GAIG;AACH;;;GAGG;AACH,MAAM,WAAW,6BAA6B;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,SAAS,CAAA;KAAE,CAAC;IACrD,WAAW,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACxD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE;QAAE,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED,wBAAgB,sBAAsB,CACpC,OAAO,CAAC,EAAE,6BAA6B,GACtC,gBAAgB,CAihDlB"}
|
|
@@ -1092,6 +1092,7 @@ export function createAgentChatAdapter(options) {
|
|
|
1092
1092
|
const turnId = generateTurnId();
|
|
1093
1093
|
let runId = null;
|
|
1094
1094
|
let lastSeq = -1;
|
|
1095
|
+
let currentRunDispatchMode = null;
|
|
1095
1096
|
let currentMessageText = normalizeMentions(recoveryMessageText.trim() || userMessageText);
|
|
1096
1097
|
let currentHistory = history;
|
|
1097
1098
|
let currentStructuredHistory = structuredHistory;
|
|
@@ -1237,6 +1238,16 @@ export function createAgentChatAdapter(options) {
|
|
|
1237
1238
|
return false;
|
|
1238
1239
|
}
|
|
1239
1240
|
};
|
|
1241
|
+
const updateCurrentRunDispatchMode = (value) => {
|
|
1242
|
+
if (typeof value !== "string")
|
|
1243
|
+
return;
|
|
1244
|
+
const mode = value.trim();
|
|
1245
|
+
if (mode)
|
|
1246
|
+
currentRunDispatchMode = mode;
|
|
1247
|
+
};
|
|
1248
|
+
const currentSSEOptions = () => ({
|
|
1249
|
+
durableBackgroundRun: currentRunDispatchMode?.startsWith("background") === true,
|
|
1250
|
+
});
|
|
1240
1251
|
const captureChatClientError = (error, phase, extra = {}) => {
|
|
1241
1252
|
captureError(error, {
|
|
1242
1253
|
tags: {
|
|
@@ -1329,11 +1340,12 @@ export function createAgentChatAdapter(options) {
|
|
|
1329
1340
|
reconnectErrorCaptured = true;
|
|
1330
1341
|
break;
|
|
1331
1342
|
}
|
|
1343
|
+
updateCurrentRunDispatchMode(reconnectRes.headers.get("X-Dispatch-Mode"));
|
|
1332
1344
|
for await (const result of readSSEStream(reconnectRes.body, content, toolCallCounter, tabId, (seq) => {
|
|
1333
1345
|
lastSeq = seq;
|
|
1334
1346
|
if (threadId)
|
|
1335
1347
|
updateActiveRunSeq(seq);
|
|
1336
|
-
}, runId)) {
|
|
1348
|
+
}, runId, currentSSEOptions())) {
|
|
1337
1349
|
yield withRequestModeMetadata(result);
|
|
1338
1350
|
}
|
|
1339
1351
|
clearActiveRun();
|
|
@@ -1398,6 +1410,7 @@ export function createAgentChatAdapter(options) {
|
|
|
1398
1410
|
}
|
|
1399
1411
|
const active = await activeRes.json();
|
|
1400
1412
|
if (active?.active && active.runId) {
|
|
1413
|
+
updateCurrentRunDispatchMode(active.dispatchMode);
|
|
1401
1414
|
const activeStatus = typeof active.status === "string" ? active.status : "";
|
|
1402
1415
|
const activeTurnId = typeof active.turnId === "string" ? active.turnId : "";
|
|
1403
1416
|
if (activeStatus !== "running" && activeTurnId !== turnId) {
|
|
@@ -1458,6 +1471,7 @@ export function createAgentChatAdapter(options) {
|
|
|
1458
1471
|
const dispatchMode = typeof active.dispatchMode === "string"
|
|
1459
1472
|
? active.dispatchMode
|
|
1460
1473
|
: "";
|
|
1474
|
+
updateCurrentRunDispatchMode(dispatchMode);
|
|
1461
1475
|
if (activeRunId === interruptedRunId) {
|
|
1462
1476
|
if (dispatchMode.startsWith("background"))
|
|
1463
1477
|
continue;
|
|
@@ -1974,6 +1988,7 @@ export function createAgentChatAdapter(options) {
|
|
|
1974
1988
|
}
|
|
1975
1989
|
// Track the run ID for reconnection
|
|
1976
1990
|
runId = res.headers.get("X-Run-Id");
|
|
1991
|
+
updateCurrentRunDispatchMode(res.headers.get("X-Dispatch-Mode"));
|
|
1977
1992
|
if (runId && !attemptedRunIds.includes(runId)) {
|
|
1978
1993
|
attemptedRunIds.push(runId);
|
|
1979
1994
|
}
|
|
@@ -1985,7 +2000,7 @@ export function createAgentChatAdapter(options) {
|
|
|
1985
2000
|
if (runId && threadId) {
|
|
1986
2001
|
updateActiveRunSeq(seq);
|
|
1987
2002
|
}
|
|
1988
|
-
}, runId)) {
|
|
2003
|
+
}, runId, currentSSEOptions())) {
|
|
1989
2004
|
yield withRequestModeMetadata(result);
|
|
1990
2005
|
}
|
|
1991
2006
|
// Run completed normally — clear active run state
|