@agent-native/core 0.92.2 → 0.92.3
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 +2 -2
- package/corpus/core/CHANGELOG.md +26 -0
- package/corpus/core/docs/content/locales/ar-SA/server.mdx +35 -0
- package/corpus/core/docs/content/locales/de-DE/server.mdx +35 -0
- package/corpus/core/docs/content/locales/es-ES/server.mdx +35 -0
- package/corpus/core/docs/content/locales/fr-FR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/hi-IN/server.mdx +35 -0
- package/corpus/core/docs/content/locales/ja-JP/server.mdx +35 -0
- package/corpus/core/docs/content/locales/ko-KR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/pt-BR/server.mdx +35 -0
- package/corpus/core/docs/content/locales/zh-CN/server.mdx +35 -0
- package/corpus/core/docs/content/locales/zh-TW/server.mdx +35 -0
- package/corpus/core/docs/content/server.mdx +35 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -4
- package/corpus/core/src/agent/engine/anthropic-engine.ts +15 -3
- package/corpus/core/src/agent/engine/builder-engine.ts +30 -4
- package/corpus/core/src/agent/engine/registry.ts +63 -2
- package/corpus/core/src/agent/model-config.ts +0 -2
- package/corpus/core/src/agent/production-agent.ts +12 -1
- package/corpus/core/src/agent/run-manager.ts +57 -36
- package/corpus/core/src/agent/run-store.ts +81 -2
- package/corpus/core/src/agent/thread-data-builder.ts +33 -2
- package/corpus/core/src/client/AssistantChat.tsx +314 -96
- package/corpus/core/src/client/agent-chat-adapter.ts +17 -1
- package/corpus/core/src/client/chat/repo-helpers.ts +45 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +36 -27
- package/corpus/core/src/client/chat-model-groups.ts +1 -5
- package/corpus/core/src/client/composer/TiptapComposer.tsx +0 -2
- package/corpus/core/src/client/embed-auth.ts +20 -0
- package/corpus/core/src/client/settings/SettingsSection.tsx +66 -5
- package/corpus/core/src/client/sse-event-processor.ts +49 -17
- package/corpus/core/src/client/use-db-sync.ts +31 -9
- package/corpus/core/src/collab/agent-presence.ts +6 -1
- package/corpus/core/src/collab/awareness-store.ts +185 -0
- package/corpus/core/src/collab/awareness.ts +58 -2
- package/corpus/core/src/scripts/agent-engines/manage-agent-engine.ts +7 -8
- package/corpus/core/src/scripts/agent-engines/set-agent-engine.ts +7 -18
- package/corpus/core/src/server/core-routes-plugin.ts +21 -2
- package/corpus/core/src/server/embed-session.ts +20 -4
- package/corpus/core/src/styles/agent-native.css +51 -0
- package/corpus/core/src/tracking/providers.ts +33 -2
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +8 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +9 -1
- package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorDetail.tsx +41 -11
- package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +44 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-action-menus-now-close-cleanly-before-opening-hist.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-dashboard-email-reports-still-send-when-the-screenshot-captu.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-alerts-now-suppress-recovery-noise-during-flapping-an.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-check-history-now-shows-timing-diagnostics-so-slow-or.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-09-uptime-recovery-alerts-now-send-when-the-original-outage-not.md +6 -0
- package/corpus/templates/analytics/docs/uptime-monitoring.md +29 -16
- package/corpus/templates/analytics/netlify.toml +3 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +126 -0
- package/corpus/templates/analytics/server/db/schema-monitoring.ts +5 -0
- package/corpus/templates/analytics/server/jobs/uptime-monitors.ts +5 -1
- package/corpus/templates/analytics/server/lib/uptime-monitors.ts +305 -31
- package/corpus/templates/analytics/server/plugins/db.ts +15 -0
- package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -1
- package/corpus/templates/analytics/server/routes/api/uptime-monitors/run.post.ts +57 -0
- package/corpus/templates/clips/actions/list-meetings.ts +12 -0
- package/corpus/templates/clips/changelog/2026-07-09-meeting-reminders-now-ignore-obvious-solo-personal-calendar-.md +6 -0
- package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +1 -0
- package/corpus/templates/clips/server/lib/calendar-event-classification.ts +79 -0
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +13 -4
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +14 -3
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +26 -4
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +47 -2
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/model-config.d.ts +2 -2
- package/dist/agent/model-config.d.ts.map +1 -1
- package/dist/agent/model-config.js +0 -2
- package/dist/agent/model-config.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +12 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +52 -33
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +24 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +64 -2
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +28 -2
- package/dist/agent/thread-data-builder.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 +243 -36
- 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 +15 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/repo-helpers.d.ts.map +1 -1
- package/dist/client/chat/repo-helpers.js +43 -0
- package/dist/client/chat/repo-helpers.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +4 -4
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +1 -3
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +0 -2
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +14 -0
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/settings/SettingsSection.d.ts.map +1 -1
- package/dist/client/settings/SettingsSection.js +32 -3
- package/dist/client/settings/SettingsSection.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +44 -17
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +29 -9
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/collab/agent-presence.d.ts.map +1 -1
- package/dist/collab/agent-presence.js +5 -2
- package/dist/collab/agent-presence.js.map +1 -1
- package/dist/collab/awareness-store.d.ts +1 -1
- package/dist/collab/awareness-store.d.ts.map +1 -1
- package/dist/collab/awareness-store.js +0 -0
- package/dist/collab/awareness-store.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -0
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/awareness.js +46 -2
- package/dist/collab/awareness.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/manage-agent-engine.js +6 -6
- package/dist/scripts/agent-engines/manage-agent-engine.js.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.d.ts.map +1 -1
- package/dist/scripts/agent-engines/set-agent-engine.js +5 -13
- package/dist/scripts/agent-engines/set-agent-engine.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +5 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +10 -3
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/embed-session.d.ts.map +1 -1
- package/dist/server/embed-session.js +17 -4
- package/dist/server/embed-session.js.map +1 -1
- package/dist/styles/agent-native.css +51 -0
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +23 -5
- package/dist/tracking/providers.js.map +1 -1
- package/docs/content/locales/ar-SA/server.mdx +35 -0
- package/docs/content/locales/de-DE/server.mdx +35 -0
- package/docs/content/locales/es-ES/server.mdx +35 -0
- package/docs/content/locales/fr-FR/server.mdx +35 -0
- package/docs/content/locales/hi-IN/server.mdx +35 -0
- package/docs/content/locales/ja-JP/server.mdx +35 -0
- package/docs/content/locales/ko-KR/server.mdx +35 -0
- package/docs/content/locales/pt-BR/server.mdx +35 -0
- package/docs/content/locales/zh-CN/server.mdx +35 -0
- package/docs/content/locales/zh-TW/server.mdx +35 -0
- package/docs/content/server.mdx +35 -0
- package/package.json +1 -1
|
@@ -366,6 +366,44 @@ function cloneContentParts(content: ContentPart[]): ContentPart[] {
|
|
|
366
366
|
);
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
export function settleInterruptedAssistantToolCallsInRepo<
|
|
370
|
+
T extends { messages?: unknown[] },
|
|
371
|
+
>(repo: T): { repo: T; changed: boolean } {
|
|
372
|
+
if (!Array.isArray(repo.messages)) return { repo, changed: false };
|
|
373
|
+
let changed = false;
|
|
374
|
+
const nextMessages = repo.messages.map((entry) => {
|
|
375
|
+
const wrapper =
|
|
376
|
+
entry && typeof entry === "object" && "message" in entry
|
|
377
|
+
? (entry as { message?: unknown })
|
|
378
|
+
: null;
|
|
379
|
+
const message = (wrapper?.message ?? entry) as
|
|
380
|
+
| { role?: unknown; content?: unknown; status?: unknown }
|
|
381
|
+
| null
|
|
382
|
+
| undefined;
|
|
383
|
+
if (message?.role !== "assistant" || !Array.isArray(message.content)) {
|
|
384
|
+
return entry;
|
|
385
|
+
}
|
|
386
|
+
const content = cloneContentParts(message.content as ContentPart[]);
|
|
387
|
+
if (
|
|
388
|
+
!settleInterruptedToolCalls(content, undefined, { includeActivity: true })
|
|
389
|
+
) {
|
|
390
|
+
return entry;
|
|
391
|
+
}
|
|
392
|
+
changed = true;
|
|
393
|
+
const nextMessage = {
|
|
394
|
+
...message,
|
|
395
|
+
content,
|
|
396
|
+
status: { type: "incomplete", reason: "error" },
|
|
397
|
+
};
|
|
398
|
+
return wrapper
|
|
399
|
+
? { ...(entry as Record<string, unknown>), message: nextMessage }
|
|
400
|
+
: nextMessage;
|
|
401
|
+
});
|
|
402
|
+
return changed
|
|
403
|
+
? { repo: { ...repo, messages: nextMessages }, changed }
|
|
404
|
+
: { repo, changed };
|
|
405
|
+
}
|
|
406
|
+
|
|
369
407
|
function clearPendingSelection() {
|
|
370
408
|
fetch(
|
|
371
409
|
agentNativePath(
|
|
@@ -586,6 +624,28 @@ function toolCallPartHasResult(part: unknown): boolean {
|
|
|
586
624
|
return candidate.type === "tool-call" && "result" in candidate;
|
|
587
625
|
}
|
|
588
626
|
|
|
627
|
+
/**
|
|
628
|
+
* Monotonic progress rank for a tool-call part. Higher means the UI should
|
|
629
|
+
* prefer this copy over a lower-ranked duplicate of the same logical call.
|
|
630
|
+
* Used so reconnect overlays that are ahead of lagging thread messages stay
|
|
631
|
+
* visible instead of flickering back to an older pending spinner.
|
|
632
|
+
*/
|
|
633
|
+
function toolCallProgressRank(part: unknown): number {
|
|
634
|
+
if (!part || typeof part !== "object") return 0;
|
|
635
|
+
const candidate = part as {
|
|
636
|
+
type?: unknown;
|
|
637
|
+
result?: unknown;
|
|
638
|
+
activity?: unknown;
|
|
639
|
+
argsText?: unknown;
|
|
640
|
+
};
|
|
641
|
+
if (candidate.type !== "tool-call") return 0;
|
|
642
|
+
if ("result" in candidate) return 4;
|
|
643
|
+
if (candidate.activity === true) return 1;
|
|
644
|
+
return typeof candidate.argsText === "string" && candidate.argsText.length > 0
|
|
645
|
+
? 2
|
|
646
|
+
: 1;
|
|
647
|
+
}
|
|
648
|
+
|
|
589
649
|
/**
|
|
590
650
|
* Identity fingerprint for a tool-call part that survives across readers: two
|
|
591
651
|
* readers of the same run assign unrelated synthetic toolCallIds until both
|
|
@@ -615,10 +675,10 @@ function toolCallFingerprintFromContentPart(part: unknown): string | null {
|
|
|
615
675
|
}
|
|
616
676
|
|
|
617
677
|
function collectRenderedToolCallStates(messages: readonly unknown[]): {
|
|
618
|
-
byId: Map<string, {
|
|
619
|
-
latestAssistantByFingerprint: Map<string, {
|
|
678
|
+
byId: Map<string, { rank: number }>;
|
|
679
|
+
latestAssistantByFingerprint: Map<string, { rank: number }>;
|
|
620
680
|
} {
|
|
621
|
-
const byId = new Map<string, {
|
|
681
|
+
const byId = new Map<string, { rank: number }>();
|
|
622
682
|
for (const message of messages) {
|
|
623
683
|
const msg = (message as { message?: unknown })?.message ?? message;
|
|
624
684
|
const content = (msg as { content?: unknown })?.content;
|
|
@@ -626,18 +686,15 @@ function collectRenderedToolCallStates(messages: readonly unknown[]): {
|
|
|
626
686
|
for (const part of content) {
|
|
627
687
|
const id = toolCallIdFromContentPart(part);
|
|
628
688
|
if (!id) continue;
|
|
629
|
-
const
|
|
689
|
+
const rank = toolCallProgressRank(part);
|
|
630
690
|
const existing = byId.get(id);
|
|
631
691
|
byId.set(id, {
|
|
632
|
-
|
|
692
|
+
rank: Math.max(existing?.rank ?? 0, rank),
|
|
633
693
|
});
|
|
634
694
|
}
|
|
635
695
|
}
|
|
636
696
|
|
|
637
|
-
const latestAssistantByFingerprint = new Map<
|
|
638
|
-
string,
|
|
639
|
-
{ hasResult: boolean }
|
|
640
|
-
>();
|
|
697
|
+
const latestAssistantByFingerprint = new Map<string, { rank: number }>();
|
|
641
698
|
const latestEntry = messages.at(-1);
|
|
642
699
|
const latestMessage = getRepoMessage(latestEntry as any);
|
|
643
700
|
const latestContent = latestMessage?.content;
|
|
@@ -645,10 +702,10 @@ function collectRenderedToolCallStates(messages: readonly unknown[]): {
|
|
|
645
702
|
for (const part of latestContent) {
|
|
646
703
|
const fingerprint = toolCallFingerprintFromContentPart(part);
|
|
647
704
|
if (!fingerprint) continue;
|
|
648
|
-
const
|
|
705
|
+
const rank = toolCallProgressRank(part);
|
|
649
706
|
const existing = latestAssistantByFingerprint.get(fingerprint);
|
|
650
707
|
latestAssistantByFingerprint.set(fingerprint, {
|
|
651
|
-
|
|
708
|
+
rank: Math.max(existing?.rank ?? 0, rank),
|
|
652
709
|
});
|
|
653
710
|
}
|
|
654
711
|
}
|
|
@@ -760,25 +817,35 @@ export function dedupeReconnectContentAgainstMessages(
|
|
|
760
817
|
const filtered =
|
|
761
818
|
byId.size > 0 || latestAssistantByFingerprint.size > 0
|
|
762
819
|
? snapshotDeduped.filter((part) => {
|
|
820
|
+
const reconnectRank = toolCallProgressRank(part);
|
|
763
821
|
const id = toolCallIdFromContentPart(part);
|
|
764
822
|
const existing = id ? byId.get(id) : undefined;
|
|
765
823
|
if (existing) {
|
|
766
|
-
|
|
767
|
-
|
|
824
|
+
// Keep reconnect copies that are strictly ahead of the rendered
|
|
825
|
+
// message (e.g. completed overlay vs lagging pending thread data).
|
|
826
|
+
// Same-or-behind ranks are duplicates and should stay hidden.
|
|
827
|
+
if (reconnectRank <= existing.rank) {
|
|
828
|
+
changed = true;
|
|
829
|
+
return false;
|
|
830
|
+
}
|
|
831
|
+
return true;
|
|
768
832
|
}
|
|
769
833
|
// Fingerprint fallback for the id-convergence window: two readers of
|
|
770
834
|
// the same active run can assign unrelated ids to the same logical tool
|
|
771
835
|
// call before the server id converges. Suppress the reconnect overlay
|
|
772
|
-
//
|
|
773
|
-
//
|
|
774
|
-
//
|
|
836
|
+
// only when the rendered message is at least as far along; completed
|
|
837
|
+
// reconnect copies stay visible over pending message copies so the UI
|
|
838
|
+
// does not pop back to an older spinner.
|
|
775
839
|
const fingerprint = toolCallFingerprintFromContentPart(part);
|
|
776
840
|
const latestByFingerprint = fingerprint
|
|
777
841
|
? latestAssistantByFingerprint.get(fingerprint)
|
|
778
842
|
: undefined;
|
|
843
|
+
const isCompletedRepeat =
|
|
844
|
+
reconnectRank >= 4 && latestByFingerprint?.rank === 4;
|
|
779
845
|
if (
|
|
780
846
|
latestByFingerprint &&
|
|
781
|
-
|
|
847
|
+
!isCompletedRepeat &&
|
|
848
|
+
reconnectRank <= latestByFingerprint.rank
|
|
782
849
|
) {
|
|
783
850
|
changed = true;
|
|
784
851
|
return false;
|
|
@@ -1557,6 +1624,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1557
1624
|
const queueDirtyRef = useRef(false);
|
|
1558
1625
|
const queueMutationVersionRef = useRef(0);
|
|
1559
1626
|
const dequeueInFlightRef = useRef(false);
|
|
1627
|
+
const queueStopVersionRef = useRef(0);
|
|
1560
1628
|
const [composerContextItems, setComposerContextItems] = useState<
|
|
1561
1629
|
AgentChatContextItem[]
|
|
1562
1630
|
>([]);
|
|
@@ -1728,6 +1796,9 @@ const AssistantChatInner = forwardRef<
|
|
|
1728
1796
|
const [reconnectContent, setReconnectContent] = useState<ContentPart[]>([]);
|
|
1729
1797
|
// When stop is clicked during reconnect, keep content visible (don't wipe it)
|
|
1730
1798
|
const [reconnectFrozen, setReconnectFrozen] = useState(false);
|
|
1799
|
+
// Adapter took over while reconnect still had visible tool cards — keep the
|
|
1800
|
+
// overlay until the adapter message catches up so we don't flash an empty gap.
|
|
1801
|
+
const [adapterHandoffPending, setAdapterHandoffPending] = useState(false);
|
|
1731
1802
|
const reconnectRunIdRef = useRef<string | null>(null);
|
|
1732
1803
|
const reconnectTailOnlyRef = useRef(false);
|
|
1733
1804
|
const reconnectCanMaterializeRef = useRef(false);
|
|
@@ -1784,16 +1855,23 @@ const AssistantChatInner = forwardRef<
|
|
|
1784
1855
|
});
|
|
1785
1856
|
const reconnectActivityContent = useMemo(
|
|
1786
1857
|
() =>
|
|
1787
|
-
isReconnecting || reconnectFrozen
|
|
1858
|
+
isReconnecting || reconnectFrozen || adapterHandoffPending
|
|
1788
1859
|
? reconnectActivityFallbackContent(runningActivityTool)
|
|
1789
1860
|
: [],
|
|
1790
|
-
[
|
|
1861
|
+
[
|
|
1862
|
+
adapterHandoffPending,
|
|
1863
|
+
isReconnecting,
|
|
1864
|
+
reconnectFrozen,
|
|
1865
|
+
runningActivityTool,
|
|
1866
|
+
],
|
|
1791
1867
|
);
|
|
1792
1868
|
const lastBroadcastRunningRef = useRef(isRunning);
|
|
1793
1869
|
const tiptapRef = useRef<TiptapComposerHandle>(null);
|
|
1794
1870
|
// Stable ref to the "stop active run" action so addToQueue can abort
|
|
1795
1871
|
// a running turn without adding many unstable closure deps to its dep list.
|
|
1796
|
-
const stopActiveRunRef = useRef<
|
|
1872
|
+
const stopActiveRunRef = useRef<
|
|
1873
|
+
(options?: { preserveQueuedMessages?: boolean }) => void
|
|
1874
|
+
>(() => {});
|
|
1797
1875
|
|
|
1798
1876
|
const markOptimisticRunning = useCallback(() => {
|
|
1799
1877
|
setOptimisticRunning(true);
|
|
@@ -1888,15 +1966,21 @@ const AssistantChatInner = forwardRef<
|
|
|
1888
1966
|
let settledRepo = repo;
|
|
1889
1967
|
if (repo?.messages?.length > 0) {
|
|
1890
1968
|
let shouldImport = true;
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
repo,
|
|
1895
|
-
);
|
|
1896
|
-
} catch {
|
|
1897
|
-
// If the runtime/export comparison itself fails, do not fail open by
|
|
1898
|
-
// importing a server snapshot that may be stale relative to local UI.
|
|
1969
|
+
// Adapter owns the live turn (including auto-continuation gaps). Do not
|
|
1970
|
+
// let a lagging server thread snapshot clobber in-flight tool cards.
|
|
1971
|
+
if (isRuntimeRunningRef.current || isAutoResumingRef.current) {
|
|
1899
1972
|
shouldImport = false;
|
|
1973
|
+
} else {
|
|
1974
|
+
try {
|
|
1975
|
+
shouldImport = shouldImportServerThreadData(
|
|
1976
|
+
normalizeThreadRepository(threadRuntime.export()),
|
|
1977
|
+
repo,
|
|
1978
|
+
);
|
|
1979
|
+
} catch {
|
|
1980
|
+
// If the runtime/export comparison itself fails, do not fail open by
|
|
1981
|
+
// importing a server snapshot that may be stale relative to local UI.
|
|
1982
|
+
shouldImport = false;
|
|
1983
|
+
}
|
|
1900
1984
|
}
|
|
1901
1985
|
if (shouldImport) {
|
|
1902
1986
|
if (options?.markTitleGenerated) {
|
|
@@ -2058,6 +2142,25 @@ const AssistantChatInner = forwardRef<
|
|
|
2058
2142
|
return false;
|
|
2059
2143
|
}
|
|
2060
2144
|
|
|
2145
|
+
// SUPERSEDE THE PREVIOUS RECONNECT GENERATION. A turn that keeps failing
|
|
2146
|
+
// (e.g. repeated stale_run at "Contacting model") produces a new runId
|
|
2147
|
+
// every few seconds; each call here used to OVERWRITE the single-slot
|
|
2148
|
+
// refs below without tearing down the prior closure, leaving its
|
|
2149
|
+
// watchdog (1s) + idleCheck (1s) + thread poll (2s) + SSE retry loop all
|
|
2150
|
+
// running. 4-5 stacked generations = a ~20 req/s request storm that
|
|
2151
|
+
// hammers the same Neon pool the server needs for heartbeats, deepening
|
|
2152
|
+
// the DB saturation that causes the failures in the first place. Abort
|
|
2153
|
+
// the prior generation's AbortController so its stream loop exits and its
|
|
2154
|
+
// `finally` clears every interval before we start a fresh one.
|
|
2155
|
+
if (reconnectAbortRef.current) {
|
|
2156
|
+
try {
|
|
2157
|
+
reconnectAbortRef.current.abort();
|
|
2158
|
+
} catch {
|
|
2159
|
+
// Already aborted / detached — nothing to unwind.
|
|
2160
|
+
}
|
|
2161
|
+
reconnectAbortRef.current = null;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2061
2164
|
reconnectRunIdRef.current = runId;
|
|
2062
2165
|
const afterSeq = resolveReconnectAfterSeq(threadId, runId);
|
|
2063
2166
|
reconnectTailOnlyRef.current = afterSeq > 0;
|
|
@@ -2072,6 +2175,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2072
2175
|
});
|
|
2073
2176
|
setIsReconnecting(true);
|
|
2074
2177
|
setReconnectFrozen(false);
|
|
2178
|
+
setAdapterHandoffPending(false);
|
|
2075
2179
|
setReconnectContent([]);
|
|
2076
2180
|
window.dispatchEvent(
|
|
2077
2181
|
new CustomEvent("agentNative.chatRunning", {
|
|
@@ -2152,6 +2256,19 @@ const AssistantChatInner = forwardRef<
|
|
|
2152
2256
|
let noProgressDuringReconnect = false;
|
|
2153
2257
|
let latestContent: ContentPart[] = [];
|
|
2154
2258
|
const preparingActionState: PreparingActionState = {};
|
|
2259
|
+
// Exponential backoff for the reattach retry loop. A run that is
|
|
2260
|
+
// "active" server-side but producing no stream (the exact stuck state
|
|
2261
|
+
// that triggers a reconnect) would otherwise re-fetch /runs/:id/events
|
|
2262
|
+
// every ~250ms — several req/s per generation. Back off 250ms → 5s so a
|
|
2263
|
+
// genuinely stuck run stops hammering the server; reset the moment real
|
|
2264
|
+
// progress streams in (see the onSeq callback) so a recovering run
|
|
2265
|
+
// re-tightens immediately.
|
|
2266
|
+
let reconnectRetryCount = 0;
|
|
2267
|
+
const backoffRetryDelay = () => {
|
|
2268
|
+
const ms = Math.min(250 * 2 ** reconnectRetryCount, 5000);
|
|
2269
|
+
reconnectRetryCount += 1;
|
|
2270
|
+
return new Promise((resolve) => window.setTimeout(resolve, ms));
|
|
2271
|
+
};
|
|
2155
2272
|
const sameRunStillActive = async (): Promise<
|
|
2156
2273
|
"active" | "inactive" | "unknown"
|
|
2157
2274
|
> => {
|
|
@@ -2176,6 +2293,11 @@ const AssistantChatInner = forwardRef<
|
|
|
2176
2293
|
afterSeq > 0
|
|
2177
2294
|
? window.setInterval(() => {
|
|
2178
2295
|
if (reconnectRunIdRef.current !== runId) return;
|
|
2296
|
+
// Adapter took over mid-poll — skip imports that would race the
|
|
2297
|
+
// live stream and flicker tool cards back to older snapshots.
|
|
2298
|
+
if (isRuntimeRunningRef.current || isAutoResumingRef.current) {
|
|
2299
|
+
return;
|
|
2300
|
+
}
|
|
2179
2301
|
void refreshThreadFromServer();
|
|
2180
2302
|
}, 2000)
|
|
2181
2303
|
: undefined;
|
|
@@ -2197,7 +2319,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2197
2319
|
if (!sseRes.ok || !sseRes.body) {
|
|
2198
2320
|
const activeState = await sameRunStillActive();
|
|
2199
2321
|
if (activeState !== "inactive") {
|
|
2200
|
-
await
|
|
2322
|
+
await backoffRetryDelay();
|
|
2201
2323
|
continue;
|
|
2202
2324
|
}
|
|
2203
2325
|
break;
|
|
@@ -2223,6 +2345,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2223
2345
|
scheduleUpdate,
|
|
2224
2346
|
(seq) => {
|
|
2225
2347
|
markReconnectProgress();
|
|
2348
|
+
reconnectRetryCount = 0;
|
|
2226
2349
|
updateActiveRunSeq(seq);
|
|
2227
2350
|
},
|
|
2228
2351
|
{ preparingActionState },
|
|
@@ -2241,9 +2364,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2241
2364
|
}
|
|
2242
2365
|
const activeState = await sameRunStillActive();
|
|
2243
2366
|
if (activeState !== "inactive") {
|
|
2244
|
-
await
|
|
2245
|
-
window.setTimeout(resolve, 250),
|
|
2246
|
-
);
|
|
2367
|
+
await backoffRetryDelay();
|
|
2247
2368
|
continue;
|
|
2248
2369
|
}
|
|
2249
2370
|
}
|
|
@@ -2604,7 +2725,8 @@ const AssistantChatInner = forwardRef<
|
|
|
2604
2725
|
!loadHistoryRepository ||
|
|
2605
2726
|
!hasRestoredRef.current ||
|
|
2606
2727
|
isRestoring ||
|
|
2607
|
-
isRunning
|
|
2728
|
+
isRunning ||
|
|
2729
|
+
isAutoResuming
|
|
2608
2730
|
) {
|
|
2609
2731
|
return;
|
|
2610
2732
|
}
|
|
@@ -2621,6 +2743,7 @@ const AssistantChatInner = forwardRef<
|
|
|
2621
2743
|
}, [
|
|
2622
2744
|
historyReloadKey,
|
|
2623
2745
|
importThreadData,
|
|
2746
|
+
isAutoResuming,
|
|
2624
2747
|
isRestoring,
|
|
2625
2748
|
isRunning,
|
|
2626
2749
|
loadHistoryRepository,
|
|
@@ -3046,6 +3169,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3046
3169
|
|
|
3047
3170
|
const next = queuedMessages[0];
|
|
3048
3171
|
if (!next) return;
|
|
3172
|
+
const stopVersion = queueStopVersionRef.current;
|
|
3049
3173
|
|
|
3050
3174
|
dequeueInFlightRef.current = true;
|
|
3051
3175
|
let cancelled = false;
|
|
@@ -3063,7 +3187,10 @@ const AssistantChatInner = forwardRef<
|
|
|
3063
3187
|
await waitForThreadRunToClear(apiUrl, threadId);
|
|
3064
3188
|
if (cancelled) return;
|
|
3065
3189
|
|
|
3066
|
-
if (
|
|
3190
|
+
if (
|
|
3191
|
+
queueStopVersionRef.current !== stopVersion ||
|
|
3192
|
+
queuedMessagesRef.current[0]?.id !== next.id
|
|
3193
|
+
) {
|
|
3067
3194
|
return;
|
|
3068
3195
|
}
|
|
3069
3196
|
|
|
@@ -3100,6 +3227,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3100
3227
|
} catch (err) {
|
|
3101
3228
|
if (
|
|
3102
3229
|
removedForAppend &&
|
|
3230
|
+
queueStopVersionRef.current === stopVersion &&
|
|
3103
3231
|
!queuedMessagesRef.current.some((message) => message.id === next.id)
|
|
3104
3232
|
) {
|
|
3105
3233
|
applyLocalQueuedMessages((prev) => [next, ...prev]);
|
|
@@ -3154,29 +3282,65 @@ const AssistantChatInner = forwardRef<
|
|
|
3154
3282
|
prevIsRuntimeRunningRef.current = isRuntimeRunning;
|
|
3155
3283
|
if (isRuntimeRunning && !wasRunning) {
|
|
3156
3284
|
// SINGLE-READER OWNERSHIP: the adapter runtime just took over (a new run
|
|
3157
|
-
// started or an adopted run resumed).
|
|
3158
|
-
//
|
|
3159
|
-
//
|
|
3160
|
-
// reader's own cleanup paths (which all check reconnectRunIdRef) no-op.
|
|
3285
|
+
// started or an adopted run resumed). Abort the reconnect reader, but keep
|
|
3286
|
+
// its visible content until the adapter message has tool/text parts so the
|
|
3287
|
+
// UI does not flash an empty gap between readers.
|
|
3161
3288
|
if (reconnectRunIdRef.current !== null) {
|
|
3289
|
+
const keepOverlay = reconnectContent.length > 0;
|
|
3162
3290
|
reconnectRunIdRef.current = null;
|
|
3163
3291
|
reconnectAbortRef.current?.abort();
|
|
3164
3292
|
reconnectAbortRef.current = null;
|
|
3165
3293
|
setIsReconnecting(false);
|
|
3166
|
-
setReconnectContent([]);
|
|
3167
3294
|
setReconnectFrozen(false);
|
|
3168
3295
|
reconnectCanMaterializeRef.current = false;
|
|
3169
3296
|
reconnectTailOnlyRef.current = false;
|
|
3297
|
+
if (keepOverlay) {
|
|
3298
|
+
setAdapterHandoffPending(true);
|
|
3299
|
+
} else {
|
|
3300
|
+
setReconnectContent([]);
|
|
3301
|
+
setAdapterHandoffPending(false);
|
|
3302
|
+
}
|
|
3170
3303
|
} else if (reconnectFrozen) {
|
|
3171
3304
|
setReconnectFrozen(false);
|
|
3172
3305
|
setReconnectContent([]);
|
|
3306
|
+
setAdapterHandoffPending(false);
|
|
3173
3307
|
reconnectCanMaterializeRef.current = false;
|
|
3174
3308
|
}
|
|
3175
3309
|
if (forceStopped) {
|
|
3176
3310
|
setForceStopped(false);
|
|
3177
3311
|
}
|
|
3178
3312
|
}
|
|
3179
|
-
}, [
|
|
3313
|
+
}, [
|
|
3314
|
+
isRuntimeRunning,
|
|
3315
|
+
reconnectFrozen,
|
|
3316
|
+
forceStopped,
|
|
3317
|
+
reconnectContent.length,
|
|
3318
|
+
]);
|
|
3319
|
+
|
|
3320
|
+
// Release the deferred reconnect overlay once thread messages have caught
|
|
3321
|
+
// up enough that dedupe would hide the overlay, or after a short timeout so
|
|
3322
|
+
// a stuck handoff cannot leave duplicate tool cards forever.
|
|
3323
|
+
useEffect(() => {
|
|
3324
|
+
if (!adapterHandoffPending) return;
|
|
3325
|
+
if (!isRuntimeRunning) {
|
|
3326
|
+
setReconnectContent([]);
|
|
3327
|
+
setAdapterHandoffPending(false);
|
|
3328
|
+
return;
|
|
3329
|
+
}
|
|
3330
|
+
const stillNeeded =
|
|
3331
|
+
dedupeReconnectContentAgainstMessages(reconnectContent, messages).length >
|
|
3332
|
+
0;
|
|
3333
|
+
if (!stillNeeded) {
|
|
3334
|
+
setReconnectContent([]);
|
|
3335
|
+
setAdapterHandoffPending(false);
|
|
3336
|
+
return;
|
|
3337
|
+
}
|
|
3338
|
+
const timer = window.setTimeout(() => {
|
|
3339
|
+
setReconnectContent([]);
|
|
3340
|
+
setAdapterHandoffPending(false);
|
|
3341
|
+
}, 2500);
|
|
3342
|
+
return () => window.clearTimeout(timer);
|
|
3343
|
+
}, [adapterHandoffPending, isRuntimeRunning, messages, reconnectContent]);
|
|
3180
3344
|
|
|
3181
3345
|
// Same transition guard for isReconnecting: only clear forceStopped on
|
|
3182
3346
|
// the false→true edge (a new reconnect starting on page load).
|
|
@@ -3194,6 +3358,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3194
3358
|
if (!reconnectCanMaterializeRef.current) {
|
|
3195
3359
|
setReconnectFrozen(false);
|
|
3196
3360
|
setReconnectContent([]);
|
|
3361
|
+
setAdapterHandoffPending(false);
|
|
3197
3362
|
return;
|
|
3198
3363
|
}
|
|
3199
3364
|
try {
|
|
@@ -3236,6 +3401,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3236
3401
|
threadRuntime.import(ensureMessageMetadata(repo));
|
|
3237
3402
|
setReconnectFrozen(false);
|
|
3238
3403
|
setReconnectContent([]);
|
|
3404
|
+
setAdapterHandoffPending(false);
|
|
3239
3405
|
reconnectCanMaterializeRef.current = false;
|
|
3240
3406
|
} catch (err) {
|
|
3241
3407
|
captureError(err, {
|
|
@@ -3259,61 +3425,108 @@ const AssistantChatInner = forwardRef<
|
|
|
3259
3425
|
threadRuntime,
|
|
3260
3426
|
]);
|
|
3261
3427
|
|
|
3428
|
+
const settleVisibleInterruptedTools = useCallback(() => {
|
|
3429
|
+
try {
|
|
3430
|
+
const repo = normalizeThreadRepository(threadRuntime.export());
|
|
3431
|
+
const settled = settleInterruptedAssistantToolCallsInRepo(repo);
|
|
3432
|
+
if (settled.changed) {
|
|
3433
|
+
threadRuntime.import(ensureMessageMetadata(settled.repo));
|
|
3434
|
+
}
|
|
3435
|
+
} catch (err) {
|
|
3436
|
+
captureError(err, {
|
|
3437
|
+
tags: {
|
|
3438
|
+
source: "agent-chat-client",
|
|
3439
|
+
phase: "settle-stopped-tool-calls",
|
|
3440
|
+
},
|
|
3441
|
+
extra: {
|
|
3442
|
+
threadId: threadId ?? null,
|
|
3443
|
+
tabId: tabId ?? null,
|
|
3444
|
+
},
|
|
3445
|
+
});
|
|
3446
|
+
}
|
|
3447
|
+
}, [tabId, threadId, threadRuntime]);
|
|
3448
|
+
|
|
3262
3449
|
// Abort the active server run (identical to what the Stop button does) so
|
|
3263
3450
|
// an immediate-while-running send can proceed cleanly without a 409 race.
|
|
3264
3451
|
// Captured in a stable ref so addToQueue can call it without listing
|
|
3265
3452
|
// all the stop-related state in its own dep array.
|
|
3266
|
-
const stopActiveRun = useCallback(
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
setDismissedRunErrorKey(null);
|
|
3277
|
-
if (runIdToAbort) {
|
|
3278
|
-
if (threadId) clearActiveRunIfMatches(threadId, runIdToAbort);
|
|
3279
|
-
fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, {
|
|
3280
|
-
method: "POST",
|
|
3281
|
-
}).catch(() => {});
|
|
3282
|
-
}
|
|
3283
|
-
if (isReconnecting) {
|
|
3284
|
-
reconnectAbortRef.current?.abort();
|
|
3285
|
-
reconnectAbortRef.current = null;
|
|
3286
|
-
reconnectRunIdRef.current = null;
|
|
3287
|
-
setIsReconnecting(false);
|
|
3288
|
-
const shouldFreezeReconnectContent =
|
|
3289
|
-
!reconnectTailOnlyRef.current &&
|
|
3290
|
-
reconnectCanMaterializeRef.current &&
|
|
3291
|
-
reconnectContent.length > 0;
|
|
3292
|
-
if (shouldFreezeReconnectContent) {
|
|
3293
|
-
setReconnectFrozen(true);
|
|
3294
|
-
} else {
|
|
3295
|
-
setReconnectFrozen(false);
|
|
3296
|
-
setReconnectContent([]);
|
|
3297
|
-
reconnectCanMaterializeRef.current = false;
|
|
3453
|
+
const stopActiveRun = useCallback(
|
|
3454
|
+
(options?: { preserveQueuedMessages?: boolean }) => {
|
|
3455
|
+
setForceStopped(true);
|
|
3456
|
+
setOptimisticRunning(false);
|
|
3457
|
+
setHasActiveServerRun(false);
|
|
3458
|
+
setPendingReconnectRecovery(null);
|
|
3459
|
+
setIsAutoResuming(false);
|
|
3460
|
+
if (autoResumeTimerRef.current !== null) {
|
|
3461
|
+
window.clearTimeout(autoResumeTimerRef.current);
|
|
3462
|
+
autoResumeTimerRef.current = null;
|
|
3298
3463
|
}
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3464
|
+
resetRunningActivity();
|
|
3465
|
+
if (!options?.preserveQueuedMessages) {
|
|
3466
|
+
queueStopVersionRef.current += 1;
|
|
3467
|
+
dequeueInFlightRef.current = false;
|
|
3468
|
+
applyLocalQueuedMessages(() => []);
|
|
3469
|
+
}
|
|
3470
|
+
const activeRun = getActiveRun();
|
|
3471
|
+
const runIdToAbort = reconnectRunIdRef.current ?? activeRun?.runId;
|
|
3472
|
+
userStoppedRunRef.current = {
|
|
3473
|
+
at: Date.now(),
|
|
3474
|
+
...(runIdToAbort ? { runId: runIdToAbort } : {}),
|
|
3475
|
+
};
|
|
3476
|
+
setRunErrorInfo(null);
|
|
3477
|
+
setDismissedRunErrorKey(null);
|
|
3478
|
+
if (runIdToAbort) {
|
|
3479
|
+
if (threadId) clearActiveRunIfMatches(threadId, runIdToAbort);
|
|
3480
|
+
fetch(`${apiUrl}/runs/${encodeURIComponent(runIdToAbort)}/abort`, {
|
|
3481
|
+
method: "POST",
|
|
3482
|
+
}).catch(() => {});
|
|
3483
|
+
}
|
|
3484
|
+
if (isReconnecting) {
|
|
3485
|
+
reconnectAbortRef.current?.abort();
|
|
3486
|
+
reconnectAbortRef.current = null;
|
|
3487
|
+
reconnectRunIdRef.current = null;
|
|
3488
|
+
setIsReconnecting(false);
|
|
3489
|
+
setAdapterHandoffPending(false);
|
|
3490
|
+
const shouldFreezeReconnectContent =
|
|
3491
|
+
!reconnectTailOnlyRef.current &&
|
|
3492
|
+
reconnectCanMaterializeRef.current &&
|
|
3493
|
+
reconnectContent.length > 0;
|
|
3494
|
+
if (shouldFreezeReconnectContent) {
|
|
3495
|
+
const frozenContent = cloneContentParts(reconnectContent);
|
|
3496
|
+
settleInterruptedToolCalls(frozenContent, undefined, {
|
|
3497
|
+
includeActivity: true,
|
|
3498
|
+
});
|
|
3499
|
+
setReconnectContent(frozenContent);
|
|
3500
|
+
setReconnectFrozen(true);
|
|
3501
|
+
} else {
|
|
3502
|
+
setReconnectFrozen(false);
|
|
3503
|
+
setReconnectContent([]);
|
|
3504
|
+
reconnectCanMaterializeRef.current = false;
|
|
3505
|
+
}
|
|
3506
|
+
reconnectTailOnlyRef.current = false;
|
|
3507
|
+
}
|
|
3508
|
+
threadRuntime.cancelRun();
|
|
3509
|
+
settleVisibleInterruptedTools();
|
|
3510
|
+
if (typeof window !== "undefined") {
|
|
3511
|
+
window.dispatchEvent(
|
|
3512
|
+
new CustomEvent("agentNative.chatRunning", {
|
|
3513
|
+
detail: { isRunning: false, tabId: tabId || threadId },
|
|
3514
|
+
}),
|
|
3515
|
+
);
|
|
3516
|
+
}
|
|
3517
|
+
},
|
|
3518
|
+
[
|
|
3519
|
+
apiUrl,
|
|
3520
|
+
applyLocalQueuedMessages,
|
|
3521
|
+
isReconnecting,
|
|
3522
|
+
resetRunningActivity,
|
|
3523
|
+
reconnectContent,
|
|
3524
|
+
settleVisibleInterruptedTools,
|
|
3525
|
+
tabId,
|
|
3526
|
+
threadId,
|
|
3527
|
+
threadRuntime,
|
|
3528
|
+
],
|
|
3529
|
+
);
|
|
3317
3530
|
// Keep the ref current so addToQueue can call it without a stale closure.
|
|
3318
3531
|
stopActiveRunRef.current = stopActiveRun;
|
|
3319
3532
|
|
|
@@ -3331,7 +3544,8 @@ const AssistantChatInner = forwardRef<
|
|
|
3331
3544
|
preserveReconnectAutoRecoveryBudget = false,
|
|
3332
3545
|
hideUserMessage = false,
|
|
3333
3546
|
) => {
|
|
3334
|
-
if (
|
|
3547
|
+
if (agentEngineConfigured.state === "missing") {
|
|
3548
|
+
void ensureAgentEngineReadyForSubmit();
|
|
3335
3549
|
return;
|
|
3336
3550
|
}
|
|
3337
3551
|
if (!preserveReconnectAutoRecoveryBudget) {
|
|
@@ -3500,7 +3714,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3500
3714
|
hideUserMessage,
|
|
3501
3715
|
},
|
|
3502
3716
|
]);
|
|
3503
|
-
stopActiveRunRef.current();
|
|
3717
|
+
stopActiveRunRef.current({ preserveQueuedMessages: true });
|
|
3504
3718
|
} else if (isRunning && intent === "queued") {
|
|
3505
3719
|
applyLocalQueuedMessages((prev) => [
|
|
3506
3720
|
...prev,
|
|
@@ -3550,6 +3764,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3550
3764
|
},
|
|
3551
3765
|
[
|
|
3552
3766
|
applyLocalQueuedMessages,
|
|
3767
|
+
agentEngineConfigured.state,
|
|
3553
3768
|
buildComposerContextSubmission,
|
|
3554
3769
|
ensureAgentEngineReadyForSubmit,
|
|
3555
3770
|
execMode,
|
|
@@ -4138,7 +4353,9 @@ const AssistantChatInner = forwardRef<
|
|
|
4138
4353
|
/>
|
|
4139
4354
|
</MessageScrollerItem>
|
|
4140
4355
|
)}
|
|
4141
|
-
{(isReconnecting ||
|
|
4356
|
+
{(isReconnecting ||
|
|
4357
|
+
reconnectFrozen ||
|
|
4358
|
+
adapterHandoffPending) &&
|
|
4142
4359
|
visibleReconnectContent.length > 0 && (
|
|
4143
4360
|
<MessageScrollerItem>
|
|
4144
4361
|
<ReconnectStreamMessage
|
|
@@ -4146,7 +4363,9 @@ const AssistantChatInner = forwardRef<
|
|
|
4146
4363
|
/>
|
|
4147
4364
|
</MessageScrollerItem>
|
|
4148
4365
|
)}
|
|
4149
|
-
{(isReconnecting ||
|
|
4366
|
+
{(isReconnecting ||
|
|
4367
|
+
reconnectFrozen ||
|
|
4368
|
+
adapterHandoffPending) &&
|
|
4150
4369
|
visibleReconnectContent.length === 0 &&
|
|
4151
4370
|
reconnectContent.length === 0 &&
|
|
4152
4371
|
reconnectActivityContent.length > 0 && (
|
|
@@ -4348,7 +4567,6 @@ const AssistantChatInner = forwardRef<
|
|
|
4348
4567
|
: undefined
|
|
4349
4568
|
}
|
|
4350
4569
|
onSlashCommand={onSlashCommand}
|
|
4351
|
-
onBeforeSubmit={ensureAgentEngineReadyForSubmit}
|
|
4352
4570
|
execMode={execMode}
|
|
4353
4571
|
onExecModeChange={onExecModeChange}
|
|
4354
4572
|
planModeDisabled={planModeDisabled}
|
|
@@ -4383,7 +4601,7 @@ const AssistantChatInner = forwardRef<
|
|
|
4383
4601
|
<TooltipTrigger asChild>
|
|
4384
4602
|
<button
|
|
4385
4603
|
type="button"
|
|
4386
|
-
onClick={stopActiveRun}
|
|
4604
|
+
onClick={() => stopActiveRun()}
|
|
4387
4605
|
className="shrink-0 flex h-7 w-7 items-center justify-center rounded-md bg-muted text-foreground hover:bg-muted/80"
|
|
4388
4606
|
>
|
|
4389
4607
|
<IconPlayerStop className="h-3.5 w-3.5" />
|