@agent-native/core 0.80.10 → 0.81.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +53 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/production-agent.ts +108 -15
- package/corpus/core/src/cli/design-connect.ts +419 -19
- package/corpus/core/src/client/AssistantChat.tsx +123 -26
- package/corpus/core/src/client/ConnectBuilderCard.tsx +1 -0
- package/corpus/core/src/client/chat/index.ts +1 -0
- package/corpus/core/src/client/chat/run-recovery.tsx +9 -7
- package/corpus/core/src/client/chat-view-transition.ts +33 -2
- package/corpus/core/src/client/composer/PromptComposer.tsx +21 -2
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +26 -4
- package/corpus/core/src/client/sharing/ShareButton.tsx +3 -3
- package/corpus/core/src/client/sse-event-processor.ts +9 -0
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +31 -3
- package/corpus/core/src/db/client.ts +29 -2
- package/corpus/core/src/provider-api/index.ts +29 -0
- package/corpus/core/src/server/core-routes-plugin.ts +58 -20
- package/corpus/core/src/styles/agent-native.css +54 -0
- package/corpus/core/src/usage/store.ts +2 -2
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +118 -10
- package/corpus/templates/analytics/app/components/layout/layout-route-policy.ts +7 -0
- package/corpus/templates/analytics/app/global.css +8 -0
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +7 -1
- package/corpus/templates/analytics/app/lib/chat-handoff.ts +34 -0
- package/corpus/templates/analytics/app/pages/Ask.tsx +9 -18
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +2 -2
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +109 -21
- package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +4 -6
- package/corpus/templates/analytics/changelog/2026-06-30-ask-opens-a-fresh-chat-unless-you-were-just-chatting-and-das.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-dashboard-email-reports-are-more-reliable-and-use-cleaner.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-dashboard-toolbar-controls-use-a-quieter-share-button-and-ve.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-session-replay-rows-show-whole-minute-durations-in-the-playl.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-30-session-replays-now-render-captured-styling-and-keep-the-eve.md +6 -0
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +16 -3
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +31 -14
- package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +9 -2
- package/corpus/templates/assets/AGENTS.md +4 -0
- package/corpus/templates/assets/actions/_tool-activity.ts +46 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +26 -5
- package/corpus/templates/assets/actions/generate-image.ts +118 -67
- package/corpus/templates/assets/actions/list-draft-assets.ts +50 -0
- package/corpus/templates/assets/actions/open-asset-picker.ts +9 -0
- package/corpus/templates/assets/actions/rerun-generation-run.ts +52 -31
- package/corpus/templates/assets/app/components/create/RecentDraftsSection.tsx +100 -0
- package/corpus/templates/assets/app/global.css +15 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +4 -0
- package/corpus/templates/assets/app/i18n/zh-TW.ts +7 -0
- package/corpus/templates/assets/app/i18n-data.ts +61 -0
- package/corpus/templates/assets/app/routes/_index.tsx +4 -0
- package/corpus/templates/assets/app/routes/library.tsx +233 -52
- package/corpus/templates/assets/changelog/2026-06-29-image-generation-can-now-render-requested-text-and-respect-b.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-image-generation-no-longer-looks-stuck-while-the-provider-is-still-working.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-tagged-presets-now-control-the-image-aspect-ratio.md +6 -0
- package/corpus/templates/assets/changelog/2026-06-30-the-assets-picker-now-supports-a-vertical-embedded-layout-fo.md +6 -0
- package/corpus/templates/assets/server/lib/generation.ts +135 -11
- package/corpus/templates/assets/shared/api.ts +4 -0
- package/corpus/templates/clips/actions/finalize-recording.ts +1 -12
- package/corpus/templates/clips/actions/list-ai-requests.ts +60 -0
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +24 -25
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +10 -5
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +6 -8
- package/corpus/templates/clips/changelog/2026-06-30-extension-save-recovery-avoids-false-upload-failures.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-30-longer-chrome-extension-recordings-now-keep-saving-reliably-.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/background.ts +19 -10
- package/corpus/templates/clips/chrome-extension/src/content-script.ts +38 -12
- package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +224 -0
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +119 -16
- package/corpus/templates/clips/chrome-extension/src/overlay.ts +16 -0
- package/corpus/templates/clips/server/lib/recording-upload-state.ts +48 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +9 -1
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +63 -13
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +74 -0
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +32 -0
- package/corpus/templates/design/AGENTS.md +27 -0
- package/corpus/templates/design/CODE_NATIVE_DESIGN_STUDIO_IMPLEMENTATION_PLAN.md +1 -1
- package/corpus/templates/design/DESIGN-STUDIO-PLAN.md +5 -5
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +76 -25
- package/corpus/templates/design/actions/apply-design-state.ts +17 -4
- package/corpus/templates/design/actions/apply-motion-edit.ts +76 -115
- package/corpus/templates/design/actions/apply-shader-fill.ts +148 -56
- package/corpus/templates/design/actions/capture-design-state.ts +27 -4
- package/corpus/templates/design/actions/connect-localhost.ts +59 -22
- package/corpus/templates/design/actions/delete-design.ts +20 -0
- package/corpus/templates/design/actions/export-zip.ts +23 -8
- package/corpus/templates/design/actions/generate-design.ts +53 -1
- package/corpus/templates/design/actions/get-component-details.ts +21 -26
- package/corpus/templates/design/actions/get-motion-timeline.ts +135 -10
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +116 -0
- package/corpus/templates/design/actions/import-design-tokens.ts +521 -0
- package/corpus/templates/design/actions/index-design-tokens.ts +18 -5
- package/corpus/templates/design/actions/insert-design-native-asset.ts +268 -0
- package/corpus/templates/design/actions/insert-figma-library-asset.ts +233 -0
- package/corpus/templates/design/actions/list-design-extensions.ts +32 -10
- package/corpus/templates/design/actions/list-design-native-assets.ts +105 -0
- package/corpus/templates/design/actions/list-design-states.ts +19 -1
- package/corpus/templates/design/actions/list-figma-library-assets.ts +292 -0
- package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +238 -0
- package/corpus/templates/design/actions/navigate.ts +18 -3
- package/corpus/templates/design/actions/open-component-source.ts +7 -2
- package/corpus/templates/design/actions/preview-component-prop-edit.ts +32 -26
- package/corpus/templates/design/actions/preview-shader-fill.ts +9 -11
- package/corpus/templates/design/actions/provider-api-catalog.ts +2 -2
- package/corpus/templates/design/actions/provider-api-docs.ts +1 -1
- package/corpus/templates/design/actions/provider-api-request.ts +3 -3
- package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +48 -0
- package/corpus/templates/design/actions/run-design-audit.ts +74 -10
- package/corpus/templates/design/actions/run-design-extension-action.ts +1 -1
- package/corpus/templates/design/actions/view-screen.ts +1 -1
- package/corpus/templates/design/actions/write-local-file.ts +213 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +244 -3197
- package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +30 -20
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +795 -289
- package/corpus/templates/design/app/components/design/EditPanel.tsx +635 -234
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +124 -32
- package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +161 -0
- package/corpus/templates/design/app/components/design/MotionDock.tsx +244 -266
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +491 -56
- package/corpus/templates/design/app/components/design/ReviewPanel.tsx +55 -67
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +38 -63
- package/corpus/templates/design/app/components/design/TokensPanel.tsx +264 -8
- package/corpus/templates/design/app/components/design/bridge/codegen.ts +125 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +4037 -0
- package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +62 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +272 -0
- package/corpus/templates/design/app/components/design/bridge/nav.bridge.ts +116 -0
- package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +24 -0
- package/corpus/templates/design/app/components/design/bridge/shader-fill-preview.bridge.ts +78 -0
- package/corpus/templates/design/app/components/design/bridge/tsconfig.json +17 -0
- package/corpus/templates/design/app/components/design/bridge/tweak.bridge.ts +28 -0
- package/corpus/templates/design/app/components/design/bridge/zoom.bridge.ts +43 -0
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +45 -16
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +88 -9
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +94 -9
- package/corpus/templates/design/app/components/layout/Layout.tsx +19 -3
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/design/app/global.css +7 -0
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +23 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
- package/corpus/templates/design/app/i18n-data.ts +369 -0
- package/corpus/templates/design/app/lib/pending-generation.ts +1 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +3024 -616
- package/corpus/templates/design/app/pages/Index.tsx +5 -1
- package/corpus/templates/design/changelog/2026-06-30-applying-the-one-click-fix-for-a-small-tap-target-accessibil.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-canvas-creation-screen-dragging-undo-redo-and-image-fill-edi.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-component-prop-edits-and-shader-fills-now-preserve-rapid-sam.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-can-browse-figma-library-components-and-insert-them-i.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-a-figma-style-left-rail-for-file-agent-assets.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-now-has-native-components-embedded-media-and-figma-im.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-preview-now-includes-a-publish-app-waitlist-option-fr.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-s-left-rail-now-matches-the-sidebar-chrome-and-uses-t.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-s-tools-create-menu-now-stays-compact-instead-of-rend.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-starter-prompts-now-begin-generating-immediately-and-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-token-edits-now-stay-visible-in-previews-and-token-li.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-design-tools-now-use-a-quieter-figma-style-list-layout.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-drawing-rectangles-on-the-all-screens-canvas-no-longer-flash.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-code-native-design-studio-edits-so-component-props-mot.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-fixed-visual-editing-hover-overlays-and-spacing-handles-so-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-edits-now-save-automatically-without-a-separate-css-a.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-motion-timelines-now-reopen-as-editable-tracks-after-writing.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-new-design-prompts-with-uploaded-screenshots-work-reliably-o.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-screen-previews-in-all-screens-view-now-keep-rounded-outline.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-selection-outlines-now-sit-flush-on-rectangles-drawn-directl.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-share-options-use-a-tighter-design-editor-popover-with-compa.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-collapsed-motion-panel-now-stays-tucked-under-the-left-s.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-design-inspector-now-shows-states-and-review-in-a-tighte.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-the-sidebar-keeps-the-organization-picker-space-stable-while.md +6 -0
- package/corpus/templates/design/changelog/2026-06-30-tokens-can-now-be-imported-from-pasted-notes-code-files-fold.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +94 -2
- package/corpus/templates/design/e2e/helpers.ts +168 -65
- package/corpus/templates/design/package.json +3 -1
- package/corpus/templates/design/server/db/schema.ts +23 -0
- package/corpus/templates/design/server/handlers/uploads.ts +22 -1
- package/corpus/templates/design/server/lib/provider-api.ts +7 -4
- package/corpus/templates/design/server/lib/verify-write-grant.ts +108 -0
- package/corpus/templates/design/server/plugins/db.ts +39 -0
- package/corpus/templates/design/server/register-secrets.ts +38 -0
- package/corpus/templates/design/shared/board-file.ts +228 -0
- package/corpus/templates/design/shared/board-objects.ts +288 -0
- package/corpus/templates/design/shared/code-layer.ts +52 -0
- package/corpus/templates/design/shared/component-model.ts +35 -0
- package/corpus/templates/design/shared/motion-compiler.ts +137 -15
- package/corpus/templates/design/shared/motion-timeline.ts +2 -2
- package/corpus/templates/design/shared/resolve-tweaks.ts +32 -9
- package/corpus/templates/design/tsconfig.json +2 -1
- package/corpus/templates/slides/AGENTS.md +9 -0
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -0
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +4 -24
- package/corpus/templates/slides/app/lib/export-pdf-client.ts +29 -20
- package/corpus/templates/slides/app/lib/export-pptx-client.ts +365 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +12 -9
- package/corpus/templates/slides/app/vite-env.d.ts +18 -0
- package/corpus/templates/slides/changelog/2026-06-30-powerpoint-downloads-now-preserve-the-rendered-slide-layout-.md +6 -0
- package/corpus/templates/slides/changelog/2026-06-30-unavailable-decks-open-in-a-cleaner-full-height-centered-sta.md +6 -0
- package/corpus/templates/slides/package.json +1 -0
- package/corpus/templates/slides/vite.config.ts +1 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +94 -10
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/design-connect.d.ts +25 -0
- package/dist/cli/design-connect.d.ts.map +1 -1
- package/dist/cli/design-connect.js +357 -18
- package/dist/cli/design-connect.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +14 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +115 -22
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +1 -0
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/chat/index.d.ts +1 -1
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -1
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/run-recovery.d.ts.map +1 -1
- package/dist/client/chat/run-recovery.js +5 -5
- package/dist/client/chat/run-recovery.js.map +1 -1
- package/dist/client/chat-view-transition.d.ts +6 -0
- package/dist/client/chat-view-transition.d.ts.map +1 -1
- package/dist/client/chat-view-transition.js +23 -2
- package/dist/client/chat-view-transition.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +3 -2
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +11 -4
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/sharing/ShareButton.js +3 -3
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +10 -0
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +8 -2
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +20 -6
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/db/client.d.ts +1 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +28 -2
- package/dist/db/client.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/index.d.ts +5 -4
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +28 -0
- package/dist/provider-api/index.js.map +1 -1
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +25 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +43 -18
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/styles/agent-native.css +54 -0
- package/dist/usage/store.js +2 -2
- package/dist/usage/store.js.map +1 -1
- package/package.json +1 -1
|
@@ -230,6 +230,12 @@ const PENDING_SELECTION_KEY = "pending-selection-context";
|
|
|
230
230
|
const ACTIVE_RUN_CLEAR_TIMEOUT_MS = 5_000;
|
|
231
231
|
const ACTIVE_RUN_STUCK_THRESHOLD_MS = 90_000;
|
|
232
232
|
const ACTIVE_RUN_POLL_INTERVAL_MS = 150;
|
|
233
|
+
// How long a single activity (model call, tool prep, long tool) must stay
|
|
234
|
+
// in-flight before its label is surfaced in the running indicator. Below this
|
|
235
|
+
// the indicator stays a steady "Thinking" so normal fast turns don't flicker
|
|
236
|
+
// through transient labels ("Contacting model", "Preparing X action"); past it
|
|
237
|
+
// the live label appears so a genuinely slow step reads as working, not hung.
|
|
238
|
+
const ACTIVITY_LABEL_REVEAL_DELAY_MS = 6_000;
|
|
233
239
|
const SUBMIT_ENGINE_STATUS_TIMEOUT_MS = 1000;
|
|
234
240
|
|
|
235
241
|
type ActiveRunLookup = {
|
|
@@ -254,6 +260,24 @@ function activeRunLooksStale(runInfo: ActiveRunLookup): boolean {
|
|
|
254
260
|
);
|
|
255
261
|
}
|
|
256
262
|
|
|
263
|
+
/**
|
|
264
|
+
* Decide whether a reconnect should give up with "no progress". The signal is
|
|
265
|
+
* *time since the last streamed event*, NOT total reconnect duration: a healthy
|
|
266
|
+
* long-running tool (e.g. image generation, which emits `activity` heartbeats
|
|
267
|
+
* every few seconds) makes continuous progress and must never be aborted just
|
|
268
|
+
* for running longer than the threshold. Only genuine silence for the full
|
|
269
|
+
* stuck threshold counts as stuck. Pure + exported so the decision is unit
|
|
270
|
+
* testable without driving the whole reconnect lifecycle.
|
|
271
|
+
*/
|
|
272
|
+
export function reconnectProgressTimedOut(args: {
|
|
273
|
+
lastProgressAt: number;
|
|
274
|
+
now: number;
|
|
275
|
+
thresholdMs?: number;
|
|
276
|
+
}): boolean {
|
|
277
|
+
const threshold = args.thresholdMs ?? ACTIVE_RUN_STUCK_THRESHOLD_MS;
|
|
278
|
+
return args.now - args.lastProgressAt >= threshold;
|
|
279
|
+
}
|
|
280
|
+
|
|
257
281
|
function isAssistantUiDuplicateMessageIdError(error: unknown): boolean {
|
|
258
282
|
const message = error instanceof Error ? error.message : String(error ?? "");
|
|
259
283
|
return (
|
|
@@ -1323,6 +1347,24 @@ const AssistantChatInner = forwardRef<
|
|
|
1323
1347
|
// True during the 250ms continuation window and startup of the next chunk
|
|
1324
1348
|
// (adapter's auto-continue delay before POSTing the next chunk).
|
|
1325
1349
|
const [isAutoResuming, setIsAutoResuming] = useState(false);
|
|
1350
|
+
const [runningActivityLabel, setRunningActivityLabel] = useState<
|
|
1351
|
+
string | null
|
|
1352
|
+
>(null);
|
|
1353
|
+
// Delayed-reveal state for the activity label (see ACTIVITY_LABEL_REVEAL_DELAY_MS).
|
|
1354
|
+
// `latest` holds the most recent activity label; `surfaced` flips true once the
|
|
1355
|
+
// reveal timer fires; `timer` is the pending one-shot reveal.
|
|
1356
|
+
const activityLabelTimerRef = useRef<number | null>(null);
|
|
1357
|
+
const latestActivityLabelRef = useRef<string | null>(null);
|
|
1358
|
+
const activityLabelSurfacedRef = useRef(false);
|
|
1359
|
+
const resetRunningActivity = useCallback(() => {
|
|
1360
|
+
if (activityLabelTimerRef.current !== null) {
|
|
1361
|
+
window.clearTimeout(activityLabelTimerRef.current);
|
|
1362
|
+
activityLabelTimerRef.current = null;
|
|
1363
|
+
}
|
|
1364
|
+
latestActivityLabelRef.current = null;
|
|
1365
|
+
activityLabelSurfacedRef.current = false;
|
|
1366
|
+
setRunningActivityLabel(null);
|
|
1367
|
+
}, []);
|
|
1326
1368
|
const [reconnectContent, setReconnectContent] = useState<ContentPart[]>([]);
|
|
1327
1369
|
// When stop is clicked during reconnect, keep content visible (don't wipe it)
|
|
1328
1370
|
const [reconnectFrozen, setReconnectFrozen] = useState(false);
|
|
@@ -1341,6 +1383,19 @@ const AssistantChatInner = forwardRef<
|
|
|
1341
1383
|
const textStreaming = isRunning || externalStreaming;
|
|
1342
1384
|
// UI-only running state — drives the stop button and thinking indicator.
|
|
1343
1385
|
const showRunningInUI = isRunning;
|
|
1386
|
+
// A revealed activity label wins; otherwise stay a steady "Thinking" by
|
|
1387
|
+
// default. We only surface "Reconnecting" while we are actively replaying
|
|
1388
|
+
// recovered content (reconnectContent populated). A bare reconnect with no
|
|
1389
|
+
// replayed content — e.g. a tail-resume after the serverless wall, where
|
|
1390
|
+
// `scheduleUpdate` intentionally leaves reconnectContent empty — is normal
|
|
1391
|
+
// ongoing work, so it must read as "Thinking", never a perpetual "Working".
|
|
1392
|
+
const runningStatusLabel = runningActivityLabel
|
|
1393
|
+
? runningActivityLabel
|
|
1394
|
+
: isAutoResuming
|
|
1395
|
+
? "Resuming"
|
|
1396
|
+
: isReconnecting && reconnectContent.length > 0
|
|
1397
|
+
? "Reconnecting"
|
|
1398
|
+
: "Thinking";
|
|
1344
1399
|
const lastBroadcastRunningRef = useRef(isRunning);
|
|
1345
1400
|
const tiptapRef = useRef<TiptapComposerHandle>(null);
|
|
1346
1401
|
// Stable ref to the "stop active run" action so addToQueue can abort
|
|
@@ -1608,11 +1663,32 @@ const AssistantChatInner = forwardRef<
|
|
|
1608
1663
|
}, 1000);
|
|
1609
1664
|
|
|
1610
1665
|
let reconnectTimedOut = false;
|
|
1611
|
-
|
|
1666
|
+
// Idle deadline, NOT a total-duration cap. A long-but-healthy run (image
|
|
1667
|
+
// generation emits `activity` heartbeats every few seconds) keeps
|
|
1668
|
+
// advancing `lastReconnectProgressAt`, so it is never aborted for simply
|
|
1669
|
+
// taking longer than the threshold. Only true silence for the full stuck
|
|
1670
|
+
// threshold trips it — the same semantics as `activeRunLooksStale` and the
|
|
1671
|
+
// SSE-level no-progress timeout. `markReconnectProgress` resets it on every
|
|
1672
|
+
// streamed event (see the `onSeq` callback below, which fires for every
|
|
1673
|
+
// event including activity, for both fresh and tail-resume reconnects).
|
|
1674
|
+
let lastReconnectProgressAt = Date.now();
|
|
1675
|
+
const markReconnectProgress = () => {
|
|
1676
|
+
lastReconnectProgressAt = Date.now();
|
|
1677
|
+
};
|
|
1678
|
+
const idleCheck = setInterval(() => {
|
|
1679
|
+
if (
|
|
1680
|
+
!reconnectProgressTimedOut({
|
|
1681
|
+
lastProgressAt: lastReconnectProgressAt,
|
|
1682
|
+
now: Date.now(),
|
|
1683
|
+
})
|
|
1684
|
+
) {
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1612
1687
|
reconnectTimedOut = true;
|
|
1613
1688
|
abortCtrl.abort();
|
|
1614
1689
|
clearInterval(watchdog);
|
|
1615
|
-
|
|
1690
|
+
clearInterval(idleCheck);
|
|
1691
|
+
}, 1000);
|
|
1616
1692
|
|
|
1617
1693
|
const streamReconnect = async () => {
|
|
1618
1694
|
let noProgressDuringReconnect = false;
|
|
@@ -1653,7 +1729,10 @@ const AssistantChatInner = forwardRef<
|
|
|
1653
1729
|
toolCallCounter,
|
|
1654
1730
|
tabId,
|
|
1655
1731
|
scheduleUpdate,
|
|
1656
|
-
(seq) =>
|
|
1732
|
+
(seq) => {
|
|
1733
|
+
markReconnectProgress();
|
|
1734
|
+
updateActiveRunSeq(seq);
|
|
1735
|
+
},
|
|
1657
1736
|
);
|
|
1658
1737
|
if (afterSeq === 0) {
|
|
1659
1738
|
setReconnectContent([...content]);
|
|
@@ -1677,7 +1756,7 @@ const AssistantChatInner = forwardRef<
|
|
|
1677
1756
|
window.clearInterval(threadPollInterval);
|
|
1678
1757
|
}
|
|
1679
1758
|
clearInterval(watchdog);
|
|
1680
|
-
|
|
1759
|
+
clearInterval(idleCheck);
|
|
1681
1760
|
}
|
|
1682
1761
|
|
|
1683
1762
|
if (noProgressDuringReconnect && reconnectRunIdRef.current === runId) {
|
|
@@ -2235,11 +2314,9 @@ const AssistantChatInner = forwardRef<
|
|
|
2235
2314
|
return () => window.removeEventListener("agent-chat:run-error", handler);
|
|
2236
2315
|
}, [tabId]);
|
|
2237
2316
|
|
|
2238
|
-
// Real activity means the next chunk has started
|
|
2239
|
-
//
|
|
2240
|
-
//
|
|
2241
|
-
// indicator stays a steady "Thinking" rather than flipping through
|
|
2242
|
-
// transient step labels.
|
|
2317
|
+
// Real activity means the next chunk has started. Surface longer-lived
|
|
2318
|
+
// activity such as "Still generating image" so active-run reconnects do not
|
|
2319
|
+
// look like failures while the server is still making progress.
|
|
2243
2320
|
useEffect(() => {
|
|
2244
2321
|
const handler = (e: Event) => {
|
|
2245
2322
|
const detail = (e as CustomEvent).detail as {
|
|
@@ -2248,13 +2325,43 @@ const AssistantChatInner = forwardRef<
|
|
|
2248
2325
|
tabId?: string;
|
|
2249
2326
|
};
|
|
2250
2327
|
if (tabId && detail?.tabId && detail.tabId !== tabId) return;
|
|
2251
|
-
|
|
2252
|
-
|
|
2328
|
+
const label =
|
|
2329
|
+
typeof detail?.label === "string" ? detail.label.trim() : "";
|
|
2330
|
+
if (!label) return;
|
|
2331
|
+
setIsAutoResuming(false);
|
|
2332
|
+
latestActivityLabelRef.current = label;
|
|
2333
|
+
// Already past the delay → keep the visible label current.
|
|
2334
|
+
if (activityLabelSurfacedRef.current) {
|
|
2335
|
+
setRunningActivityLabel(label);
|
|
2336
|
+
return;
|
|
2337
|
+
}
|
|
2338
|
+
// Not yet surfaced → arm a single reveal timer on the FIRST activity of
|
|
2339
|
+
// this in-flight period. A burst of quick steps that all finish (clear)
|
|
2340
|
+
// before the timer fires never surfaces anything, so normal fast turns
|
|
2341
|
+
// stay a steady "Thinking". A step still in-flight at the deadline reveals
|
|
2342
|
+
// the latest label so a slow operation reads as working, not hung.
|
|
2343
|
+
if (activityLabelTimerRef.current === null) {
|
|
2344
|
+
activityLabelTimerRef.current = window.setTimeout(() => {
|
|
2345
|
+
activityLabelTimerRef.current = null;
|
|
2346
|
+
activityLabelSurfacedRef.current = true;
|
|
2347
|
+
if (latestActivityLabelRef.current) {
|
|
2348
|
+
setRunningActivityLabel(latestActivityLabelRef.current);
|
|
2349
|
+
}
|
|
2350
|
+
}, ACTIVITY_LABEL_REVEAL_DELAY_MS);
|
|
2253
2351
|
}
|
|
2254
2352
|
};
|
|
2353
|
+
const clear = (e: Event) => {
|
|
2354
|
+
const detail = (e as CustomEvent).detail as { tabId?: string };
|
|
2355
|
+
if (tabId && detail?.tabId && detail.tabId !== tabId) return;
|
|
2356
|
+
resetRunningActivity();
|
|
2357
|
+
};
|
|
2255
2358
|
window.addEventListener("agent-chat:activity", handler);
|
|
2256
|
-
|
|
2257
|
-
|
|
2359
|
+
window.addEventListener("agent-chat:activity-clear", clear);
|
|
2360
|
+
return () => {
|
|
2361
|
+
window.removeEventListener("agent-chat:activity", handler);
|
|
2362
|
+
window.removeEventListener("agent-chat:activity-clear", clear);
|
|
2363
|
+
};
|
|
2364
|
+
}, [resetRunningActivity, tabId]);
|
|
2258
2365
|
|
|
2259
2366
|
// Show "Resuming…" during the adapter's auto-continuation window (the
|
|
2260
2367
|
// ~250ms gap between the end of one serverless chunk and the POST for the
|
|
@@ -2274,8 +2381,9 @@ const AssistantChatInner = forwardRef<
|
|
|
2274
2381
|
useEffect(() => {
|
|
2275
2382
|
if (!isRunning) {
|
|
2276
2383
|
setIsAutoResuming(false);
|
|
2384
|
+
resetRunningActivity();
|
|
2277
2385
|
}
|
|
2278
|
-
}, [isRunning]);
|
|
2386
|
+
}, [isRunning, resetRunningActivity]);
|
|
2279
2387
|
|
|
2280
2388
|
// Auto-dequeue: when the agent is idle, send the next queued message. This
|
|
2281
2389
|
// intentionally does not depend on observing the running -> idle transition:
|
|
@@ -3336,18 +3444,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3336
3444
|
<ReconnectStreamMessage content={reconnectContent} />
|
|
3337
3445
|
)}
|
|
3338
3446
|
{showRunningInUI && (
|
|
3339
|
-
<RunningActivityStatus
|
|
3340
|
-
label={
|
|
3341
|
-
isReconnecting
|
|
3342
|
-
? "Reconnecting"
|
|
3343
|
-
: isAutoResuming
|
|
3344
|
-
? "Resuming"
|
|
3345
|
-
: // Keep a steady "Thinking" while the model works —
|
|
3346
|
-
// never flip through transient activity labels
|
|
3347
|
-
// (e.g. "Contacting model", "Preparing X action").
|
|
3348
|
-
"Thinking"
|
|
3349
|
-
}
|
|
3350
|
-
/>
|
|
3447
|
+
<RunningActivityStatus label={runningStatusLabel} />
|
|
3351
3448
|
)}
|
|
3352
3449
|
{queuedMessages.map((msg) => {
|
|
3353
3450
|
const displayText = msg.text
|
|
@@ -195,7 +195,7 @@ export function BuilderConnectCta({
|
|
|
195
195
|
if (variant === "compact") {
|
|
196
196
|
if (configured) {
|
|
197
197
|
return (
|
|
198
|
-
<span className="inline-flex h-8 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground">
|
|
198
|
+
<span className="agent-builder-setup-card__builder-button inline-flex h-8 items-center gap-1.5 whitespace-nowrap rounded-md border border-border bg-background px-2.5 text-[11px] font-medium text-foreground">
|
|
199
199
|
<IconCheck size={11} className="text-emerald-500" />
|
|
200
200
|
{orgName ? `Connected to ${orgName}` : "Connected"}
|
|
201
201
|
</span>
|
|
@@ -203,12 +203,12 @@ export function BuilderConnectCta({
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
return (
|
|
206
|
-
<div className="flex min-w-0 flex-col items-start gap-1 sm:items-end">
|
|
206
|
+
<div className="agent-builder-setup-card__builder-cta flex min-w-0 flex-col items-start gap-1 sm:items-end">
|
|
207
207
|
<button
|
|
208
208
|
type="button"
|
|
209
209
|
onClick={() => start()}
|
|
210
210
|
disabled={connecting}
|
|
211
|
-
className="inline-flex h-8 shrink-0 items-center gap-1 whitespace-nowrap rounded-md bg-foreground px-3 text-[11px] font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-60"
|
|
211
|
+
className="agent-builder-setup-card__builder-button inline-flex h-8 shrink-0 items-center gap-1 whitespace-nowrap rounded-md bg-foreground px-3 text-[11px] font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-60"
|
|
212
212
|
aria-busy={connecting}
|
|
213
213
|
>
|
|
214
214
|
{connecting ? (
|
|
@@ -433,6 +433,8 @@ export function BuilderSetupCard({
|
|
|
433
433
|
<div
|
|
434
434
|
ref={cardRef}
|
|
435
435
|
className={cn(
|
|
436
|
+
"agent-builder-setup-card",
|
|
437
|
+
sidebarLayout && "agent-builder-setup-card--sidebar",
|
|
436
438
|
fullWidth
|
|
437
439
|
? "w-full pb-2"
|
|
438
440
|
: sidebarLayout
|
|
@@ -441,8 +443,8 @@ export function BuilderSetupCard({
|
|
|
441
443
|
)}
|
|
442
444
|
>
|
|
443
445
|
<div className="rounded-lg border border-border/80 bg-background/80 p-3 shadow-sm backdrop-blur">
|
|
444
|
-
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
445
|
-
<div className="min-w-0">
|
|
446
|
+
<div className="agent-builder-setup-card__content flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
447
|
+
<div className="agent-builder-setup-card__copy min-w-0">
|
|
446
448
|
<h3 className="text-[13px] font-medium text-foreground">
|
|
447
449
|
Connect AI
|
|
448
450
|
</h3>
|
|
@@ -452,7 +454,7 @@ export function BuilderSetupCard({
|
|
|
452
454
|
</div>
|
|
453
455
|
<div
|
|
454
456
|
className={cn(
|
|
455
|
-
"flex shrink-0",
|
|
457
|
+
"agent-builder-setup-card__actions flex shrink-0",
|
|
456
458
|
sidebarLayout
|
|
457
459
|
? "flex-col items-start gap-0 sm:items-center"
|
|
458
460
|
: "flex-nowrap items-center gap-2",
|
|
@@ -463,7 +465,7 @@ export function BuilderSetupCard({
|
|
|
463
465
|
type="button"
|
|
464
466
|
onClick={() => setKeyOpen((open) => !open)}
|
|
465
467
|
className={cn(
|
|
466
|
-
"inline-flex shrink-0 items-center whitespace-nowrap rounded-md text-[11px] font-medium",
|
|
468
|
+
"agent-builder-setup-card__key-button inline-flex shrink-0 items-center whitespace-nowrap rounded-md text-[11px] font-medium",
|
|
467
469
|
sidebarLayout
|
|
468
470
|
? "h-7 border-0 bg-transparent px-0 text-muted-foreground hover:bg-transparent hover:text-foreground"
|
|
469
471
|
: "h-8 border border-border bg-background px-3 text-foreground hover:bg-accent",
|
|
@@ -132,6 +132,31 @@ export function markAgentChatHomeHandoff(storageKey?: string | null): void {
|
|
|
132
132
|
} catch {}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Check whether a full-page-chat handoff marker is still recent without
|
|
137
|
+
* consuming it. Use this when a chat route should only restore or animate a
|
|
138
|
+
* conversation if the user actually chatted a moment ago.
|
|
139
|
+
*/
|
|
140
|
+
export function isAgentChatHomeHandoffActive(
|
|
141
|
+
storageKey?: string | null,
|
|
142
|
+
options: AgentChatHomeHandoffOptions = {},
|
|
143
|
+
): boolean {
|
|
144
|
+
if (typeof window === "undefined") return false;
|
|
145
|
+
|
|
146
|
+
let startedAt = 0;
|
|
147
|
+
try {
|
|
148
|
+
const raw = window.sessionStorage.getItem(
|
|
149
|
+
agentChatHomeHandoffKey(storageKey),
|
|
150
|
+
);
|
|
151
|
+
startedAt = raw ? Number.parseInt(raw, 10) : 0;
|
|
152
|
+
} catch {
|
|
153
|
+
startedAt = 0;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const ttlMs = options.ttlMs ?? AGENT_CHAT_HOME_HANDOFF_TTL_MS;
|
|
157
|
+
return Number.isFinite(startedAt) && Date.now() - startedAt <= ttlMs;
|
|
158
|
+
}
|
|
159
|
+
|
|
135
160
|
/**
|
|
136
161
|
* Consume a recent full-page-chat handoff marker. Returns true only once per
|
|
137
162
|
* marker, so layouts can keep `openOnChatRunning` scoped to the route that
|
|
@@ -153,9 +178,15 @@ export function consumeAgentChatHomeHandoff(
|
|
|
153
178
|
startedAt = 0;
|
|
154
179
|
}
|
|
155
180
|
|
|
181
|
+
return isAgentChatHomeHandoffActiveFromTimestamp(startedAt, options);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function isAgentChatHomeHandoffActiveFromTimestamp(
|
|
185
|
+
startedAt: number,
|
|
186
|
+
options: AgentChatHomeHandoffOptions,
|
|
187
|
+
): boolean {
|
|
156
188
|
const ttlMs = options.ttlMs ?? AGENT_CHAT_HOME_HANDOFF_TTL_MS;
|
|
157
|
-
|
|
158
|
-
return active;
|
|
189
|
+
return Number.isFinite(startedAt) && Date.now() - startedAt <= ttlMs;
|
|
159
190
|
}
|
|
160
191
|
|
|
161
192
|
/**
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
|
|
31
31
|
import type { ReasoningEffort } from "../../shared/reasoning-effort.js";
|
|
32
32
|
import { AssistantUiStaleIndexErrorBoundary } from "../assistant-ui-recovery.js";
|
|
33
|
-
import { BuilderSetupCard } from "../chat/run-recovery.js";
|
|
33
|
+
import { BuilderConnectCta, BuilderSetupCard } from "../chat/run-recovery.js";
|
|
34
34
|
import { TooltipProvider } from "../components/ui/tooltip.js";
|
|
35
35
|
import {
|
|
36
36
|
fetchAgentEngineConfiguredState,
|
|
@@ -578,10 +578,11 @@ function PromptComposerInner({
|
|
|
578
578
|
},
|
|
579
579
|
[composerEffort, composerEngine, composerModel, onSubmit],
|
|
580
580
|
);
|
|
581
|
+
const useInlineMissingKeySetup = layoutVariant === "compact";
|
|
581
582
|
|
|
582
583
|
return (
|
|
583
584
|
<>
|
|
584
|
-
{missingApiKey ? (
|
|
585
|
+
{missingApiKey && !useInlineMissingKeySetup ? (
|
|
585
586
|
<BuilderSetupCard
|
|
586
587
|
onConnected={handleBuilderConnected}
|
|
587
588
|
bouncePulse={missingKeyBouncePulse}
|
|
@@ -589,6 +590,24 @@ function PromptComposerInner({
|
|
|
589
590
|
layout="sidebar"
|
|
590
591
|
/>
|
|
591
592
|
) : null}
|
|
593
|
+
{missingApiKey && useInlineMissingKeySetup ? (
|
|
594
|
+
<div className="mb-2 rounded-md border border-border/80 bg-background/80 p-2.5 text-start shadow-sm">
|
|
595
|
+
<div className="flex flex-col gap-2">
|
|
596
|
+
<div className="min-w-0">
|
|
597
|
+
<p className="text-[12px] font-medium text-foreground">
|
|
598
|
+
Connect AI
|
|
599
|
+
</p>
|
|
600
|
+
<p className="mt-0.5 text-[11px] leading-4 text-muted-foreground">
|
|
601
|
+
Connect Builder.io before sending.
|
|
602
|
+
</p>
|
|
603
|
+
</div>
|
|
604
|
+
<BuilderConnectCta
|
|
605
|
+
variant="compact"
|
|
606
|
+
onConnected={handleBuilderConnected}
|
|
607
|
+
/>
|
|
608
|
+
</div>
|
|
609
|
+
</div>
|
|
610
|
+
) : null}
|
|
592
611
|
<AgentComposerFrame
|
|
593
612
|
className={cn(
|
|
594
613
|
"text-start",
|
|
@@ -529,6 +529,7 @@ export {
|
|
|
529
529
|
AGENT_CHAT_VIEW_TRANSITION_NAME,
|
|
530
530
|
consumeAgentChatHomeHandoff,
|
|
531
531
|
getAgentChatViewTransitionStyle,
|
|
532
|
+
isAgentChatHomeHandoffActive,
|
|
532
533
|
markAgentChatHomeHandoff,
|
|
533
534
|
navigateWithAgentChatViewTransition,
|
|
534
535
|
startAgentChatViewTransition,
|
|
@@ -97,6 +97,9 @@ const SECTION_LABEL_CLASS =
|
|
|
97
97
|
const APP_SUBMENU_CONTENT_CLASS =
|
|
98
98
|
"z-50 w-72 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2";
|
|
99
99
|
|
|
100
|
+
const SWITCHER_BUTTON_CLASS =
|
|
101
|
+
"flex w-full items-center gap-2 rounded-md border border-border/70 px-2.5 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/50 hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-60 cursor-pointer";
|
|
102
|
+
|
|
100
103
|
const DEFAULT_ORGANIZATION_SETTINGS_PATH = "/settings#team";
|
|
101
104
|
|
|
102
105
|
const APP_ICON_MAP: Record<string, typeof IconApps> = {
|
|
@@ -274,6 +277,25 @@ function AppsSubmenu({
|
|
|
274
277
|
);
|
|
275
278
|
}
|
|
276
279
|
|
|
280
|
+
function ReservedOrgSwitcherSpace({ className }: { className?: string }) {
|
|
281
|
+
return <div aria-hidden="true" className={`h-8 ${className ?? ""}`} />;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function OrgSwitcherLoadingPlaceholder({ className }: { className?: string }) {
|
|
285
|
+
return (
|
|
286
|
+
<button
|
|
287
|
+
type="button"
|
|
288
|
+
disabled
|
|
289
|
+
aria-label="Loading organization"
|
|
290
|
+
className={`${SWITCHER_BUTTON_CLASS} animate-pulse ${className ?? ""}`}
|
|
291
|
+
>
|
|
292
|
+
<IconBuilding className="h-3.5 w-3.5 shrink-0 opacity-60" />
|
|
293
|
+
<span className="h-3 min-w-0 flex-1 rounded-sm bg-muted-foreground/20" />
|
|
294
|
+
<IconSelector className="h-3 w-3 shrink-0 opacity-30" />
|
|
295
|
+
</button>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
277
299
|
/**
|
|
278
300
|
* Compact org switcher button. Shows the active org (or "Personal" when the
|
|
279
301
|
* user has none); opens a popover with the user's other orgs, pending
|
|
@@ -326,7 +348,7 @@ export function OrgSwitcher({
|
|
|
326
348
|
|
|
327
349
|
if (!org) {
|
|
328
350
|
return reserveSpace && isLoading ? (
|
|
329
|
-
<
|
|
351
|
+
<OrgSwitcherLoadingPlaceholder className={className} />
|
|
330
352
|
) : null;
|
|
331
353
|
}
|
|
332
354
|
|
|
@@ -338,7 +360,7 @@ export function OrgSwitcher({
|
|
|
338
360
|
orgCount > 0 || pendingInvitations.length > 0 || domainMatches.length > 0;
|
|
339
361
|
if (!hasAny && !org.email) {
|
|
340
362
|
return reserveSpace ? (
|
|
341
|
-
<
|
|
363
|
+
<ReservedOrgSwitcherSpace className={className} />
|
|
342
364
|
) : null;
|
|
343
365
|
}
|
|
344
366
|
if (
|
|
@@ -348,7 +370,7 @@ export function OrgSwitcher({
|
|
|
348
370
|
domainMatches.length === 0
|
|
349
371
|
) {
|
|
350
372
|
return reserveSpace ? (
|
|
351
|
-
<
|
|
373
|
+
<ReservedOrgSwitcherSpace className={className} />
|
|
352
374
|
) : null;
|
|
353
375
|
}
|
|
354
376
|
|
|
@@ -368,7 +390,7 @@ export function OrgSwitcher({
|
|
|
368
390
|
<PopoverPrimitive.Trigger asChild>
|
|
369
391
|
<button
|
|
370
392
|
type="button"
|
|
371
|
-
className={
|
|
393
|
+
className={`${SWITCHER_BUTTON_CLASS} ${className ?? ""}`}
|
|
372
394
|
>
|
|
373
395
|
<ButtonIcon className="h-3.5 w-3.5 shrink-0" />
|
|
374
396
|
<span className="truncate flex-1 text-start">{buttonLabel}</span>
|
|
@@ -153,13 +153,13 @@ interface SharesResponse {
|
|
|
153
153
|
policy?: SharesPolicy;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
//
|
|
157
|
-
//
|
|
156
|
+
// Match shadcn's <Button size="sm" variant="outline"> sizing so the trigger
|
|
157
|
+
// sits flush next to other controls while staying transparent at rest.
|
|
158
158
|
const BUTTON_BASE =
|
|
159
159
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
|
|
160
160
|
const BUTTON_OUTLINE_SM = cn(
|
|
161
161
|
BUTTON_BASE,
|
|
162
|
-
"h-9 px-3 border border-[hsl(var(--sidebar-border,var(--input)))] bg-
|
|
162
|
+
"h-9 px-3 border border-[hsl(var(--sidebar-border,var(--input)))] bg-transparent text-foreground hover:bg-[hsl(var(--sidebar-accent,var(--accent)))] hover:text-[hsl(var(--sidebar-accent-foreground,var(--accent-foreground)))]",
|
|
163
163
|
);
|
|
164
164
|
const BUTTON_PRIMARY_SM = cn(
|
|
165
165
|
BUTTON_BASE,
|
|
@@ -352,6 +352,11 @@ export function processEvent(
|
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
if (ev.type === "text") {
|
|
355
|
+
// Visible output means the run is plainly not hanging — drop any running
|
|
356
|
+
// activity label so a transient "Contacting model" / "Still generating
|
|
357
|
+
// image" doesn't linger beside streamed text. Idempotent (clears once, then
|
|
358
|
+
// no-ops) so per-token text deltas stay cheap.
|
|
359
|
+
if (ev.text) dispatchActivityClear(tabId);
|
|
355
360
|
const lastPart = content[content.length - 1];
|
|
356
361
|
if (lastPart && lastPart.type === "text") {
|
|
357
362
|
lastPart.text += ev.text ?? "";
|
|
@@ -492,6 +497,10 @@ export function processEvent(
|
|
|
492
497
|
}),
|
|
493
498
|
);
|
|
494
499
|
}
|
|
500
|
+
// Clear any sticky running-activity label (e.g. "Still generating image"):
|
|
501
|
+
// the tool that was emitting activity heartbeats has finished, so the label
|
|
502
|
+
// must not linger while the model streams its follow-up text or reasoning.
|
|
503
|
+
dispatchActivityClear(tabId);
|
|
495
504
|
// Use id-based lookup when available so parallel same-name tool calls
|
|
496
505
|
// get their results correctly assigned; fall back to name-matching.
|
|
497
506
|
const doneIdx = findPendingToolCallIndex(content, doneTool, ev.id);
|
|
@@ -4,6 +4,7 @@ import { useLocation, useNavigate } from "react-router";
|
|
|
4
4
|
import { appBasePath } from "./api-path.js";
|
|
5
5
|
import {
|
|
6
6
|
consumeAgentChatHomeHandoff,
|
|
7
|
+
isAgentChatHomeHandoffActive,
|
|
7
8
|
markAgentChatHomeHandoff,
|
|
8
9
|
navigateWithAgentChatViewTransition,
|
|
9
10
|
} from "./chat-view-transition.js";
|
|
@@ -13,6 +14,8 @@ export interface UseAgentChatHomeHandoffOptions {
|
|
|
13
14
|
storageKey?: string | null;
|
|
14
15
|
/** The current destination path that receives the sidebar handoff. */
|
|
15
16
|
activePath: string;
|
|
17
|
+
/** How long the handoff marker remains valid. Defaults to 6 hours. */
|
|
18
|
+
ttlMs?: number;
|
|
16
19
|
/** Disable consumption without changing hook call order. */
|
|
17
20
|
enabled?: boolean;
|
|
18
21
|
}
|
|
@@ -30,6 +33,10 @@ export interface UseAgentChatHomeHandoffLinksOptions {
|
|
|
30
33
|
isChatPath?: (pathname: string) => boolean;
|
|
31
34
|
/** Disable link interception without changing hook call order. */
|
|
32
35
|
enabled?: boolean;
|
|
36
|
+
/** How long the handoff marker remains valid. Defaults to 6 hours. */
|
|
37
|
+
ttlMs?: number;
|
|
38
|
+
/** Only intercept links if a recent handoff marker already exists. */
|
|
39
|
+
requireActiveHandoff?: boolean;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
function stripBasePath(path: string): string {
|
|
@@ -96,6 +103,10 @@ function shouldHandleAnchorClick(
|
|
|
96
103
|
return true;
|
|
97
104
|
}
|
|
98
105
|
|
|
106
|
+
function handoffTtlOptions(ttlMs: number | undefined) {
|
|
107
|
+
return ttlMs === undefined ? undefined : { ttlMs };
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
/**
|
|
100
111
|
* Returns true for the route that has just received a full-page-chat handoff.
|
|
101
112
|
* Pass the result to `AgentSidebar openOnChatRunning`.
|
|
@@ -103,6 +114,7 @@ function shouldHandleAnchorClick(
|
|
|
103
114
|
export function useAgentChatHomeHandoff({
|
|
104
115
|
storageKey,
|
|
105
116
|
activePath,
|
|
117
|
+
ttlMs,
|
|
106
118
|
enabled = true,
|
|
107
119
|
}: UseAgentChatHomeHandoffOptions): boolean {
|
|
108
120
|
const [handoffPath, setHandoffPath] = useState<string | null>(null);
|
|
@@ -112,10 +124,10 @@ export function useAgentChatHomeHandoff({
|
|
|
112
124
|
setHandoffPath(null);
|
|
113
125
|
return;
|
|
114
126
|
}
|
|
115
|
-
if (consumeAgentChatHomeHandoff(storageKey)) {
|
|
127
|
+
if (consumeAgentChatHomeHandoff(storageKey, handoffTtlOptions(ttlMs))) {
|
|
116
128
|
setHandoffPath(activePath);
|
|
117
129
|
}
|
|
118
|
-
}, [activePath, enabled, storageKey]);
|
|
130
|
+
}, [activePath, enabled, storageKey, ttlMs]);
|
|
119
131
|
|
|
120
132
|
return enabled && handoffPath === activePath;
|
|
121
133
|
}
|
|
@@ -129,6 +141,8 @@ export function useAgentChatHomeHandoffLinks({
|
|
|
129
141
|
chatPath = "/",
|
|
130
142
|
isChatPath,
|
|
131
143
|
enabled = true,
|
|
144
|
+
ttlMs,
|
|
145
|
+
requireActiveHandoff = false,
|
|
132
146
|
}: UseAgentChatHomeHandoffLinksOptions): void {
|
|
133
147
|
const location = useLocation();
|
|
134
148
|
const navigate = useNavigate();
|
|
@@ -161,6 +175,13 @@ export function useAgentChatHomeHandoffLinks({
|
|
|
161
175
|
return;
|
|
162
176
|
}
|
|
163
177
|
|
|
178
|
+
if (
|
|
179
|
+
requireActiveHandoff &&
|
|
180
|
+
!isAgentChatHomeHandoffActive(storageKey, handoffTtlOptions(ttlMs))
|
|
181
|
+
) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
164
185
|
event.preventDefault();
|
|
165
186
|
markAgentChatHomeHandoff(storageKey);
|
|
166
187
|
navigateWithAgentChatViewTransition(navigate, path);
|
|
@@ -168,5 +189,12 @@ export function useAgentChatHomeHandoffLinks({
|
|
|
168
189
|
|
|
169
190
|
document.addEventListener("click", handleClick, true);
|
|
170
191
|
return () => document.removeEventListener("click", handleClick, true);
|
|
171
|
-
}, [
|
|
192
|
+
}, [
|
|
193
|
+
enabled,
|
|
194
|
+
location.pathname,
|
|
195
|
+
navigate,
|
|
196
|
+
requireActiveHandoff,
|
|
197
|
+
storageKey,
|
|
198
|
+
ttlMs,
|
|
199
|
+
]);
|
|
172
200
|
}
|