@agent-native/core 0.98.4 → 0.98.6
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 +28 -0
- package/corpus/core/docs/content/durable-background-runs.mdx +37 -0
- package/corpus/core/docs/content/external-agents.mdx +62 -7
- package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +38 -7
- package/corpus/core/docs/content/locales/ar-SA/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +39 -7
- package/corpus/core/docs/content/locales/de-DE/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +39 -7
- package/corpus/core/docs/content/locales/es-ES/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +39 -7
- package/corpus/core/docs/content/locales/fr-FR/tracking.mdx +4 -2
- package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +38 -7
- package/corpus/core/docs/content/locales/hi-IN/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +38 -7
- package/corpus/core/docs/content/locales/ja-JP/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +38 -7
- package/corpus/core/docs/content/locales/ko-KR/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +39 -7
- package/corpus/core/docs/content/locales/pt-BR/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +37 -7
- package/corpus/core/docs/content/locales/zh-CN/tracking.mdx +2 -0
- package/corpus/core/docs/content/locales/zh-TW/durable-background-runs.mdx +14 -0
- package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +37 -7
- package/corpus/core/docs/content/locales/zh-TW/tracking.mdx +2 -0
- package/corpus/core/docs/content/tracking.mdx +2 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/handlers.ts +95 -21
- package/corpus/core/src/a2a/task-store.ts +65 -3
- package/corpus/core/src/agent/durable-background.ts +16 -16
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +25 -8
- package/corpus/core/src/agent/engine/anthropic-engine.ts +17 -3
- package/corpus/core/src/agent/engine/output-tokens.ts +6 -6
- package/corpus/core/src/agent/production-agent.ts +200 -27
- package/corpus/core/src/agent/run-manager.ts +81 -1
- package/corpus/core/src/agent/run-store.ts +301 -41
- package/corpus/core/src/client/AgentPanel.tsx +6 -2
- package/corpus/core/src/client/AssistantChat.tsx +364 -69
- package/corpus/core/src/client/agent-chat-adapter.ts +169 -1
- package/corpus/core/src/client/chat/message-components.tsx +3 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +20 -36
- package/corpus/core/src/client/conversation/use-near-bottom-autoscroll.ts +7 -0
- package/corpus/core/src/client/extensions/AgentNativeExtensionFrame.tsx +2 -0
- package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +2 -0
- package/corpus/core/src/client/extensions/ExtensionEditor.tsx +9 -2
- package/corpus/core/src/client/extensions/ExtensionViewer.tsx +2 -0
- package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +2 -0
- package/corpus/core/src/client/extensions/portable-extension.ts +2 -0
- package/corpus/core/src/client/session-replay.ts +361 -29
- package/corpus/core/src/client/sse-event-processor.ts +43 -1
- package/corpus/core/src/demo/actions/toggle-demo-mode.ts +1 -1
- package/corpus/core/src/demo/config.ts +6 -6
- package/corpus/core/src/demo/fetch-interceptor.ts +13 -4
- package/corpus/core/src/demo/redact.ts +13 -149
- package/corpus/core/src/extensions/html-shell.ts +3 -0
- package/corpus/core/src/extensions/session-replay-iframe.ts +131 -0
- package/corpus/core/src/integrations/adapters/slack.ts +123 -13
- package/corpus/core/src/integrations/identity-links-store.ts +210 -0
- package/corpus/core/src/integrations/identity.ts +132 -0
- package/corpus/core/src/integrations/index.ts +6 -0
- package/corpus/core/src/integrations/plugin.ts +58 -1
- package/corpus/core/src/integrations/types.ts +7 -0
- package/corpus/core/src/mcp/build-server.ts +127 -22
- package/corpus/core/src/mcp/external-agent-policy.ts +18 -0
- package/corpus/core/src/mcp/index.ts +1 -0
- package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +1 -20
- package/corpus/core/src/server/agent-chat/plugin-options.ts +12 -1
- package/corpus/core/src/server/agent-chat/script-entries.ts +20 -3
- package/corpus/core/src/server/agent-chat-plugin.ts +3 -0
- package/corpus/core/src/session-replay-iframe-protocol.ts +58 -0
- package/corpus/core/src/shared/reasoning-effort.ts +35 -0
- package/corpus/core/src/styles/agent-conversation.css +26 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
- package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +30 -21
- package/corpus/templates/analytics/AGENTS.md +27 -0
- package/corpus/templates/analytics/actions/create-session-replay-agent-link.ts +0 -1
- package/corpus/templates/analytics/actions/get-error-issue.ts +1 -0
- package/corpus/templates/analytics/actions/get-session-replay-summary.ts +1 -0
- package/corpus/templates/analytics/actions/get-session-replay-timeline.ts +40 -0
- package/corpus/templates/analytics/actions/list-error-issues.ts +13 -0
- package/corpus/templates/analytics/actions/list-session-recordings.ts +1 -0
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -0
- package/corpus/templates/analytics/app/global.css +2 -2
- package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +41 -4
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +52 -203
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +69 -16
- package/corpus/templates/analytics/changelog/2026-07-12-analytics-chat-retries-before-showing-a-generic-no-data-mess.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-analytics-sessions-search-no-longer-loses-fast-keystrokes-an.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-chart-selection-respects-chat-state.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-connected-external-agents-can-now-look-up-sessions-error-iss.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-demo-mode-anonymizes-error-reporting-emails.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-session-identities-stay-visible-when-demo-mode-is-off.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-12-session-replays-show-iframe-content.md +6 -0
- package/corpus/templates/analytics/netlify.toml +0 -3
- package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +14 -0
- package/corpus/templates/analytics/server/lib/error-capture.ts +101 -2
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +24 -0
- package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +49 -17
- package/corpus/templates/analytics/server/lib/session-replay.ts +18 -16
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +43 -6
- package/corpus/templates/analytics/server/plugins/db.ts +15 -0
- package/corpus/templates/assets/app/global.css +1 -1
- package/corpus/templates/brain/app/global.css +2 -2
- package/corpus/templates/brain/changelog/2026-07-12-fix-long-response-progress.md +6 -0
- package/corpus/templates/forms/app/components/CloudUpgrade.tsx +1 -1
- package/corpus/templates/forms/app/components/ThemeToggle.tsx +22 -6
- package/corpus/templates/forms/app/components/builder/FieldPropertiesPanel.tsx +5 -5
- package/corpus/templates/forms/app/components/builder/FieldRenderer.tsx +3 -3
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +16 -16
- package/corpus/templates/forms/app/global.css +164 -13
- package/corpus/templates/forms/app/pages/AskPage.tsx +35 -13
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +143 -67
- package/corpus/templates/forms/app/pages/FormFillPage.tsx +44 -11
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +42 -24
- package/corpus/templates/forms/app/pages/ResponseInsightsPage.tsx +28 -15
- package/corpus/templates/forms/app/pages/ResponsesPage.tsx +46 -20
- package/corpus/templates/forms/app/routes/_app.settings.tsx +1 -1
- package/corpus/templates/forms/changelog/2026-07-12-ask-forms-suggestions-now-sit-beneath-the-composer-for-a-tig.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-12-builder-response-tables-now-fill-the-view-without-a-redundan.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-12-form-builder-surfaces-are-calmer-fields-stay-stable-on-hover.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-12-response-tables-now-fill-the-available-pane-with-a-flexible-.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-12-smoother-more-tactile-controls-throughout-animated-icon-and-.md +6 -0
- package/corpus/templates/mail/AGENTS.md +3 -1
- package/corpus/templates/mail/app/components/email/EmailThread.tsx +21 -13
- package/corpus/templates/mail/app/components/email/email-iframe-document.ts +18 -0
- package/corpus/templates/mail/server/lib/mail-integrations.ts +3 -0
- package/dist/a2a/handlers.d.ts.map +1 -1
- package/dist/a2a/handlers.js +84 -15
- package/dist/a2a/handlers.js.map +1 -1
- package/dist/a2a/task-store.d.ts +20 -1
- package/dist/a2a/task-store.d.ts.map +1 -1
- package/dist/a2a/task-store.js +57 -4
- package/dist/a2a/task-store.js.map +1 -1
- package/dist/agent/durable-background.d.ts +10 -10
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +16 -16
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +19 -8
- 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 +11 -3
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/output-tokens.js +6 -6
- package/dist/agent/engine/output-tokens.js.map +1 -1
- package/dist/agent/production-agent.d.ts +52 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +154 -26
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-manager.d.ts +9 -0
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +71 -1
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/run-store.d.ts +9 -0
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +243 -48
- package/dist/agent/run-store.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +6 -2
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +21 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +298 -67
- 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 +146 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +2 -1
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +3 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +13 -37
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.d.ts +1 -0
- package/dist/client/conversation/use-near-bottom-autoscroll.d.ts.map +1 -1
- package/dist/client/conversation/use-near-bottom-autoscroll.js +6 -0
- package/dist/client/conversation/use-near-bottom-autoscroll.js.map +1 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.d.ts.map +1 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.js +2 -1
- package/dist/client/extensions/AgentNativeExtensionFrame.js.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/client/extensions/EmbeddedExtension.js +2 -1
- package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionEditor.js +5 -2
- package/dist/client/extensions/ExtensionEditor.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +2 -1
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -1
- package/dist/client/extensions/InlineExtensionFrame.js +2 -1
- package/dist/client/extensions/InlineExtensionFrame.js.map +1 -1
- package/dist/client/extensions/portable-extension.d.ts.map +1 -1
- package/dist/client/extensions/portable-extension.js +2 -0
- package/dist/client/extensions/portable-extension.js.map +1 -1
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +308 -29
- package/dist/client/session-replay.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +5 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +37 -1
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/demo/actions/toggle-demo-mode.js +1 -1
- package/dist/demo/actions/toggle-demo-mode.js.map +1 -1
- package/dist/demo/config.js +6 -6
- package/dist/demo/config.js.map +1 -1
- package/dist/demo/fetch-interceptor.d.ts.map +1 -1
- package/dist/demo/fetch-interceptor.js +13 -4
- package/dist/demo/fetch-interceptor.js.map +1 -1
- package/dist/demo/redact.d.ts +7 -7
- package/dist/demo/redact.d.ts.map +1 -1
- package/dist/demo/redact.js +8 -145
- package/dist/demo/redact.js.map +1 -1
- package/dist/extensions/html-shell.d.ts.map +1 -1
- package/dist/extensions/html-shell.js +2 -0
- package/dist/extensions/html-shell.js.map +1 -1
- package/dist/extensions/session-replay-iframe.d.ts +10 -0
- package/dist/extensions/session-replay-iframe.d.ts.map +1 -0
- package/dist/extensions/session-replay-iframe.js +121 -0
- package/dist/extensions/session-replay-iframe.js.map +1 -0
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +98 -13
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/identity-links-store.d.ts +23 -0
- package/dist/integrations/identity-links-store.d.ts.map +1 -0
- package/dist/integrations/identity-links-store.js +158 -0
- package/dist/integrations/identity-links-store.js.map +1 -0
- package/dist/integrations/identity.d.ts +11 -0
- package/dist/integrations/identity.d.ts.map +1 -0
- package/dist/integrations/identity.js +100 -0
- package/dist/integrations/identity.js.map +1 -0
- package/dist/integrations/index.d.ts +2 -0
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +2 -0
- package/dist/integrations/index.js.map +1 -1
- package/dist/integrations/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +43 -1
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/types.d.ts +6 -0
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/mcp/build-server.d.ts +9 -2
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +97 -20
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/external-agent-policy.d.ts +19 -0
- package/dist/mcp/external-agent-policy.d.ts.map +1 -0
- package/dist/mcp/external-agent-policy.js +2 -0
- package/dist/mcp/external-agent-policy.js.map +1 -0
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +1 -19
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/server/agent-chat/plugin-options.d.ts +11 -1
- package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
- package/dist/server/agent-chat/plugin-options.js.map +1 -1
- package/dist/server/agent-chat/script-entries.d.ts.map +1 -1
- package/dist/server/agent-chat/script-entries.js +16 -2
- package/dist/server/agent-chat/script-entries.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +3 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/session-replay-iframe-protocol.d.ts +36 -0
- package/dist/session-replay-iframe-protocol.d.ts.map +1 -0
- package/dist/session-replay-iframe-protocol.js +24 -0
- package/dist/session-replay-iframe-protocol.js.map +1 -0
- package/dist/shared/reasoning-effort.d.ts +11 -0
- package/dist/shared/reasoning-effort.d.ts.map +1 -1
- package/dist/shared/reasoning-effort.js +36 -0
- package/dist/shared/reasoning-effort.js.map +1 -1
- package/dist/styles/agent-conversation.css +26 -0
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
- package/docs/content/durable-background-runs.mdx +37 -0
- package/docs/content/external-agents.mdx +62 -7
- package/docs/content/locales/ar-SA/external-agents.mdx +38 -7
- package/docs/content/locales/ar-SA/tracking.mdx +2 -0
- package/docs/content/locales/de-DE/external-agents.mdx +39 -7
- package/docs/content/locales/de-DE/tracking.mdx +2 -0
- package/docs/content/locales/es-ES/external-agents.mdx +39 -7
- package/docs/content/locales/es-ES/tracking.mdx +2 -0
- package/docs/content/locales/fr-FR/external-agents.mdx +39 -7
- package/docs/content/locales/fr-FR/tracking.mdx +4 -2
- package/docs/content/locales/hi-IN/external-agents.mdx +38 -7
- package/docs/content/locales/hi-IN/tracking.mdx +2 -0
- package/docs/content/locales/ja-JP/external-agents.mdx +38 -7
- package/docs/content/locales/ja-JP/tracking.mdx +2 -0
- package/docs/content/locales/ko-KR/external-agents.mdx +38 -7
- package/docs/content/locales/ko-KR/tracking.mdx +2 -0
- package/docs/content/locales/pt-BR/external-agents.mdx +39 -7
- package/docs/content/locales/pt-BR/tracking.mdx +2 -0
- package/docs/content/locales/zh-CN/external-agents.mdx +37 -7
- package/docs/content/locales/zh-CN/tracking.mdx +2 -0
- package/docs/content/locales/zh-TW/durable-background-runs.mdx +14 -0
- package/docs/content/locales/zh-TW/external-agents.mdx +37 -7
- package/docs/content/locales/zh-TW/tracking.mdx +2 -0
- package/docs/content/tracking.mdx +2 -0
- package/package.json +2 -2
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +34 -5
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +0 -15
- package/corpus/core/src/observability/hosted-model-experiment.ts +0 -118
- package/dist/observability/hosted-model-experiment.d.ts +0 -39
- package/dist/observability/hosted-model-experiment.d.ts.map +0 -1
- package/dist/observability/hosted-model-experiment.js +0 -90
- package/dist/observability/hosted-model-experiment.js.map +0 -1
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
IconPlayerStopFilled,
|
|
22
22
|
IconTerminal,
|
|
23
23
|
IconAlertTriangle,
|
|
24
|
+
IconChevronDown,
|
|
24
25
|
IconRefresh,
|
|
25
26
|
} from "@tabler/icons-react";
|
|
26
27
|
import React, {
|
|
@@ -126,7 +127,6 @@ import {
|
|
|
126
127
|
import { useReconnectReaderOwner } from "./chat/use-reconnect-reader-owner.js";
|
|
127
128
|
import {
|
|
128
129
|
MessageScroller,
|
|
129
|
-
MessageScrollerButton,
|
|
130
130
|
MessageScrollerContent,
|
|
131
131
|
MessageScrollerItem,
|
|
132
132
|
MessageScrollerProvider,
|
|
@@ -162,6 +162,7 @@ import type {
|
|
|
162
162
|
Reference,
|
|
163
163
|
} from "./composer/types.js";
|
|
164
164
|
import { ContextMeter } from "./context-xray/ContextMeter.js";
|
|
165
|
+
import { useNearBottomAutoscroll } from "./conversation/index.js";
|
|
165
166
|
import {
|
|
166
167
|
useAgentDynamicSuggestionsResult,
|
|
167
168
|
type AgentDynamicSuggestionsOption,
|
|
@@ -725,6 +726,75 @@ function getMessageText(message: unknown): string {
|
|
|
725
726
|
return typeof content === "string" ? displayableUserMessageText(content) : "";
|
|
726
727
|
}
|
|
727
728
|
|
|
729
|
+
function contentPartFollowKey(part: unknown): string {
|
|
730
|
+
if (!part || typeof part !== "object") return "unknown";
|
|
731
|
+
const candidate = part as {
|
|
732
|
+
type?: unknown;
|
|
733
|
+
text?: unknown;
|
|
734
|
+
toolCallId?: unknown;
|
|
735
|
+
toolName?: unknown;
|
|
736
|
+
status?: { type?: unknown };
|
|
737
|
+
argsText?: unknown;
|
|
738
|
+
result?: unknown;
|
|
739
|
+
image?: unknown;
|
|
740
|
+
};
|
|
741
|
+
const type = typeof candidate.type === "string" ? candidate.type : "unknown";
|
|
742
|
+
if (type === "text" || type === "reasoning") {
|
|
743
|
+
return `${type}:${String(candidate.text ?? "").length}`;
|
|
744
|
+
}
|
|
745
|
+
if (type === "tool-call") {
|
|
746
|
+
return [
|
|
747
|
+
type,
|
|
748
|
+
candidate.toolCallId ?? "",
|
|
749
|
+
candidate.toolName ?? "",
|
|
750
|
+
candidate.status?.type ?? "",
|
|
751
|
+
String(candidate.argsText ?? "").length,
|
|
752
|
+
String(candidate.result ?? "").length,
|
|
753
|
+
].join(":");
|
|
754
|
+
}
|
|
755
|
+
if (type === "image") return `image:${String(candidate.image ?? "").length}`;
|
|
756
|
+
return `${type}:${String(candidate.text ?? candidate.result ?? "").length}`;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function contentFollowKey(content: unknown): string {
|
|
760
|
+
if (typeof content === "string") return `text:${content.length}`;
|
|
761
|
+
if (!Array.isArray(content)) return "";
|
|
762
|
+
return content.map(contentPartFollowKey).join("|");
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function messageFollowKey(message: unknown): string {
|
|
766
|
+
const candidate = ((message as { message?: unknown })?.message ??
|
|
767
|
+
message) as {
|
|
768
|
+
id?: unknown;
|
|
769
|
+
role?: unknown;
|
|
770
|
+
status?: { type?: unknown; reason?: unknown };
|
|
771
|
+
content?: unknown;
|
|
772
|
+
};
|
|
773
|
+
return [
|
|
774
|
+
candidate.id ?? "",
|
|
775
|
+
candidate.role ?? "",
|
|
776
|
+
candidate.status?.type ?? "",
|
|
777
|
+
candidate.status?.reason ?? "",
|
|
778
|
+
contentFollowKey(candidate.content),
|
|
779
|
+
].join(",");
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
function queuedMessageFollowKey(message: QueuedMessage): string {
|
|
783
|
+
return [
|
|
784
|
+
message.id,
|
|
785
|
+
message.text.length,
|
|
786
|
+
message.images?.length ?? 0,
|
|
787
|
+
message.attachments?.length ?? 0,
|
|
788
|
+
message.references?.length ?? 0,
|
|
789
|
+
message.requestMode ?? "",
|
|
790
|
+
message.recoveryAction ?? "",
|
|
791
|
+
].join(":");
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function reconnectContentFollowKey(content: readonly ContentPart[]): string {
|
|
795
|
+
return content.map(contentPartFollowKey).join("|");
|
|
796
|
+
}
|
|
797
|
+
|
|
728
798
|
export function reconnectActivityFallbackContent(
|
|
729
799
|
toolName: string | null | undefined,
|
|
730
800
|
): ContentPart[] {
|
|
@@ -932,6 +1002,91 @@ function latestRenderedAssistantText(messages: readonly unknown[]): string {
|
|
|
932
1002
|
return assistantTextFromContent(latestMessage.content);
|
|
933
1003
|
}
|
|
934
1004
|
|
|
1005
|
+
function latestRenderedAssistantReasoning(
|
|
1006
|
+
messages: readonly unknown[],
|
|
1007
|
+
): string[] {
|
|
1008
|
+
const latestEntry = messages.at(-1);
|
|
1009
|
+
const latestMessage = getRepoMessage(latestEntry as any);
|
|
1010
|
+
if (
|
|
1011
|
+
latestMessage?.role !== "assistant" ||
|
|
1012
|
+
!Array.isArray(latestMessage.content)
|
|
1013
|
+
) {
|
|
1014
|
+
return [];
|
|
1015
|
+
}
|
|
1016
|
+
return latestMessage.content.flatMap((part) =>
|
|
1017
|
+
part?.type === "reasoning" &&
|
|
1018
|
+
typeof part.text === "string" &&
|
|
1019
|
+
part.text.length > 0
|
|
1020
|
+
? [part.text]
|
|
1021
|
+
: [],
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function trimReconnectReasoningAlreadyRendered(
|
|
1026
|
+
content: ContentPart[],
|
|
1027
|
+
renderedReasoning: readonly string[],
|
|
1028
|
+
options?: { trimTailOverlap?: boolean },
|
|
1029
|
+
): ContentPart[] {
|
|
1030
|
+
if (renderedReasoning.length === 0) return content;
|
|
1031
|
+
const firstReconnectReasoning = content.find(
|
|
1032
|
+
(part): part is Extract<ContentPart, { type: "reasoning" }> =>
|
|
1033
|
+
part.type === "reasoning" && part.text.length > 0,
|
|
1034
|
+
)?.text;
|
|
1035
|
+
let renderedOffset = 0;
|
|
1036
|
+
if (options?.trimTailOverlap && firstReconnectReasoning) {
|
|
1037
|
+
for (let index = renderedReasoning.length - 1; index >= 0; index -= 1) {
|
|
1038
|
+
const rendered = renderedReasoning[index];
|
|
1039
|
+
if (
|
|
1040
|
+
rendered === firstReconnectReasoning ||
|
|
1041
|
+
rendered.startsWith(firstReconnectReasoning) ||
|
|
1042
|
+
firstReconnectReasoning.startsWith(rendered) ||
|
|
1043
|
+
longestSuffixPrefixOverlap(rendered, firstReconnectReasoning) > 0
|
|
1044
|
+
) {
|
|
1045
|
+
renderedOffset = index;
|
|
1046
|
+
break;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
let reasoningIndex = 0;
|
|
1051
|
+
let changed = false;
|
|
1052
|
+
const next: ContentPart[] = [];
|
|
1053
|
+
|
|
1054
|
+
for (const part of content) {
|
|
1055
|
+
if (part.type !== "reasoning") {
|
|
1056
|
+
next.push(part);
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
const rendered = renderedReasoning[renderedOffset + reasoningIndex];
|
|
1060
|
+
reasoningIndex += 1;
|
|
1061
|
+
if (!rendered) {
|
|
1062
|
+
next.push(part);
|
|
1063
|
+
continue;
|
|
1064
|
+
}
|
|
1065
|
+
if (rendered === part.text || rendered.startsWith(part.text)) {
|
|
1066
|
+
changed = true;
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
if (part.text.startsWith(rendered)) {
|
|
1070
|
+
const tail = part.text.slice(rendered.length);
|
|
1071
|
+
if (tail) next.push({ ...part, text: tail });
|
|
1072
|
+
changed = true;
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
if (options?.trimTailOverlap) {
|
|
1076
|
+
const overlap = longestSuffixPrefixOverlap(rendered, part.text);
|
|
1077
|
+
if (overlap > 0) {
|
|
1078
|
+
const tail = part.text.slice(overlap);
|
|
1079
|
+
if (tail) next.push({ ...part, text: tail });
|
|
1080
|
+
changed = true;
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
next.push(part);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
return changed ? next : content;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
935
1090
|
function dedupePendingToolCallReplaysWithinContent(
|
|
936
1091
|
content: ContentPart[],
|
|
937
1092
|
): ContentPart[] {
|
|
@@ -1118,11 +1273,13 @@ export function dedupeReconnectContentAgainstMessages(
|
|
|
1118
1273
|
const { byId, latestAssistantByFingerprint, latestAssistantByName } =
|
|
1119
1274
|
collectRenderedToolCallStates(messages);
|
|
1120
1275
|
const renderedAssistantText = latestRenderedAssistantText(messages);
|
|
1276
|
+
const renderedAssistantReasoning = latestRenderedAssistantReasoning(messages);
|
|
1121
1277
|
if (
|
|
1122
1278
|
byId.size === 0 &&
|
|
1123
1279
|
latestAssistantByFingerprint.size === 0 &&
|
|
1124
1280
|
latestAssistantByName.size === 0 &&
|
|
1125
|
-
!renderedAssistantText
|
|
1281
|
+
!renderedAssistantText &&
|
|
1282
|
+
renderedAssistantReasoning.length === 0
|
|
1126
1283
|
) {
|
|
1127
1284
|
return changed ? snapshotDeduped : content;
|
|
1128
1285
|
}
|
|
@@ -1243,12 +1400,18 @@ export function dedupeReconnectContentAgainstMessages(
|
|
|
1243
1400
|
return true;
|
|
1244
1401
|
})
|
|
1245
1402
|
: snapshotDeduped;
|
|
1246
|
-
const
|
|
1403
|
+
const reasoningDeduped = trimReconnectReasoningAlreadyRendered(
|
|
1247
1404
|
filtered,
|
|
1405
|
+
renderedAssistantReasoning,
|
|
1406
|
+
{ trimTailOverlap: options?.trimTailTextOverlap },
|
|
1407
|
+
);
|
|
1408
|
+
if (reasoningDeduped !== filtered) changed = true;
|
|
1409
|
+
const textDeduped = trimReconnectTextAlreadyRendered(
|
|
1410
|
+
reasoningDeduped,
|
|
1248
1411
|
renderedAssistantText,
|
|
1249
1412
|
{ trimTailOverlap: options?.trimTailTextOverlap },
|
|
1250
1413
|
);
|
|
1251
|
-
if (textDeduped !==
|
|
1414
|
+
if (textDeduped !== reasoningDeduped) changed = true;
|
|
1252
1415
|
return changed ? textDeduped : content;
|
|
1253
1416
|
}
|
|
1254
1417
|
|
|
@@ -1450,7 +1613,7 @@ function AssistantChatUserMessageItem() {
|
|
|
1450
1613
|
const message = messageRuntime.getState();
|
|
1451
1614
|
if (isHiddenUserMessage(message)) return null;
|
|
1452
1615
|
return (
|
|
1453
|
-
<MessageScrollerItem messageId={message.id}
|
|
1616
|
+
<MessageScrollerItem messageId={message.id}>
|
|
1454
1617
|
<UserMessage />
|
|
1455
1618
|
</MessageScrollerItem>
|
|
1456
1619
|
);
|
|
@@ -1883,6 +2046,87 @@ function stripBase64FromRepo(repo: unknown): unknown {
|
|
|
1883
2046
|
return { ...r, messages };
|
|
1884
2047
|
}
|
|
1885
2048
|
|
|
2049
|
+
/**
|
|
2050
|
+
* Owns the "Resuming…" status shown during the adapter's auto-continuation
|
|
2051
|
+
* window — the gap between the end of one serverless chunk and the POST for
|
|
2052
|
+
* the next. Set true the moment the adapter dispatches
|
|
2053
|
+
* `agent-chat:auto-continue`. Cleared here as soon as the successor chunk
|
|
2054
|
+
* produces real output (`agent-chat:stream-progress` — dispatched by the SSE
|
|
2055
|
+
* processor for non-empty text/reasoning deltas) or the run is force-stopped;
|
|
2056
|
+
* a 30s failsafe timer covers any case neither fires. The caller clears it
|
|
2057
|
+
* via the returned `clearAutoResume` for other real-progress signals (tool
|
|
2058
|
+
* activity, an accepted run error, an explicit stop) that live outside this
|
|
2059
|
+
* hook.
|
|
2060
|
+
*
|
|
2061
|
+
* Deliberately NOT cleared by `agent-chat:activity-clear` or merely-idle
|
|
2062
|
+
* `isRunning`: both also fire for old-chunk `tool_done` replays / server
|
|
2063
|
+
* retries, and clearing on those would re-expose terminal message controls
|
|
2064
|
+
* during the exact gap this state exists to cover.
|
|
2065
|
+
*/
|
|
2066
|
+
export function useAutoResumeStatus(
|
|
2067
|
+
tabId: string | undefined,
|
|
2068
|
+
forceStopped: boolean,
|
|
2069
|
+
): { isAutoResuming: boolean; clearAutoResume: () => void } {
|
|
2070
|
+
const [isAutoResuming, setIsAutoResuming] = useState(false);
|
|
2071
|
+
const autoResumeTimerRef = useRef<number | null>(null);
|
|
2072
|
+
|
|
2073
|
+
const clearAutoResume = useCallback(() => {
|
|
2074
|
+
if (autoResumeTimerRef.current !== null) {
|
|
2075
|
+
window.clearTimeout(autoResumeTimerRef.current);
|
|
2076
|
+
autoResumeTimerRef.current = null;
|
|
2077
|
+
}
|
|
2078
|
+
setIsAutoResuming(false);
|
|
2079
|
+
}, []);
|
|
2080
|
+
|
|
2081
|
+
useEffect(() => {
|
|
2082
|
+
const handler = (e: Event) => {
|
|
2083
|
+
const detail = (e as CustomEvent).detail as { tabId?: string };
|
|
2084
|
+
if (tabId && detail?.tabId && detail.tabId !== tabId) return;
|
|
2085
|
+
if (autoResumeTimerRef.current !== null) {
|
|
2086
|
+
window.clearTimeout(autoResumeTimerRef.current);
|
|
2087
|
+
}
|
|
2088
|
+
setIsAutoResuming(true);
|
|
2089
|
+
autoResumeTimerRef.current = window.setTimeout(() => {
|
|
2090
|
+
autoResumeTimerRef.current = null;
|
|
2091
|
+
setIsAutoResuming(false);
|
|
2092
|
+
}, AUTO_RESUME_STATUS_TIMEOUT_MS);
|
|
2093
|
+
};
|
|
2094
|
+
window.addEventListener("agent-chat:auto-continue", handler);
|
|
2095
|
+
return () => {
|
|
2096
|
+
if (autoResumeTimerRef.current !== null) {
|
|
2097
|
+
window.clearTimeout(autoResumeTimerRef.current);
|
|
2098
|
+
autoResumeTimerRef.current = null;
|
|
2099
|
+
}
|
|
2100
|
+
window.removeEventListener("agent-chat:auto-continue", handler);
|
|
2101
|
+
};
|
|
2102
|
+
}, [tabId]);
|
|
2103
|
+
|
|
2104
|
+
// Real forward progress in the current chunk (visible text or reasoning
|
|
2105
|
+
// deltas) means the run is not stuck between chunks — clear the indicator
|
|
2106
|
+
// immediately rather than waiting on the 30s failsafe. This is the
|
|
2107
|
+
// plain-text-continuation case: no tool call fires `agent-chat:activity`
|
|
2108
|
+
// to clear it, so without this listener "Resuming" would linger for up to
|
|
2109
|
+
// AUTO_RESUME_STATUS_TIMEOUT_MS after the run already resumed or finished.
|
|
2110
|
+
useEffect(() => {
|
|
2111
|
+
const handler = (e: Event) => {
|
|
2112
|
+
const detail = (e as CustomEvent).detail as { tabId?: string };
|
|
2113
|
+
if (tabId && detail?.tabId && detail.tabId !== tabId) return;
|
|
2114
|
+
clearAutoResume();
|
|
2115
|
+
};
|
|
2116
|
+
window.addEventListener("agent-chat:stream-progress", handler);
|
|
2117
|
+
return () =>
|
|
2118
|
+
window.removeEventListener("agent-chat:stream-progress", handler);
|
|
2119
|
+
}, [clearAutoResume, tabId]);
|
|
2120
|
+
|
|
2121
|
+
useEffect(() => {
|
|
2122
|
+
if (forceStopped) {
|
|
2123
|
+
clearAutoResume();
|
|
2124
|
+
}
|
|
2125
|
+
}, [clearAutoResume, forceStopped]);
|
|
2126
|
+
|
|
2127
|
+
return { isAutoResuming, clearAutoResume };
|
|
2128
|
+
}
|
|
2129
|
+
|
|
1886
2130
|
const AssistantChatInner = forwardRef<
|
|
1887
2131
|
AssistantChatHandle,
|
|
1888
2132
|
AssistantChatProps & { apiUrl: string }
|
|
@@ -2248,15 +2492,6 @@ const AssistantChatInner = forwardRef<
|
|
|
2248
2492
|
runId?: string;
|
|
2249
2493
|
} | null>(null);
|
|
2250
2494
|
const [isReconnecting, setIsReconnecting] = useState(false);
|
|
2251
|
-
// True during the 250ms continuation window and startup of the next chunk
|
|
2252
|
-
// (adapter's auto-continue delay before POSTing the next chunk).
|
|
2253
|
-
const [isAutoResuming, setIsAutoResuming] = useState(false);
|
|
2254
|
-
// Latest-value ref for the same single-reader checks as isRuntimeRunningRef:
|
|
2255
|
-
// during an adapter auto-continuation the runtime can flick false between
|
|
2256
|
-
// chunks while the adapter is still driving the turn.
|
|
2257
|
-
const isAutoResumingRef = useRef(isAutoResuming);
|
|
2258
|
-
isAutoResumingRef.current = isAutoResuming;
|
|
2259
|
-
const autoResumeTimerRef = useRef<number | null>(null);
|
|
2260
2495
|
const [optimisticRunning, setOptimisticRunning] = useState(false);
|
|
2261
2496
|
const [runningActivityLabel, setRunningActivityLabel] = useState<
|
|
2262
2497
|
string | null
|
|
@@ -2303,6 +2538,17 @@ const AssistantChatInner = forwardRef<
|
|
|
2303
2538
|
// queueing forever" state where isReconnecting or isRuntimeRunning gets
|
|
2304
2539
|
// wedged (e.g. after a tab refresh + stop during reconnect).
|
|
2305
2540
|
const [forceStopped, setForceStopped] = useState(false);
|
|
2541
|
+
// True during the 250ms continuation window and startup of the next chunk
|
|
2542
|
+
// (adapter's auto-continue delay before POSTing the next chunk).
|
|
2543
|
+
const { isAutoResuming, clearAutoResume } = useAutoResumeStatus(
|
|
2544
|
+
tabId,
|
|
2545
|
+
forceStopped,
|
|
2546
|
+
);
|
|
2547
|
+
// Latest-value ref for the same single-reader checks as isRuntimeRunningRef:
|
|
2548
|
+
// during an adapter auto-continuation the runtime can flick false between
|
|
2549
|
+
// chunks while the adapter is still driving the turn.
|
|
2550
|
+
const isAutoResumingRef = useRef(isAutoResuming);
|
|
2551
|
+
isAutoResumingRef.current = isAutoResuming;
|
|
2306
2552
|
const [hasActiveServerRun, setHasActiveServerRun] = useState(() =>
|
|
2307
2553
|
activeRunMatchesThread(getActiveRun(), threadId),
|
|
2308
2554
|
);
|
|
@@ -2365,6 +2611,10 @@ const AssistantChatInner = forwardRef<
|
|
|
2365
2611
|
const stopActiveRunRef = useRef<
|
|
2366
2612
|
(options?: { preserveQueuedMessages?: boolean }) => void
|
|
2367
2613
|
>(() => {});
|
|
2614
|
+
// addToQueue is declared before the autoscroll hook because it also feeds
|
|
2615
|
+
// the reconnect/imperative APIs. Keep a stable bridge so an accepted visible
|
|
2616
|
+
// submit can explicitly reattach bottom-following without closure churn.
|
|
2617
|
+
const resumeFollowingRef = useRef<() => void>(() => {});
|
|
2368
2618
|
|
|
2369
2619
|
const markOptimisticRunning = useCallback(() => {
|
|
2370
2620
|
setOptimisticRunning(true);
|
|
@@ -3624,10 +3874,13 @@ const AssistantChatInner = forwardRef<
|
|
|
3624
3874
|
...(detail.recoverable ? { recoverable: detail.recoverable } : {}),
|
|
3625
3875
|
});
|
|
3626
3876
|
setDismissedRunErrorKey(null);
|
|
3877
|
+
// An errored continuation must not keep showing "Resuming" — there is
|
|
3878
|
+
// no further chunk coming to clear it.
|
|
3879
|
+
clearAutoResume();
|
|
3627
3880
|
};
|
|
3628
3881
|
window.addEventListener("agent-chat:run-error", handler);
|
|
3629
3882
|
return () => window.removeEventListener("agent-chat:run-error", handler);
|
|
3630
|
-
}, [latestAssistantRunId, tabId, threadId]);
|
|
3883
|
+
}, [clearAutoResume, latestAssistantRunId, tabId, threadId]);
|
|
3631
3884
|
|
|
3632
3885
|
// Real activity means the next chunk has started. Surface longer-lived
|
|
3633
3886
|
// activity such as "Still generating image" so active-run reconnects do not
|
|
@@ -3644,7 +3897,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3644
3897
|
typeof detail?.label === "string" ? detail.label.trim() : "";
|
|
3645
3898
|
if (!label) return;
|
|
3646
3899
|
const tool = typeof detail?.tool === "string" ? detail.tool.trim() : "";
|
|
3647
|
-
|
|
3900
|
+
clearAutoResume();
|
|
3648
3901
|
setRunningActivityTool(tool || null);
|
|
3649
3902
|
updateActiveRunActivity(tool || null);
|
|
3650
3903
|
latestActivityLabelRef.current = label;
|
|
@@ -3679,50 +3932,23 @@ const AssistantChatInner = forwardRef<
|
|
|
3679
3932
|
window.removeEventListener("agent-chat:activity", handler);
|
|
3680
3933
|
window.removeEventListener("agent-chat:activity-clear", clear);
|
|
3681
3934
|
};
|
|
3682
|
-
}, [resetRunningActivity, tabId]);
|
|
3683
|
-
|
|
3684
|
-
//
|
|
3685
|
-
//
|
|
3686
|
-
// next
|
|
3935
|
+
}, [clearAutoResume, resetRunningActivity, tabId]);
|
|
3936
|
+
|
|
3937
|
+
// "Resuming…" itself (auto-continue → stream-progress/forceStopped clear,
|
|
3938
|
+
// plus the 30s failsafe) is owned by useAutoResumeStatus above. Real
|
|
3939
|
+
// next-chunk activity (tool start/activity events) clears it in the
|
|
3940
|
+
// activity handler above; real streamed output (text/reasoning) clears it
|
|
3941
|
+
// via that hook's own stream-progress listener. This effect only handles
|
|
3942
|
+
// the running-activity reset once both running signals go idle — it does
|
|
3943
|
+
// NOT clear auto-resume merely because `isRunning` is still true: the
|
|
3944
|
+
// adapter dispatches auto-continue before the old chunk's runtime flips
|
|
3945
|
+
// idle, so doing that would expose terminal message controls during the
|
|
3946
|
+
// exact gap this state is meant to cover.
|
|
3687
3947
|
useEffect(() => {
|
|
3688
|
-
const handler = (e: Event) => {
|
|
3689
|
-
const detail = (e as CustomEvent).detail as { tabId?: string };
|
|
3690
|
-
if (tabId && detail?.tabId && detail.tabId !== tabId) return;
|
|
3691
|
-
if (autoResumeTimerRef.current !== null) {
|
|
3692
|
-
window.clearTimeout(autoResumeTimerRef.current);
|
|
3693
|
-
}
|
|
3694
|
-
setIsAutoResuming(true);
|
|
3695
|
-
autoResumeTimerRef.current = window.setTimeout(() => {
|
|
3696
|
-
autoResumeTimerRef.current = null;
|
|
3697
|
-
setIsAutoResuming(false);
|
|
3698
|
-
}, AUTO_RESUME_STATUS_TIMEOUT_MS);
|
|
3699
|
-
};
|
|
3700
|
-
window.addEventListener("agent-chat:auto-continue", handler);
|
|
3701
|
-
return () => {
|
|
3702
|
-
if (autoResumeTimerRef.current !== null) {
|
|
3703
|
-
window.clearTimeout(autoResumeTimerRef.current);
|
|
3704
|
-
autoResumeTimerRef.current = null;
|
|
3705
|
-
}
|
|
3706
|
-
window.removeEventListener("agent-chat:auto-continue", handler);
|
|
3707
|
-
};
|
|
3708
|
-
}, [tabId]);
|
|
3709
|
-
|
|
3710
|
-
// Clear auto-resume once the next chunk has visibly started or the user stops.
|
|
3711
|
-
// Do not clear solely because `isRunning` is false: auto-resume intentionally
|
|
3712
|
-
// covers that between-chunk gap so the chat never looks idle while work is
|
|
3713
|
-
// still scheduled.
|
|
3714
|
-
useEffect(() => {
|
|
3715
|
-
if (isRunning || forceStopped) {
|
|
3716
|
-
if (autoResumeTimerRef.current !== null) {
|
|
3717
|
-
window.clearTimeout(autoResumeTimerRef.current);
|
|
3718
|
-
autoResumeTimerRef.current = null;
|
|
3719
|
-
}
|
|
3720
|
-
setIsAutoResuming(false);
|
|
3721
|
-
}
|
|
3722
3948
|
if (!isRunning && !isAutoResuming) {
|
|
3723
3949
|
resetRunningActivity();
|
|
3724
3950
|
}
|
|
3725
|
-
}, [
|
|
3951
|
+
}, [isAutoResuming, isRunning, resetRunningActivity]);
|
|
3726
3952
|
|
|
3727
3953
|
// Auto-dequeue: when the agent is idle, send the next queued message. This
|
|
3728
3954
|
// intentionally does not depend on observing the running -> idle transition:
|
|
@@ -4025,11 +4251,7 @@ const AssistantChatInner = forwardRef<
|
|
|
4025
4251
|
setOptimisticRunning(false);
|
|
4026
4252
|
setHasActiveServerRun(false);
|
|
4027
4253
|
setPendingReconnectRecovery(null);
|
|
4028
|
-
|
|
4029
|
-
if (autoResumeTimerRef.current !== null) {
|
|
4030
|
-
window.clearTimeout(autoResumeTimerRef.current);
|
|
4031
|
-
autoResumeTimerRef.current = null;
|
|
4032
|
-
}
|
|
4254
|
+
clearAutoResume();
|
|
4033
4255
|
resetRunningActivity();
|
|
4034
4256
|
if (!options?.preserveQueuedMessages) {
|
|
4035
4257
|
queueStopVersionRef.current += 1;
|
|
@@ -4087,6 +4309,7 @@ const AssistantChatInner = forwardRef<
|
|
|
4087
4309
|
[
|
|
4088
4310
|
apiUrl,
|
|
4089
4311
|
applyLocalQueuedMessages,
|
|
4312
|
+
clearAutoResume,
|
|
4090
4313
|
isReconnecting,
|
|
4091
4314
|
resetRunningActivity,
|
|
4092
4315
|
reconnectContent,
|
|
@@ -4133,9 +4356,6 @@ const AssistantChatInner = forwardRef<
|
|
|
4133
4356
|
// Selection context attached via Cmd+I is one-shot — clear it as soon
|
|
4134
4357
|
// as the user actually sends a message so it can't be re-used.
|
|
4135
4358
|
clearPendingSelection();
|
|
4136
|
-
// Sending a message is an explicit user action. The scroller anchors the
|
|
4137
|
-
// appended user turn so the new message and reply land in view without
|
|
4138
|
-
// re-pinning the viewport during ordinary streaming.
|
|
4139
4359
|
const submitted = includeComposerContext
|
|
4140
4360
|
? buildComposerContextSubmission(text)
|
|
4141
4361
|
: { text, includesContext: false };
|
|
@@ -4344,6 +4564,10 @@ const AssistantChatInner = forwardRef<
|
|
|
4344
4564
|
// intentionally reported before the agent's response resolves: a
|
|
4345
4565
|
// caller like sendToAgentChatAndConfirm only needs to know the submit
|
|
4346
4566
|
// wasn't silently dropped, not whether the run itself later succeeds.
|
|
4567
|
+
// A visible submit is explicit user intent to see the new turn. Reattach
|
|
4568
|
+
// following only after the message was queued/appended successfully;
|
|
4569
|
+
// hidden reconnect recovery turns must leave the user's viewport alone.
|
|
4570
|
+
if (!hideUserMessage) resumeFollowingRef.current();
|
|
4347
4571
|
reportAgentChatSubmitResult(submitMessageId, true);
|
|
4348
4572
|
if (submitted.includesContext) {
|
|
4349
4573
|
updateComposerContextItems(() => []);
|
|
@@ -4510,6 +4734,62 @@ const AssistantChatInner = forwardRef<
|
|
|
4510
4734
|
adapterHandoffPending || reconnectTailOnlyRef.current,
|
|
4511
4735
|
},
|
|
4512
4736
|
);
|
|
4737
|
+
const autoscrollFollowKey = [
|
|
4738
|
+
messages.map(messageFollowKey).join(";"),
|
|
4739
|
+
`q:${queuedMessages.map(queuedMessageFollowKey).join("|")}`,
|
|
4740
|
+
`r:${reconnectContentFollowKey(visibleReconnectContent)}`,
|
|
4741
|
+
`status:${showRunningInUI ? runningStatusLabel : "idle"}`,
|
|
4742
|
+
].join(";;");
|
|
4743
|
+
const {
|
|
4744
|
+
scrollRef,
|
|
4745
|
+
isNearBottomRef,
|
|
4746
|
+
showScrollToBottom,
|
|
4747
|
+
scrollToBottom,
|
|
4748
|
+
scrollToBottomAfterPaint,
|
|
4749
|
+
resumeFollowing,
|
|
4750
|
+
} = useNearBottomAutoscroll<HTMLDivElement>({
|
|
4751
|
+
followKey: autoscrollFollowKey,
|
|
4752
|
+
streaming: textStreaming,
|
|
4753
|
+
});
|
|
4754
|
+
resumeFollowingRef.current = resumeFollowing;
|
|
4755
|
+
|
|
4756
|
+
const scrollToBottomWhileLayoutSettles = useCallback(() => {
|
|
4757
|
+
scrollToBottomAfterPaint();
|
|
4758
|
+
const element = scrollRef.current;
|
|
4759
|
+
if (!element || typeof ResizeObserver === "undefined") return undefined;
|
|
4760
|
+
|
|
4761
|
+
let stopped = false;
|
|
4762
|
+
const observer = new ResizeObserver(() => {
|
|
4763
|
+
if (!stopped && isNearBottomRef.current) scrollToBottom();
|
|
4764
|
+
});
|
|
4765
|
+
observer.observe(element);
|
|
4766
|
+
const timeout = window.setTimeout(() => {
|
|
4767
|
+
stopped = true;
|
|
4768
|
+
observer.disconnect();
|
|
4769
|
+
if (isNearBottomRef.current) scrollToBottom();
|
|
4770
|
+
}, 1600);
|
|
4771
|
+
|
|
4772
|
+
return () => {
|
|
4773
|
+
stopped = true;
|
|
4774
|
+
window.clearTimeout(timeout);
|
|
4775
|
+
observer.disconnect();
|
|
4776
|
+
};
|
|
4777
|
+
}, [isNearBottomRef, scrollRef, scrollToBottom, scrollToBottomAfterPaint]);
|
|
4778
|
+
|
|
4779
|
+
const wasRestoringRef = useRef(isRestoring);
|
|
4780
|
+
useEffect(() => {
|
|
4781
|
+
const wasRestoring = wasRestoringRef.current;
|
|
4782
|
+
wasRestoringRef.current = isRestoring;
|
|
4783
|
+
if (wasRestoring && !isRestoring) {
|
|
4784
|
+
return scrollToBottomWhileLayoutSettles();
|
|
4785
|
+
}
|
|
4786
|
+
}, [isRestoring, scrollToBottomWhileLayoutSettles]);
|
|
4787
|
+
|
|
4788
|
+
useEffect(() => {
|
|
4789
|
+
if (!textStreaming && isNearBottomRef.current) {
|
|
4790
|
+
scrollToBottomAfterPaint();
|
|
4791
|
+
}
|
|
4792
|
+
}, [isNearBottomRef, scrollToBottomAfterPaint, textStreaming]);
|
|
4513
4793
|
const chatScrollResetKey = `${tabId ?? ""}:${threadId ?? ""}`;
|
|
4514
4794
|
|
|
4515
4795
|
const { isDevMode: cpDevMode } = useDevMode(apiUrl);
|
|
@@ -4661,6 +4941,7 @@ const AssistantChatInner = forwardRef<
|
|
|
4661
4941
|
showComposerSlot ||
|
|
4662
4942
|
showCenteredEmptyThreadFooterSlot ||
|
|
4663
4943
|
(guidedQuestions && guidedQuestions.length > 0) ||
|
|
4944
|
+
showScrollToBottom ||
|
|
4664
4945
|
composerContextItems.length > 0 ||
|
|
4665
4946
|
showPlanModeCallout,
|
|
4666
4947
|
);
|
|
@@ -4764,9 +5045,12 @@ const AssistantChatInner = forwardRef<
|
|
|
4764
5045
|
)}
|
|
4765
5046
|
|
|
4766
5047
|
{/* Messages area */}
|
|
4767
|
-
<MessageScrollerProvider
|
|
5048
|
+
<MessageScrollerProvider
|
|
5049
|
+
key={chatScrollResetKey}
|
|
5050
|
+
autoScroll={false}
|
|
5051
|
+
>
|
|
4768
5052
|
<MessageScroller className="agent-chat-scroll">
|
|
4769
|
-
<MessageScrollerViewport>
|
|
5053
|
+
<MessageScrollerViewport ref={scrollRef}>
|
|
4770
5054
|
{authError ? (
|
|
4771
5055
|
<div className="flex flex-col items-center justify-center h-full px-4 gap-3">
|
|
4772
5056
|
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-muted">
|
|
@@ -5028,7 +5312,6 @@ const AssistantChatInner = forwardRef<
|
|
|
5028
5312
|
<MessageScrollerItem
|
|
5029
5313
|
key={msg.id}
|
|
5030
5314
|
messageId={msg.id}
|
|
5031
|
-
scrollAnchor
|
|
5032
5315
|
>
|
|
5033
5316
|
<div className="group flex items-start justify-end gap-1.5">
|
|
5034
5317
|
<button
|
|
@@ -5072,8 +5355,20 @@ const AssistantChatInner = forwardRef<
|
|
|
5072
5355
|
</MessageScrollerContent>
|
|
5073
5356
|
)}
|
|
5074
5357
|
</MessageScrollerViewport>
|
|
5075
|
-
{!authError &&
|
|
5076
|
-
|
|
5358
|
+
{!authError &&
|
|
5359
|
+
!isRestoring &&
|
|
5360
|
+
!showEmptyState &&
|
|
5361
|
+
showScrollToBottom ? (
|
|
5362
|
+
<div className="shrink-0 flex justify-center -mb-1">
|
|
5363
|
+
<button
|
|
5364
|
+
type="button"
|
|
5365
|
+
onClick={scrollToBottom}
|
|
5366
|
+
className="flex h-7 w-7 items-center justify-center rounded-full border border-border bg-background shadow-sm hover:bg-accent"
|
|
5367
|
+
aria-label="Scroll to bottom"
|
|
5368
|
+
>
|
|
5369
|
+
<IconChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
|
|
5370
|
+
</button>
|
|
5371
|
+
</div>
|
|
5077
5372
|
) : null}
|
|
5078
5373
|
</MessageScroller>
|
|
5079
5374
|
</MessageScrollerProvider>
|