@agent-native/core 0.141.5 → 0.141.7
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/templates/analytics/.agents/skills/dashboard-management/SKILL.md +3 -14
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +20 -8
- package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +5 -5
- package/corpus/templates/analytics/AGENTS.md +2 -2
- package/corpus/templates/analytics/actions/compose-dashboard.ts +1 -1
- package/corpus/templates/analytics/actions/get-first-party-analytics-health.ts +15 -1
- package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +135 -0
- package/corpus/templates/analytics/actions/navigate.ts +1 -1
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -2
- package/corpus/templates/analytics/actions/update-dashboard.ts +0 -1
- package/corpus/templates/analytics/actions/view-screen.ts +0 -20
- package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +5 -5
- package/corpus/templates/analytics/app/i18n-data.ts +330 -90
- package/corpus/templates/analytics/app/pages/Agents.tsx +0 -3
- package/corpus/templates/analytics/app/pages/DataSources.tsx +66 -30
- package/corpus/templates/analytics/app/pages/Settings.tsx +22 -43
- package/corpus/templates/analytics/app/pages/settings/settings-search.ts +23 -20
- package/corpus/templates/analytics/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/analytics/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/analytics/app/routes/team.tsx +1 -1
- package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-now-repair-automatically-after-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-08-05-settings-no-longer-includes-an-unnecessary-about-section.md +6 -0
- package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +1 -1
- package/corpus/templates/analytics/netlify.toml +4 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +109 -0
- package/corpus/templates/analytics/scripts/first-party-analytics-bigquery.sql +104 -0
- package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +342 -0
- package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -2
- package/corpus/templates/analytics/server/lib/analytics-rollup-lock.ts +8 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +11 -29
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +55 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +1058 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-health.ts +151 -46
- package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +15 -9
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +59 -17
- package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -3
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +2 -4
- package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +61 -0
- package/corpus/templates/analytics/server/plugins/db.ts +58 -187
- package/corpus/templates/analytics/server/routes/api/analytics-rollups/backfill.post.ts +53 -0
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +8 -6
- package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +16 -1
- package/corpus/templates/assets/AGENTS.md +1 -0
- package/corpus/templates/assets/actions/_helpers.ts +12 -0
- package/corpus/templates/assets/actions/generate-image.ts +9 -3
- package/corpus/templates/assets/actions/import-style-from-url.ts +115 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +3 -1
- package/corpus/templates/assets/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/assets/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/assets/app/routes/settings.tsx +5 -0
- package/corpus/templates/assets/app/routes/team.tsx +1 -1
- package/corpus/templates/assets/changelog/2026-08-05-brand-kits-can-import-a-website-s-hydrated-visual-language-a.md +6 -0
- package/corpus/templates/assets/changelog/2026-08-05-generated-images-now-hand-off-embeddable-previews-to-other-w.md +6 -0
- package/corpus/templates/assets/server/lib/generation.ts +58 -0
- package/corpus/templates/assets/shared/api.ts +12 -0
- package/corpus/templates/brain/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/brain/app/routes/team.tsx +1 -1
- package/corpus/templates/brain/app/routes.ts +1 -1
- package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +3 -1
- package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +1 -1
- package/corpus/templates/calendar/app/routes/_app.settings.$.tsx +1 -0
- package/corpus/templates/calendar/app/routes/_app.team.tsx +1 -1
- package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +1 -1
- package/corpus/templates/chat/app/root.tsx +1 -1
- package/corpus/templates/chat/app/routes/agent.tsx +7 -7
- package/corpus/templates/chat/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/chat/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/chat/app/routes/team.tsx +1 -1
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/app/components/player/transcript-panel.tsx +1 -1
- package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +1 -1
- package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +1 -1
- package/corpus/templates/clips/app/routes/_app.settings.$.tsx +1 -0
- package/corpus/templates/clips/changelog/2026-08-05-clips-overlays-now-use-clean-surfaces-without-clipped-shadow.md +6 -0
- package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +2 -2
- package/corpus/templates/clips/desktop/src/styles.css +3 -54
- package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -3
- package/corpus/templates/content/app/routes/_app.extensions._index.tsx +1 -1
- package/corpus/templates/content/app/routes/_app.settings.$.tsx +1 -0
- package/corpus/templates/content/app/routes/_app.team.tsx +1 -1
- package/corpus/templates/content/e2e/global-setup.ts +1 -1
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +6 -1
- package/corpus/templates/design/actions/analyze-brand-assets.ts +7 -7
- package/corpus/templates/design/actions/get-design-system.ts +3 -1
- package/corpus/templates/design/actions/import-from-url.ts +6 -5
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +2 -2
- package/corpus/templates/design/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/design/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/design/app/routes/team.tsx +1 -1
- package/corpus/templates/design/changelog/2026-08-05-missing-design-systems-now-return-a-clear-not-found-response.md +6 -0
- package/corpus/templates/design/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-de.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +1 -1
- package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +1 -1
- package/corpus/templates/dispatch/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/dispatch/app/routes/team.tsx +1 -1
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +1 -1
- package/corpus/templates/factory/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/factory/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/factory/app/routes/team.tsx +1 -1
- package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +1 -1
- package/corpus/templates/forms/app/routes/_app.settings.$.tsx +1 -0
- package/corpus/templates/forms/app/routes/_app.team.tsx +1 -1
- package/corpus/templates/forms/shared/navigation.ts +1 -1
- package/corpus/templates/macros/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/macros/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/macros/app/routes/team.tsx +1 -1
- package/corpus/templates/mail/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/mail/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/plan/app/hooks/use-navigation-state.ts +1 -1
- package/corpus/templates/plan/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/plan/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/plan/app/routes/team.tsx +1 -1
- package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +3 -1
- package/corpus/templates/slides/actions/analyze-brand-assets.ts +7 -7
- package/corpus/templates/slides/actions/import-from-url.ts +6 -6
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -2
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +1 -1
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +3 -0
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -1
- package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +9 -3
- package/corpus/templates/slides/app/context/DeckContext.tsx +71 -20
- package/corpus/templates/slides/app/global.css +6 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +5 -5
- package/corpus/templates/slides/app/lib/create-deck-generation.ts +9 -5
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +17 -0
- package/corpus/templates/slides/app/pages/Index.tsx +16 -5
- package/corpus/templates/slides/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/slides/app/routes/settings.$.tsx +1 -0
- package/corpus/templates/slides/app/routes/team.tsx +1 -1
- package/corpus/templates/slides/changelog/2026-08-05-failed-deck-creation-and-reference-uploads-now-recover-clean.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-05-new-presentation-prompts-now-clearly-describe-presentation-g.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-05-slides-recover-completed-generation-results-after-a-sync-hic.md +6 -0
- package/corpus/templates/slides/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-st.md +6 -0
- package/corpus/templates/tasks/app/components/layout/Header.tsx +3 -1
- package/corpus/templates/tasks/app/routes/extensions._index.tsx +1 -1
- package/corpus/templates/tasks/app/routes/settings.$.tsx +1 -0
- package/dist/agent/engine/ai-sdk-engine.js +49 -3
- package/dist/agent/production-agent.d.ts +1 -0
- package/dist/agent/production-agent.js +8 -0
- package/dist/agent/types.d.ts +7 -0
- package/dist/client/AssistantChat.js +1 -1
- package/dist/client/agent-chat-adapter.d.ts +1 -1
- package/dist/client/agent-chat-adapter.js +30 -3
- package/dist/client/agent-page/AgentWorkspaceContent.d.ts +1 -1
- package/dist/client/agent-page/AgentWorkspaceContent.js +13 -13
- package/dist/client/chat/run-recovery.d.ts +2 -1
- package/dist/client/chat/run-recovery.js +14 -3
- package/dist/client/embed-auth.js +6 -2
- package/dist/client/error-format.d.ts +1 -0
- package/dist/client/error-format.js +16 -2
- package/dist/client/integrations/IntegrationsPanel.js +40 -6
- package/dist/client/org/OrgSwitcher.js +2 -2
- package/dist/client/org/TeamPage.js +92 -72
- package/dist/client/require-session.d.ts +1 -1
- package/dist/client/require-session.js +3 -3
- package/dist/client/resources/use-mcp-servers.d.ts +5 -0
- package/dist/client/resources/use-mcp-servers.js +36 -0
- package/dist/client/settings/AccountSettingsCard.js +40 -35
- package/dist/client/settings/AutomationsSection.js +1 -1
- package/dist/client/settings/DemoModeSection.d.ts +3 -1
- package/dist/client/settings/DemoModeSection.js +4 -1
- package/dist/client/settings/ExtensionsSettingsContent.js +1 -1
- package/dist/client/settings/SchedulingTimezoneField.d.ts +3 -1
- package/dist/client/settings/SchedulingTimezoneField.js +9 -5
- package/dist/client/settings/SettingsPanel.d.ts +4 -1
- package/dist/client/settings/SettingsPanel.js +21 -21
- package/dist/client/settings/SettingsTabsPage.d.ts +2 -2
- package/dist/client/settings/SettingsTabsPage.js +98 -45
- package/dist/client/settings/VoiceTranscriptionSection.js +6 -1
- package/dist/client/settings/useSettingsPanelController.js +47 -8
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/ingestion/website.d.ts +30 -0
- package/dist/localization/default-messages.d.ts +5 -0
- package/dist/localization/default-messages.js +5 -0
- package/dist/mcp-client/oauth-routes.js +1 -1
- package/dist/mcp-client/routes.d.ts +1 -0
- package/dist/mcp-client/routes.js +49 -0
- package/dist/navigation/index.js +16 -5
- package/dist/observability/routes.d.ts +3 -3
- package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
- package/dist/provider-api/actions/provider-api.d.ts +11 -11
- package/dist/scripts/call-agent.js +11 -0
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +9 -7
- package/dist/server/identity-sso.js +2 -1
- package/dist/server/onboarding-html.js +57 -34
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/workspace-provider-oauth.js +1 -1
- package/dist/setup-connections/onboarding.js +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +1 -1
- package/dist/shared/sign-in-journey.d.ts +5 -2
- package/dist/shared/sign-in-journey.js +10 -4
- package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
- package/dist/templates/chat/app/hooks/use-navigation-state.ts +1 -1
- package/dist/templates/chat/app/root.tsx +1 -1
- package/dist/templates/chat/app/routes/agent.tsx +7 -7
- package/dist/templates/chat/app/routes/extensions._index.tsx +1 -1
- package/dist/templates/chat/app/routes/settings.$.tsx +1 -0
- package/dist/templates/chat/app/routes/team.tsx +1 -1
- package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
- package/docs/content/authentication.mdx +4 -5
- package/docs/content/template-analytics-developers.mdx +1 -1
- package/docs/content/template-calendar-developers.mdx +1 -1
- package/docs/content/template-chat-developers.mdx +2 -2
- package/docs/content/template-content-developers.mdx +1 -1
- package/docs/content/template-mail-developers.mdx +1 -1
- package/docs/content/template-plan-developers.mdx +1 -1
- package/docs/content/toolkit-command-navigation.mdx +6 -6
- package/docs/content/toolkit-org-team.mdx +1 -1
- package/docs/content/toolkit-settings.mdx +8 -8
- package/docs/content/toolkit-setup-connections.mdx +1 -1
- package/package.json +1 -1
- package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
- package/src/templates/chat/app/hooks/use-navigation-state.ts +1 -1
- package/src/templates/chat/app/root.tsx +1 -1
- package/src/templates/chat/app/routes/agent.tsx +7 -7
- package/src/templates/chat/app/routes/extensions._index.tsx +1 -1
- package/src/templates/chat/app/routes/settings.$.tsx +1 -0
- package/src/templates/chat/app/routes/team.tsx +1 -1
- package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
- package/corpus/templates/analytics/.agents/skills/dashboard-management/references/template-catalog-and-demo.md +0 -62
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +0 -369
- package/corpus/templates/analytics/actions/list-dashboard-templates.ts +0 -47
- package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +0 -34
- package/corpus/templates/analytics/app/routes/catalog.tsx +0 -240
- package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-stay-complete-and-atomic.md +0 -6
- package/corpus/templates/analytics/changelog/2026-08-05-historical-rollups-stay-complete-while-live-events-arrive.md +0 -6
- package/src/templates/default/actions/navigate.spec.ts +0 -53
|
@@ -40,7 +40,10 @@ import {
|
|
|
40
40
|
} from "@/components/ui/alert-dialog";
|
|
41
41
|
import { Button } from "@/components/ui/button";
|
|
42
42
|
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
|
43
|
-
import
|
|
43
|
+
import {
|
|
44
|
+
describeDeckPersistenceFailure,
|
|
45
|
+
type Deck,
|
|
46
|
+
} from "@/context/DeckContext";
|
|
44
47
|
import { useDecks } from "@/context/DeckContext";
|
|
45
48
|
import { useAgentGenerating } from "@/hooks/use-agent-generating";
|
|
46
49
|
import { useDesignSystems } from "@/hooks/use-design-systems";
|
|
@@ -571,14 +574,17 @@ export default function Index() {
|
|
|
571
574
|
setNewDeckPromptOpen(false);
|
|
572
575
|
|
|
573
576
|
const persisted = await ensureDeckPersisted(deck.id);
|
|
574
|
-
if (!persisted) {
|
|
577
|
+
if (!persisted.persisted) {
|
|
575
578
|
if (!savePromptForRetry(prompt)) {
|
|
576
579
|
setNewDeckInitialPrompt({ text: prompt, key: Date.now() });
|
|
577
580
|
}
|
|
578
581
|
setNewDeckRetryFiles(filesForGeneration);
|
|
579
582
|
deleteDeck(deckId);
|
|
580
583
|
toast.error(t("home.generationStartFailed"), {
|
|
581
|
-
description:
|
|
584
|
+
description: describeDeckPersistenceFailure(
|
|
585
|
+
persisted,
|
|
586
|
+
t("home.generationStartFailedDescription"),
|
|
587
|
+
),
|
|
582
588
|
});
|
|
583
589
|
setShowNewDeckPrompt(true);
|
|
584
590
|
return;
|
|
@@ -747,9 +753,14 @@ export default function Index() {
|
|
|
747
753
|
noDefaultSlides: true,
|
|
748
754
|
});
|
|
749
755
|
const persisted = await ensureDeckPersisted(referenceDeck.id);
|
|
750
|
-
if (!persisted) {
|
|
756
|
+
if (!persisted.persisted) {
|
|
751
757
|
deleteDeck(referenceDeck.id);
|
|
752
|
-
throw new Error(
|
|
758
|
+
throw new Error(
|
|
759
|
+
describeDeckPersistenceFailure(
|
|
760
|
+
persisted,
|
|
761
|
+
"The PDF reference deck could not be saved.",
|
|
762
|
+
),
|
|
763
|
+
);
|
|
753
764
|
}
|
|
754
765
|
try {
|
|
755
766
|
const imported = (await callAction("import-file", {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, meta } from "./settings.js";
|
|
@@ -11,7 +11,9 @@ type Translate = ReturnType<typeof useT>;
|
|
|
11
11
|
|
|
12
12
|
function resolveTitle(t: Translate, pathname: string): string {
|
|
13
13
|
if (pathname === "/tasks") return t("header.pageTasks");
|
|
14
|
-
if (pathname === "/settings"
|
|
14
|
+
if (pathname === "/settings" || pathname.startsWith("/settings/")) {
|
|
15
|
+
return t("header.pageSettings");
|
|
16
|
+
}
|
|
15
17
|
if (pathname === "/team") return t("header.pageTeam");
|
|
16
18
|
if (pathname.startsWith("/extensions")) return t("header.pageExtensions");
|
|
17
19
|
return APP_TITLE;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, meta } from "./settings.js";
|
|
@@ -193,9 +193,14 @@ class AISDKEngine {
|
|
|
193
193
|
// `builder-engine` and `anthropic-engine` already fail closed here; this
|
|
194
194
|
// engine was the only one that did not.
|
|
195
195
|
//
|
|
196
|
-
// A
|
|
197
|
-
// legitimately accept unauthenticated requests.
|
|
198
|
-
|
|
196
|
+
// A LOCAL `baseUrl` is exempt: a self-hosted gateway on the same machine or
|
|
197
|
+
// private network may legitimately accept unauthenticated requests. A public
|
|
198
|
+
// one may not — every hosted provider requires a key, so exempting any
|
|
199
|
+
// baseUrl at all reopened the same doomed unauthenticated request this
|
|
200
|
+
// guard exists to stop, just for anyone pointing at a remote gateway.
|
|
201
|
+
if (!this.apiKey &&
|
|
202
|
+
!isLocalBaseUrl(this.baseUrl) &&
|
|
203
|
+
this.requiredEnvVars.length > 0) {
|
|
199
204
|
yield {
|
|
200
205
|
type: "stop",
|
|
201
206
|
reason: "error",
|
|
@@ -531,6 +536,47 @@ async function importProviderPackage(provider) {
|
|
|
531
536
|
return import("ai-sdk-ollama");
|
|
532
537
|
}
|
|
533
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* True only for a gateway on this machine or a private network — the case the
|
|
541
|
+
* keyless exemption was written for. Anything routable on the public internet
|
|
542
|
+
* (openrouter.ai, an api.* host, a cloud proxy) needs credentials, so treating
|
|
543
|
+
* it as keyless just sends a request that cannot succeed.
|
|
544
|
+
*
|
|
545
|
+
* An unparseable value is NOT local: this gates a security-shaped decision, so
|
|
546
|
+
* the ambiguous case has to take the safe branch and require a key.
|
|
547
|
+
*/
|
|
548
|
+
function isLocalBaseUrl(baseUrl) {
|
|
549
|
+
if (!baseUrl)
|
|
550
|
+
return false;
|
|
551
|
+
let host;
|
|
552
|
+
try {
|
|
553
|
+
host = new URL(baseUrl).hostname.toLowerCase();
|
|
554
|
+
}
|
|
555
|
+
catch (error) {
|
|
556
|
+
// An invalid URL is never a local exemption; fail closed.
|
|
557
|
+
if (error instanceof TypeError)
|
|
558
|
+
return false;
|
|
559
|
+
throw error;
|
|
560
|
+
}
|
|
561
|
+
if (host === "localhost" || host.endsWith(".localhost"))
|
|
562
|
+
return true;
|
|
563
|
+
if (host === "::1" || host === "[::1]")
|
|
564
|
+
return true;
|
|
565
|
+
if (host.endsWith(".local") || host.endsWith(".internal"))
|
|
566
|
+
return true;
|
|
567
|
+
// IPv4 loopback and the RFC1918 private ranges.
|
|
568
|
+
const v4 = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
569
|
+
if (!v4)
|
|
570
|
+
return false;
|
|
571
|
+
const [a, b] = [Number(v4[1]), Number(v4[2])];
|
|
572
|
+
if (a === 127 || a === 10)
|
|
573
|
+
return true;
|
|
574
|
+
if (a === 192 && b === 168)
|
|
575
|
+
return true;
|
|
576
|
+
if (a === 172 && b >= 16 && b <= 31)
|
|
577
|
+
return true;
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
534
580
|
function capitalize(s) {
|
|
535
581
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
536
582
|
}
|
|
@@ -922,6 +922,7 @@ export interface ChainServerDrivenContinuationDeps {
|
|
|
922
922
|
updateRunStatusIfRunning?: typeof updateRunStatusIfRunning;
|
|
923
923
|
markRunAborted?: typeof markRunAborted;
|
|
924
924
|
setRunTerminalReason?: typeof setRunTerminalReason;
|
|
925
|
+
setRunError?: typeof setRunError;
|
|
925
926
|
recordRunDiagnostic?: typeof recordRunDiagnostic;
|
|
926
927
|
markBackgroundContinuationChunkTerminal?: typeof markBackgroundContinuationChunkTerminal;
|
|
927
928
|
generateRunId?: typeof generateRunId;
|
|
@@ -5271,6 +5271,7 @@ export async function chainServerDrivenContinuation(opts) {
|
|
|
5271
5271
|
updateRunStatusIfRunning: opts.deps?.updateRunStatusIfRunning ?? updateRunStatusIfRunning,
|
|
5272
5272
|
markRunAborted: opts.deps?.markRunAborted ?? markRunAborted,
|
|
5273
5273
|
setRunTerminalReason: opts.deps?.setRunTerminalReason ?? setRunTerminalReason,
|
|
5274
|
+
setRunError: opts.deps?.setRunError ?? setRunError,
|
|
5274
5275
|
recordRunDiagnostic: opts.deps?.recordRunDiagnostic ?? recordRunDiagnostic,
|
|
5275
5276
|
markBackgroundContinuationChunkTerminal: opts.deps?.markBackgroundContinuationChunkTerminal ??
|
|
5276
5277
|
markBackgroundContinuationChunkTerminal,
|
|
@@ -5545,6 +5546,13 @@ export async function chainServerDrivenContinuation(opts) {
|
|
|
5545
5546
|
await d
|
|
5546
5547
|
.setRunTerminalReason(runId, "background_continuation_dispatch_failed")
|
|
5547
5548
|
.catch(() => { });
|
|
5549
|
+
// Record the cause on the row itself, not only inside diag_stage's JSON.
|
|
5550
|
+
// Without this the run goes terminal with error_code and error_detail
|
|
5551
|
+
// both NULL, so every dashboard and query reads it as a failure with no
|
|
5552
|
+
// known cause and the real message is only findable by parsing a blob.
|
|
5553
|
+
await d
|
|
5554
|
+
.setRunError(runId, "background_continuation_dispatch_failed", chainErr instanceof Error ? chainErr.message : String(chainErr))
|
|
5555
|
+
.catch(() => { });
|
|
5548
5556
|
}
|
|
5549
5557
|
}
|
|
5550
5558
|
}
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -275,6 +275,13 @@ export type AgentChatEvent = {
|
|
|
275
275
|
/** Remote task to resume when status is pending/input-required. */
|
|
276
276
|
taskId?: string;
|
|
277
277
|
durationMs?: number;
|
|
278
|
+
/**
|
|
279
|
+
* Why the call ended, on a terminal status. Already computed for
|
|
280
|
+
* telemetry; without it here the persisted event says only that a
|
|
281
|
+
* cross-app call failed after N ms and never why, so a failed A2A call
|
|
282
|
+
* cannot be diagnosed from the database without a repro.
|
|
283
|
+
*/
|
|
284
|
+
terminalCode?: string;
|
|
278
285
|
} | {
|
|
279
286
|
/**
|
|
280
287
|
* Periodic liveness for an in-flight cross-app A2A call. Emitted by the
|
|
@@ -4007,7 +4007,7 @@ const AssistantChatInner = forwardRef(function AssistantChatInner({ emptyStateTe
|
|
|
4007
4007
|
} }) })), shouldShowRunError && visibleRunError && (_jsx(MessageScrollerItem, { children: _jsx(RunErrorRecoveryCard, { info: visibleRunError, onContinue: () => {
|
|
4008
4008
|
setRunErrorInfo(null);
|
|
4009
4009
|
addToQueue(RECONNECT_NO_PROGRESS_CONTINUE_MESSAGE, undefined, undefined, undefined, undefined, "queued", "continue");
|
|
4010
|
-
}, onRetry: retryAfterRunError, onFork: onForkChat, onDismiss: () => {
|
|
4010
|
+
}, onRetry: retryAfterRunError, onFork: onForkChat, onProviderConnected: handleBuilderConnected, onDismiss: () => {
|
|
4011
4011
|
if (visibleRunErrorKey) {
|
|
4012
4012
|
setDismissedRunErrorKey(visibleRunErrorKey);
|
|
4013
4013
|
}
|
|
@@ -14,7 +14,7 @@ export type AgentChatSurfaceKind =
|
|
|
14
14
|
/** Chat hosted by Desktop for a local app with explicit code access. */
|
|
15
15
|
| "desktop";
|
|
16
16
|
export declare const BACKGROUND_FOLLOW_IDLE_TIMEOUT_MS = 210000;
|
|
17
|
-
export declare const MAX_FOLLOWED_BACKGROUND_RUNS =
|
|
17
|
+
export declare const MAX_FOLLOWED_BACKGROUND_RUNS = 24;
|
|
18
18
|
export declare const MAX_BACKGROUND_FOLLOW_WALL_TIME_MS: number;
|
|
19
19
|
/**
|
|
20
20
|
* Server truth for "does this run still look alive?", used to keep the client
|
|
@@ -131,8 +131,30 @@ export const BACKGROUND_FOLLOW_IDLE_TIMEOUT_MS = 210_000;
|
|
|
131
131
|
// every poll tick and the follow loop never exits (prod: 26 chained runs over
|
|
132
132
|
// 22 minutes, all error:stale_run, user watching a spinner). These bound the
|
|
133
133
|
// whole turn instead, and an identical repeated failure counts as no progress.
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
//
|
|
135
|
+
// CLIENT-ABOVE-SERVER INVARIANT (asserted in agent-chat-adapter.spec.ts).
|
|
136
|
+
// These are a backstop for a server that has gone silent in a way the idle
|
|
137
|
+
// timeout misses — NOT the primary limit. They must stay ABOVE the server's
|
|
138
|
+
// own ceilings so the server, which can actually tell progress from looping,
|
|
139
|
+
// always terminates a turn first and writes a truthful terminal reason:
|
|
140
|
+
// BACKGROUND_SOFT_TIMEOUT_CEILING_MS (13 min, run-manager.ts) — one chunk
|
|
141
|
+
// MAX_TURN_WALL_CLOCK_MS (90 min, production-agent.ts) — one turn
|
|
142
|
+
// MAX_BACKGROUND_RUN_CONTINUATIONS (20, production-agent.ts)
|
|
143
|
+
//
|
|
144
|
+
// They were originally set to 10 min / 6 runs, which put the whole-turn client
|
|
145
|
+
// budget BELOW the 13-minute ceiling of a single legal chunk. Any turn needing
|
|
146
|
+
// a second full-length chunk was killed by the client while the server was
|
|
147
|
+
// healthy and had 80 minutes left — measured in prod as turns dying at 11-25
|
|
148
|
+
// minutes with `last_progress_at` tracking the abort, i.e. still streaming
|
|
149
|
+
// tokens and completing tools when the client gave up. That is the top
|
|
150
|
+
// non-auth cause of "the chat just stopped" reports.
|
|
151
|
+
//
|
|
152
|
+
// Killing a turn that is NOT progressing is already covered twice over, by
|
|
153
|
+
// mechanisms that read progress rather than a clock: the 210s idle timeout
|
|
154
|
+
// above, and MAX_REPEATED_BACKGROUND_TERMINAL_REASONS below. Do not re-tighten
|
|
155
|
+
// these two to catch a stuck turn — fix the progress signal instead.
|
|
156
|
+
export const MAX_FOLLOWED_BACKGROUND_RUNS = 24;
|
|
157
|
+
export const MAX_BACKGROUND_FOLLOW_WALL_TIME_MS = 95 * 60_000;
|
|
136
158
|
const MAX_REPEATED_BACKGROUND_TERMINAL_REASONS = 3;
|
|
137
159
|
// A re-observed terminal run whose outcome would be an ERROR (never a
|
|
138
160
|
// genuine "done" success) gets a short extra grace window before the follow
|
|
@@ -2185,7 +2207,12 @@ export function createAgentChatAdapter(options) {
|
|
|
2185
2207
|
if (Date.now() - followStartedAt >=
|
|
2186
2208
|
MAX_BACKGROUND_FOLLOW_WALL_TIME_MS) {
|
|
2187
2209
|
yield* stopBackgroundTurnBeforeReporting({
|
|
2188
|
-
|
|
2210
|
+
// Says only what this backstop actually knows. It fires on a
|
|
2211
|
+
// clock, so it cannot tell a looping turn from one that was
|
|
2212
|
+
// still working — the old copy asserted "kept restarting
|
|
2213
|
+
// without finishing", which prod runs contradicted: they were
|
|
2214
|
+
// streaming tokens right up to the abort.
|
|
2215
|
+
message: `This turn ran for ${Math.round(MAX_BACKGROUND_FOLLOW_WALL_TIME_MS / 60_000)} minutes without finishing, so it was stopped. Your chat context is preserved — retrying, or splitting this into smaller requests, usually gets through.`,
|
|
2189
2216
|
errorCode: "background_follow_time_budget_exhausted",
|
|
2190
2217
|
details: `followed_runs: ${followedRunIds.size}`,
|
|
2191
2218
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
export type AgentWorkspaceTab = "overview" | "resources" | "automations" | "agents"
|
|
2
|
+
export type AgentWorkspaceTab = "overview" | "resources" | "automations" | "agents";
|
|
3
3
|
export declare function AgentWorkspaceContent({ overview, className, activeTab, }: {
|
|
4
4
|
overview: ReactNode;
|
|
5
5
|
activeTab: AgentWorkspaceTab;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { IconBook2, IconBolt, IconChecklist, IconFolder, IconHierarchy2, IconHistory, IconNotes,
|
|
2
|
+
import { IconBook2, IconBolt, IconChecklist, IconFolder, IconHierarchy2, IconHistory, IconNotes, IconTopologyRing2, } from "@tabler/icons-react";
|
|
3
3
|
import { lazy, Suspense, useEffect, useState } from "react";
|
|
4
4
|
import { AgentsSection } from "../settings/AgentsSection.js";
|
|
5
5
|
import { cn } from "../utils.js";
|
|
@@ -21,7 +21,6 @@ const HUB_TABS = [
|
|
|
21
21
|
{ id: "resources", label: "Resources", icon: IconFolder },
|
|
22
22
|
{ id: "automations", label: "Automations", icon: IconBolt },
|
|
23
23
|
{ id: "agents", label: "Connected agents", icon: IconTopologyRing2 },
|
|
24
|
-
{ id: "access", label: "Access", icon: IconShieldLock },
|
|
25
24
|
];
|
|
26
25
|
const RESOURCE_IDS = new Set(RESOURCE_TABS.map((tab) => tab.id));
|
|
27
26
|
function normalizeHash(value) {
|
|
@@ -31,9 +30,15 @@ function initialHubState() {
|
|
|
31
30
|
if (typeof window === "undefined") {
|
|
32
31
|
return { tab: "overview", resource: "files" };
|
|
33
32
|
}
|
|
33
|
+
const pathParts = window.location.pathname.split("/").filter(Boolean);
|
|
34
|
+
const settingsIndex = pathParts.indexOf("settings");
|
|
35
|
+
const settingsPath = settingsIndex === -1 ? [] : pathParts.slice(settingsIndex + 1);
|
|
36
|
+
const routeParts = settingsPath[0] === "agent" ? settingsPath : ["agent", ...settingsPath];
|
|
34
37
|
const hash = normalizeHash(window.location.hash);
|
|
35
|
-
const
|
|
36
|
-
const
|
|
38
|
+
const hashParts = hash.split(":");
|
|
39
|
+
const pathScoped = settingsPath[0] === "agent";
|
|
40
|
+
const scoped = pathScoped || hashParts[0] === "agent";
|
|
41
|
+
const parts = pathScoped ? routeParts : hashParts;
|
|
37
42
|
const requested = scoped ? parts[1] : hash;
|
|
38
43
|
const resource = scoped && RESOURCE_IDS.has(parts[2])
|
|
39
44
|
? parts[2]
|
|
@@ -54,8 +59,6 @@ function initialHubState() {
|
|
|
54
59
|
requested === "remote-agents") {
|
|
55
60
|
return { tab: "agents", resource };
|
|
56
61
|
}
|
|
57
|
-
if (requested === "access")
|
|
58
|
-
return { tab: "access", resource };
|
|
59
62
|
return { tab: "overview", resource };
|
|
60
63
|
}
|
|
61
64
|
function ResourceContent({ resource }) {
|
|
@@ -65,8 +68,8 @@ function ResourceContent({ resource }) {
|
|
|
65
68
|
const selected = tab.id === resource;
|
|
66
69
|
return (_jsxs("button", { type: "button", onClick: () => {
|
|
67
70
|
if (typeof window !== "undefined") {
|
|
68
|
-
window.history.pushState(null, "",
|
|
69
|
-
window.dispatchEvent(new Event("
|
|
71
|
+
window.history.pushState(null, "", `/settings/agent/resources/${tab.id}${window.location.search}`);
|
|
72
|
+
window.dispatchEvent(new Event("popstate"));
|
|
70
73
|
}
|
|
71
74
|
}, className: cn("inline-flex shrink-0 items-center gap-1.5 rounded-md px-2.5 py-1.5 text-xs font-medium transition-colors", selected
|
|
72
75
|
? "bg-accent text-foreground"
|
|
@@ -75,9 +78,6 @@ function ResourceContent({ resource }) {
|
|
|
75
78
|
? "Add context the agent can use across conversations."
|
|
76
79
|
: "Manage the resources that shape how the agent works." })] }), _jsx(Suspense, { fallback: _jsx("div", { className: "h-48 animate-pulse rounded-xl border border-border bg-muted/20" }), children: _jsx(ResourcesPanel, { showMcpServers: false, resourceFilter: resource, resourceTreeVariant: "collection", scope: "personal" }, resource) })] }));
|
|
77
80
|
}
|
|
78
|
-
function AccessContent() {
|
|
79
|
-
return (_jsx("div", { children: _jsx("div", { className: "rounded-xl border border-border/70 bg-card text-card-foreground", children: _jsx("div", { className: "px-5 py-5 sm:px-6", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("span", { className: "flex size-9 shrink-0 items-center justify-center rounded-lg border border-border bg-background text-muted-foreground", children: _jsx(IconShieldLock, { className: "size-4" }) }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-sm font-medium text-foreground", children: "Agent access" }), _jsx("p", { className: "mt-1 text-sm leading-5 text-muted-foreground", children: "Use the agent card and MCP endpoints from connected clients." })] })] }) }) }) }));
|
|
80
|
-
}
|
|
81
81
|
export function AgentWorkspaceContent({ overview, className, activeTab, }) {
|
|
82
82
|
const [{ resource }, setState] = useState(initialHubState);
|
|
83
83
|
useEffect(() => {
|
|
@@ -98,8 +98,8 @@ export function AgentWorkspaceContent({ overview, className, activeTab, }) {
|
|
|
98
98
|
? "Schedule and manage agent tasks."
|
|
99
99
|
: activeTab === "agents"
|
|
100
100
|
? "Connect agents to delegate work from chat."
|
|
101
|
-
: "
|
|
102
|
-
return (_jsxs("div", { className: cn("w-full space-y-6", className), children: [_jsxs("header", { className: "space-y-1", children: [_jsx("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: activeTab === "overview" ? "Agent" : page?.label }), _jsx("p", { className: "text-sm leading-5 text-muted-foreground", children: pageDescription })] }), activeTab === "overview" && overview, activeTab === "resources" && _jsx(ResourceContent, { resource: resource }), activeTab === "automations" && (_jsx(Suspense, { fallback: _jsx("div", { className: "h-48 animate-pulse rounded-xl border border-border bg-muted/20" }), children: _jsx(AgentJobsTab, { scope: "user", canManageOrg: true, hideHeader: true }) })), activeTab === "agents" && _jsx(AgentsSection, {})
|
|
101
|
+
: "Configure how your agent works, what it knows, and what it can do.";
|
|
102
|
+
return (_jsxs("div", { className: cn("w-full space-y-6", className), children: [_jsxs("header", { className: "space-y-1", children: [_jsx("h1", { className: "text-2xl font-semibold tracking-tight text-foreground", children: activeTab === "overview" ? "Agent" : page?.label }), _jsx("p", { className: "text-sm leading-5 text-muted-foreground", children: pageDescription })] }), activeTab === "overview" && overview, activeTab === "resources" && _jsx(ResourceContent, { resource: resource }), activeTab === "automations" && (_jsx(Suspense, { fallback: _jsx("div", { className: "h-48 animate-pulse rounded-xl border border-border bg-muted/20" }), children: _jsx(AgentJobsTab, { scope: "user", canManageOrg: true, hideHeader: true }) })), activeTab === "agents" && _jsx(AgentsSection, {})] }));
|
|
103
103
|
}
|
|
104
104
|
export function agentWorkspaceTabHash(tab) {
|
|
105
105
|
return `agent:${tab}`;
|
|
@@ -35,12 +35,13 @@ export declare function BuilderSetupCard({ onConnected, bouncePulse, fullWidth,
|
|
|
35
35
|
fullWidth?: boolean;
|
|
36
36
|
layout?: BuilderSetupCardLayout;
|
|
37
37
|
}): import("react").JSX.Element;
|
|
38
|
-
export declare function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDismiss, }: {
|
|
38
|
+
export declare function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDismiss, onProviderConnected, }: {
|
|
39
39
|
info: RunErrorInfo;
|
|
40
40
|
onContinue: () => void;
|
|
41
41
|
onRetry: () => void;
|
|
42
42
|
onFork?: () => void | boolean | Promise<void | boolean>;
|
|
43
43
|
onDismiss: () => void;
|
|
44
|
+
onProviderConnected?: () => void;
|
|
44
45
|
}): import("react").JSX.Element;
|
|
45
46
|
export declare function LoopLimitContinueCard({ info, onContinue, }: {
|
|
46
47
|
info: LoopLimitInfo;
|
|
@@ -8,6 +8,7 @@ import { useState, useEffect, useCallback, useRef } from "react";
|
|
|
8
8
|
import { saveAgentEngineApiKey, } from "../agent-engine-key.js";
|
|
9
9
|
import { agentNativePath } from "../api-path.js";
|
|
10
10
|
import { writeClipboardText } from "../clipboard.js";
|
|
11
|
+
import { isProviderAuthenticationError } from "../error-format.js";
|
|
11
12
|
import { useT } from "../i18n.js";
|
|
12
13
|
import { useBuilderConnectFlow } from "../settings/useBuilderStatus.js";
|
|
13
14
|
import { cn } from "../utils.js";
|
|
@@ -215,7 +216,7 @@ export function BuilderSetupCard({ onConnected, bouncePulse, fullWidth, layout =
|
|
|
215
216
|
: "mx-auto w-full max-w-[42rem] px-3 pb-2 sm:w-fit"), children: _jsx("div", { className: cn("agent-builder-setup-card__panel rounded-lg border border-border/80 bg-background/80 shadow-sm backdrop-blur", sidebarLayout ? "p-2.5" : "p-3"), children: _jsx(BuilderSetupContent, { onConnected: onConnected, layout: layout }) }) }));
|
|
216
217
|
}
|
|
217
218
|
// ─── RunErrorRecoveryCard ─────────────────────────────────────────────────────
|
|
218
|
-
export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDismiss, }) {
|
|
219
|
+
export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDismiss, onProviderConnected, }) {
|
|
219
220
|
const [detailsOpen, setDetailsOpen] = useState(false);
|
|
220
221
|
const [copyState, setCopyState] = useState("idle");
|
|
221
222
|
const [forking, setForking] = useState(false);
|
|
@@ -225,6 +226,12 @@ export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDism
|
|
|
225
226
|
});
|
|
226
227
|
const canRecover = info.recoverable === true;
|
|
227
228
|
const shouldShowBuilderReconnect = isBuilderReconnectRunError(info);
|
|
229
|
+
const isProviderAuthError = isProviderAuthenticationError([info.message, info.details].filter(Boolean).join("\n"), info.errorCode);
|
|
230
|
+
// Blocked on something the reader goes and fixes elsewhere, then comes back
|
|
231
|
+
// to. Without a retry the card is a dead end and its own copy ("then retry")
|
|
232
|
+
// points at a button that isn't there.
|
|
233
|
+
const isUnblockableExternally = info.errorCode === "email_verification_required";
|
|
234
|
+
const canRetry = canRecover || isProviderAuthError || isUnblockableExternally;
|
|
228
235
|
const builderReconnectResolved = shouldShowBuilderReconnect &&
|
|
229
236
|
builderReconnect.hasFetchedStatus &&
|
|
230
237
|
builderReconnect.configured;
|
|
@@ -254,6 +261,10 @@ export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDism
|
|
|
254
261
|
window.dispatchEvent(new CustomEvent("agent-chat:new-chat"));
|
|
255
262
|
onDismiss();
|
|
256
263
|
}, [onDismiss]);
|
|
264
|
+
const handleProviderConnected = useCallback(() => {
|
|
265
|
+
onProviderConnected?.();
|
|
266
|
+
onDismiss();
|
|
267
|
+
}, [onDismiss, onProviderConnected]);
|
|
257
268
|
const handleFork = useCallback(async () => {
|
|
258
269
|
if (!onFork || forking)
|
|
259
270
|
return;
|
|
@@ -277,9 +288,9 @@ export function RunErrorRecoveryCard({ info, onContinue, onRetry, onFork, onDism
|
|
|
277
288
|
onDismiss();
|
|
278
289
|
}
|
|
279
290
|
}, [builderReconnectResolved, onDismiss]);
|
|
280
|
-
return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] p-3 text-sm", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-700 dark:text-amber-300", children: _jsx(IconAlertTriangle, { size: 14 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-medium text-foreground", children: runErrorHeadline(info) }), _jsx("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: info.message }), shouldShowBuilderReconnect && !builderReconnectResolved && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "The current Builder.io or model-provider credential was rejected. Reconnect Builder.io (free tier available), then retry this message." })), isConnectionRecoveryError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "If retry lands on the same error, start a new chat session and continue from what already changed." })), (info.runId || info.errorCode || info.details) && (_jsxs("button", { type: "button", onClick: () => setDetailsOpen((v) => !v), className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { size: 12, className: cn("transition-transform", detailsOpen && "rotate-180") }), "Details"] })), detailsOpen && (_jsxs("div", { className: "mt-2 rounded-md border border-border/60 bg-background/70 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: [info.runId && _jsxs("div", { children: ["run: ", info.runId] }), info.errorCode && _jsxs("div", { children: ["code: ", info.errorCode] }), info.details && (_jsx("pre", { className: "mt-2 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details }))] }))] }), _jsx("button", { type: "button", onClick: onDismiss, "aria-label": "Dismiss", className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-background/80 hover:text-foreground", children: _jsx(IconX, { size: 14 }) })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [shouldShowBuilderReconnect && !builderReconnectResolved && (_jsxs("button", { type: "button", onClick: () => builderReconnect.start(), disabled: builderReconnect.connecting, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-70", children: [builderReconnect.connecting ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : null, builderReconnect.connecting
|
|
291
|
+
return (_jsxs("div", { className: "rounded-lg border border-amber-500/25 bg-amber-500/[0.06] p-3 text-sm", children: [_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "mt-0.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-amber-500/10 text-amber-700 dark:text-amber-300", children: _jsx(IconAlertTriangle, { size: 14 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "font-medium text-foreground", children: runErrorHeadline(info) }), _jsx("p", { className: "mt-1 text-xs leading-relaxed text-muted-foreground", children: info.message }), isProviderAuthError && (_jsx("div", { className: "mt-3 rounded-md border border-border/70 bg-background/60 p-2.5", children: _jsx(BuilderSetupContent, { layout: "sidebar", onConnected: handleProviderConnected }) })), shouldShowBuilderReconnect && !builderReconnectResolved && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "The current Builder.io or model-provider credential was rejected. Reconnect Builder.io (free tier available), then retry this message." })), isConnectionRecoveryError && (_jsx("p", { className: "mt-2 text-xs leading-relaxed text-muted-foreground", children: "If retry lands on the same error, start a new chat session and continue from what already changed." })), (info.runId || info.errorCode || info.details) && (_jsxs("button", { type: "button", onClick: () => setDetailsOpen((v) => !v), className: "mt-2 inline-flex items-center gap-1 text-[11px] font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronDown, { size: 12, className: cn("transition-transform", detailsOpen && "rotate-180") }), "Details"] })), detailsOpen && (_jsxs("div", { className: "mt-2 rounded-md border border-border/60 bg-background/70 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: [info.runId && _jsxs("div", { children: ["run: ", info.runId] }), info.errorCode && _jsxs("div", { children: ["code: ", info.errorCode] }), info.details && (_jsx("pre", { className: "mt-2 max-h-28 overflow-auto whitespace-pre-wrap break-words font-mono", children: info.details }))] }))] }), _jsx("button", { type: "button", onClick: onDismiss, "aria-label": "Dismiss", className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-background/80 hover:text-foreground", children: _jsx(IconX, { size: 14 }) })] }), _jsxs("div", { className: "mt-3 flex flex-wrap items-center gap-2", children: [shouldShowBuilderReconnect && !builderReconnectResolved && (_jsxs("button", { type: "button", onClick: () => builderReconnect.start(), disabled: builderReconnect.connecting, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90 disabled:cursor-wait disabled:opacity-70", children: [builderReconnect.connecting ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : null, builderReconnect.connecting
|
|
281
292
|
? "Connecting Builder.io"
|
|
282
|
-
: "Reconnect Builder.io"] })), canRecover && (
|
|
293
|
+
: "Reconnect Builder.io"] })), canRecover && (_jsx(_Fragment, { children: _jsxs("button", { type: "button", onClick: onContinue, className: "inline-flex h-8 items-center gap-1.5 rounded-md bg-foreground px-3 text-xs font-medium text-background hover:opacity-90", children: [_jsx(IconPlayerPlay, { size: 13 }), "Continue"] }) })), canRetry && (_jsxs("button", { type: "button", onClick: onRetry, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconRefresh, { size: 13 }), isQueryError ? "Diagnose and retry" : "Retry"] })), canRecover && isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: startNewChat, className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent", children: [_jsx(IconPlus, { size: 13 }), "New chat"] })), canRecover && onFork && !isConnectionRecoveryError && (_jsxs("button", { type: "button", onClick: handleFork, disabled: forking, title: "Fork this conversation into a separate chat thread.", "aria-label": "Fork this conversation into a separate chat thread", className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-border bg-background px-3 text-xs font-medium text-foreground hover:bg-accent disabled:cursor-wait disabled:opacity-70", children: [forking ? (_jsx(IconLoader2, { size: 13, className: "animate-spin" })) : (_jsx(IconGitFork, { size: 13 })), forking ? "Forking..." : "Fork chat"] })), _jsxs("button", { type: "button", onClick: copyDetails, className: "ml-auto inline-flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-medium text-muted-foreground hover:bg-background/80 hover:text-foreground", children: [copyState === "copied" ? (_jsx(IconCheck, { size: 13 })) : copyState === "failed" ? (_jsx(IconX, { size: 13 })) : (_jsx(IconCopy, { size: 13 })), _jsx("span", { "aria-live": "polite", children: copyState === "copied"
|
|
283
294
|
? "Copied"
|
|
284
295
|
: copyState === "failed"
|
|
285
296
|
? "Copy failed"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EMBED_MODE_QUERY_PARAM, EMBED_START_PATH, EMBED_TARGET_HEADER, EMBED_TARGET_QUERY_PARAM, EMBED_TOKEN_QUERY_PARAM, MCP_APP_CHAT_BRIDGE_QUERY_PARAM, } from "../shared/embed-auth.js";
|
|
2
|
+
import { SIGN_IN_ENTRY_PATH, SIGN_IN_LEGACY_ENTRY_PATH, } from "../shared/sign-in-journey.js";
|
|
2
3
|
let installed = false;
|
|
3
4
|
let memoryToken = null;
|
|
4
5
|
let mcpChatBridgeActive = false;
|
|
@@ -322,9 +323,12 @@ function shouldGuardAuthFailure(method, url) {
|
|
|
322
323
|
if (url.pathname === EMBED_START_PATH)
|
|
323
324
|
return false;
|
|
324
325
|
// Suffix, not equality: an app mounted under a base path serves
|
|
325
|
-
// `/<app>/
|
|
326
|
-
|
|
326
|
+
// `/<app>/sign-in` (or the legacy framework path), which an exact match
|
|
327
|
+
// would miss.
|
|
328
|
+
if (url.pathname.endsWith(SIGN_IN_ENTRY_PATH) ||
|
|
329
|
+
url.pathname.endsWith(SIGN_IN_LEGACY_ENTRY_PATH)) {
|
|
327
330
|
return false;
|
|
331
|
+
}
|
|
328
332
|
return true;
|
|
329
333
|
}
|
|
330
334
|
function activeAuthFailure(record) {
|
|
@@ -18,4 +18,5 @@ export interface NormalizedChatError {
|
|
|
18
18
|
message: string;
|
|
19
19
|
details?: string;
|
|
20
20
|
}
|
|
21
|
+
export declare function isProviderAuthenticationError(text: string, errorCode?: string): boolean;
|
|
21
22
|
export declare function normalizeChatError(errorMessage: string, errorCode?: string): NormalizedChatError;
|
|
@@ -60,7 +60,7 @@ function isProviderRateLimit(text, errorCode) {
|
|
|
60
60
|
/\b(?:http\s*)?429\b.*\b(?:status|too many requests|rate[-_\s]?limit|no body)\b/i.test(text) ||
|
|
61
61
|
/\b(?:too many requests|rate[-_\s]?limit(?:ed| exceeded)?)\b/i.test(text));
|
|
62
62
|
}
|
|
63
|
-
function isProviderAuthenticationError(text, errorCode) {
|
|
63
|
+
export function isProviderAuthenticationError(text, errorCode) {
|
|
64
64
|
const code = normalizeErrorCode(errorCode);
|
|
65
65
|
const lower = text.toLowerCase();
|
|
66
66
|
return (code === "authentication_error" ||
|
|
@@ -72,6 +72,8 @@ function isProviderAuthenticationError(text, errorCode) {
|
|
|
72
72
|
lower.includes("invalid api key") ||
|
|
73
73
|
lower.includes("incorrect api key") ||
|
|
74
74
|
lower.includes("api key is invalid") ||
|
|
75
|
+
lower.includes("rejected the saved api key") ||
|
|
76
|
+
lower.includes("saved provider key was rejected") ||
|
|
75
77
|
(lower.includes("authentication_error") && lower.includes("api")));
|
|
76
78
|
}
|
|
77
79
|
function isConnectionError(text, errorCode) {
|
|
@@ -98,6 +100,18 @@ export function normalizeChatError(errorMessage, errorCode) {
|
|
|
98
100
|
details: text,
|
|
99
101
|
};
|
|
100
102
|
}
|
|
103
|
+
// Reaches us as a bare gateway 403 with no upgradeUrl, so before this case
|
|
104
|
+
// it fell all the way through to the raw upstream sentence under a generic
|
|
105
|
+
// "The agent hit an error" headline, with no retry and no action — a dead
|
|
106
|
+
// end. It was the single largest cause of turns ending without an answer in
|
|
107
|
+
// one app, and reads to the user as the chat being broken rather than as
|
|
108
|
+
// something one person can fix in a minute.
|
|
109
|
+
if (code === "email_verification_required") {
|
|
110
|
+
return {
|
|
111
|
+
message: "AI is paused until an email address in this workspace is verified. Check the inbox for the verification link, then retry.",
|
|
112
|
+
details: text,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
101
115
|
// A model/parameter combination this provider will never accept. Retrying is
|
|
102
116
|
// pointless and the raw sentence names an API surface the reader has no way
|
|
103
117
|
// to act on, so say what they can actually change.
|
|
@@ -115,7 +129,7 @@ export function normalizeChatError(errorMessage, errorCode) {
|
|
|
115
129
|
}
|
|
116
130
|
if (isProviderAuthenticationError(text, errorCode)) {
|
|
117
131
|
return {
|
|
118
|
-
message: "The
|
|
132
|
+
message: "The saved provider key was rejected. Connect Builder.io for managed AI, or update your provider key, then retry.",
|
|
119
133
|
details: text,
|
|
120
134
|
};
|
|
121
135
|
}
|