@agent-native/core 0.146.0 → 0.146.2

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.
Files changed (120) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/turn-into-app/SKILL.md +4 -0
  3. package/corpus/templates/analytics/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  4. package/corpus/templates/analytics/actions/compose-dashboard.ts +2 -27
  5. package/corpus/templates/analytics/actions/get-sql-dashboard.ts +7 -8
  6. package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +18 -2
  7. package/corpus/templates/analytics/actions/mutate-dashboard.ts +16 -24
  8. package/corpus/templates/analytics/actions/rename-dashboard.ts +2 -24
  9. package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +2 -24
  10. package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +2 -25
  11. package/corpus/templates/analytics/actions/update-dashboard.ts +4 -32
  12. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +1 -40
  13. package/corpus/templates/analytics/changelog/2026-08-07-language-selection-now-lives-in-settings-instead-of-the-comm.md +6 -0
  14. package/corpus/templates/analytics/netlify.toml +7 -0
  15. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +13 -13
  16. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +4 -2
  17. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +0 -35
  18. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +5 -3
  19. package/corpus/templates/analytics/server/lib/dashboard-collab-sync.ts +77 -0
  20. package/corpus/templates/analytics/server/lib/demo-dashboards.ts +2 -23
  21. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +28 -74
  22. package/corpus/templates/analytics/server/routes/api/dashboard-agent-context.json.get.ts +5 -9
  23. package/corpus/templates/assets/.agents/skills/turn-into-app/SKILL.md +4 -0
  24. package/corpus/templates/assets/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  25. package/corpus/templates/brain/.agents/skills/turn-into-app/SKILL.md +4 -0
  26. package/corpus/templates/brain/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  27. package/corpus/templates/calendar/.agents/skills/turn-into-app/SKILL.md +4 -0
  28. package/corpus/templates/calendar/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  29. package/corpus/templates/chat/.agents/skills/turn-into-app/SKILL.md +4 -0
  30. package/corpus/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  31. package/corpus/templates/clips/.agents/skills/turn-into-app/SKILL.md +4 -0
  32. package/corpus/templates/clips/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  33. package/corpus/templates/clips/app/components/player/video-player.tsx +1 -1
  34. package/corpus/templates/clips/changelog/2026-08-07-fixed-trimmed-clips-durations-so-the-main-player-display-mat.md +6 -0
  35. package/corpus/templates/clips/chrome-extension/STORE_DRAFT.md +1 -1
  36. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
  37. package/corpus/templates/content/.agents/skills/turn-into-app/SKILL.md +4 -0
  38. package/corpus/templates/content/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  39. package/corpus/templates/crm/.agents/skills/turn-into-app/SKILL.md +4 -0
  40. package/corpus/templates/crm/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  41. package/corpus/templates/design/.agents/skills/turn-into-app/SKILL.md +4 -0
  42. package/corpus/templates/design/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  43. package/corpus/templates/dispatch/.agents/skills/turn-into-app/SKILL.md +4 -0
  44. package/corpus/templates/dispatch/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  45. package/corpus/templates/dispatch/changelog/2026-08-07-language-selection-now-lives-in-settings-instead-of-the-disp.md +6 -0
  46. package/corpus/templates/factory/.agents/skills/turn-into-app/SKILL.md +4 -0
  47. package/corpus/templates/factory/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  48. package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +59 -22
  49. package/corpus/templates/forms/.agents/skills/turn-into-app/SKILL.md +4 -0
  50. package/corpus/templates/forms/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  51. package/corpus/templates/macros/.agents/skills/turn-into-app/SKILL.md +4 -0
  52. package/corpus/templates/macros/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  53. package/corpus/templates/mail/.agents/skills/turn-into-app/SKILL.md +4 -0
  54. package/corpus/templates/mail/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  55. package/corpus/templates/plan/.agents/skills/turn-into-app/SKILL.md +4 -0
  56. package/corpus/templates/plan/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  57. package/corpus/templates/slides/.agents/skills/image-generation-via-a2a/SKILL.md +10 -1
  58. package/corpus/templates/slides/.agents/skills/slide-images/SKILL.md +10 -3
  59. package/corpus/templates/slides/.agents/skills/turn-into-app/SKILL.md +4 -0
  60. package/corpus/templates/slides/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  61. package/corpus/templates/slides/actions/generate-image-api.ts +123 -0
  62. package/corpus/templates/slides/actions/import-file.ts +144 -99
  63. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +10 -2
  64. package/corpus/templates/slides/app/components/editor/ImageGenPanel.tsx +1 -1
  65. package/corpus/templates/slides/changelog/2026-08-07-deleting-a-deck-no-longer-freezes-the-slides-browser.md +6 -0
  66. package/corpus/templates/slides/changelog/2026-08-07-slides-verify-generated-images-are-persisted-on-the-target-s.md +6 -0
  67. package/corpus/templates/slides/package.json +1 -0
  68. package/corpus/templates/slides/server/handlers/import/html-converter.ts +9 -0
  69. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +960 -0
  70. package/corpus/templates/slides/server/lib/slide-image-insertion.ts +141 -0
  71. package/corpus/templates/slides/server/plugins/agent-chat.ts +2 -0
  72. package/corpus/templates/tasks/.agents/skills/turn-into-app/SKILL.md +4 -0
  73. package/corpus/templates/tasks/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  74. package/dist/client/AgentPanel.d.ts +10 -4
  75. package/dist/client/AgentPanel.js +29 -82
  76. package/dist/collab/awareness.d.ts +2 -2
  77. package/dist/collab/struct-routes.d.ts +1 -1
  78. package/dist/db/migrations.js +22 -4
  79. package/dist/deploy/build.d.ts +1 -0
  80. package/dist/deploy/build.js +27 -22
  81. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  82. package/dist/ingestion/pptx.d.ts +1 -0
  83. package/dist/ingestion/pptx.js +353 -28
  84. package/dist/notifications/routes.d.ts +2 -2
  85. package/dist/observability/routes.d.ts +1 -1
  86. package/dist/progress/routes.d.ts +1 -1
  87. package/dist/resources/handlers.d.ts +1 -1
  88. package/dist/secrets/routes.d.ts +6 -6
  89. package/dist/server/builder-browser.d.ts +34 -0
  90. package/dist/server/builder-browser.js +179 -0
  91. package/dist/server/core-routes-plugin.d.ts +5 -0
  92. package/dist/server/core-routes-plugin.js +24 -8
  93. package/dist/server/index.d.ts +1 -1
  94. package/dist/server/index.js +1 -1
  95. package/dist/shared/cache-control.d.ts +2 -2
  96. package/dist/shared/cache-control.js +2 -2
  97. package/dist/templates/chat/.agents/skills/turn-into-app/SKILL.md +4 -0
  98. package/dist/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  99. package/dist/templates/default/.agents/skills/turn-into-app/SKILL.md +4 -0
  100. package/dist/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  101. package/dist/templates/headless/.agents/skills/turn-into-app/SKILL.md +4 -0
  102. package/dist/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  103. package/dist/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +4 -0
  104. package/dist/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  105. package/dist/vite/client.js +13 -1
  106. package/docs/content/drop-in-agent.mdx +3 -3
  107. package/docs/content/getting-started-actions.mdx +1 -2
  108. package/docs/content/getting-started-pages.mdx +1 -2
  109. package/docs/content/getting-started.mdx +0 -4
  110. package/docs/content/what-is-agent-native.mdx +4 -4
  111. package/package.json +3 -3
  112. package/src/templates/chat/.agents/skills/turn-into-app/SKILL.md +4 -0
  113. package/src/templates/chat/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  114. package/src/templates/default/.agents/skills/turn-into-app/SKILL.md +4 -0
  115. package/src/templates/default/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  116. package/src/templates/headless/.agents/skills/turn-into-app/SKILL.md +4 -0
  117. package/src/templates/headless/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  118. package/src/templates/workspace-core/.agents/skills/turn-into-app/SKILL.md +4 -0
  119. package/src/templates/workspace-core/.agents/skills/turn-into-app/references/fresh-project.md +8 -0
  120. package/docs/content/getting-started-database.mdx +0 -173
@@ -0,0 +1,141 @@
1
+ interface InsertSlideImageOptions {
2
+ alt?: string;
3
+ }
4
+
5
+ function cleanAlt(value: string | undefined): string {
6
+ return (value || "Uploaded image").replace(/\s+/g, " ").trim();
7
+ }
8
+
9
+ function escapeAttribute(value: string): string {
10
+ return value
11
+ .replace(/&/g, "&")
12
+ .replace(/"/g, """)
13
+ .replace(/</g, "&lt;")
14
+ .replace(/>/g, "&gt;");
15
+ }
16
+
17
+ function decodeHtml(value: string): string {
18
+ return value
19
+ .replace(/&quot;/gi, '"')
20
+ .replace(/&#39;/gi, "'")
21
+ .replace(/&lt;/gi, "<")
22
+ .replace(/&gt;/gi, ">")
23
+ .replace(/&amp;/gi, "&");
24
+ }
25
+
26
+ function textContent(html: string): string {
27
+ return decodeHtml(html.replace(/<[^>]+>/g, " "))
28
+ .replace(/\s+/g, " ")
29
+ .trim();
30
+ }
31
+
32
+ function attribute(attributes: string, name: string): string | null {
33
+ const match = attributes.match(
34
+ new RegExp(`\\b${name}\\s*=\\s*(["'])([\\s\\S]*?)\\1`, "i"),
35
+ );
36
+ return match ? match[2] : null;
37
+ }
38
+
39
+ function hasStyleProperty(style: string, property: string): boolean {
40
+ return new RegExp(`(?:^|;)\\s*${property}\\s*:`, "i").test(style);
41
+ }
42
+
43
+ function appendImageStyle(baseStyle: string): string {
44
+ const declarations = [baseStyle.trim().replace(/;+\s*$/, "")].filter(Boolean);
45
+ if (!hasStyleProperty(baseStyle, "display"))
46
+ declarations.push("display: block");
47
+ if (!hasStyleProperty(baseStyle, "object-fit")) {
48
+ declarations.push("object-fit: cover");
49
+ }
50
+ if (!hasStyleProperty(baseStyle, "min-width"))
51
+ declarations.push("min-width: 0");
52
+ return declarations.length > 0 ? `${declarations.join("; ")};` : "";
53
+ }
54
+
55
+ function imageHtml(src: string, alt: string, style: string): string {
56
+ return `<img src="${escapeAttribute(src)}" alt="${escapeAttribute(alt)}" class="fmd-img-uploaded"${style ? ` style="${escapeAttribute(style)}"` : ""}>`;
57
+ }
58
+
59
+ /**
60
+ * Server-safe counterpart to the editor's image replacement helper. It uses
61
+ * deterministic string transforms because DOMParser is not available in the
62
+ * server runtime that executes actions.
63
+ */
64
+ export function insertImageIntoSlideHtml(
65
+ content: string,
66
+ src: string,
67
+ options: InsertSlideImageOptions = {},
68
+ ): string {
69
+ const openingTags = /<([a-z][\w:-]*)([^>]*)>/gi;
70
+ for (const match of content.matchAll(openingTags)) {
71
+ const className = attribute(match[2], "class") || "";
72
+ if (!/(?:^|\s)fmd-img-placeholder(?:\s|$)/.test(className)) continue;
73
+
74
+ const closingTag = new RegExp(`</${match[1]}\\s*>`, "gi");
75
+ closingTag.lastIndex = (match.index ?? 0) + match[0].length;
76
+ const closing = closingTag.exec(content);
77
+ if (!closing || match.index === undefined) continue;
78
+
79
+ const style = appendImageStyle(
80
+ attribute(match[2], "style") ||
81
+ "width: 100%; height: 100%; border-radius: 8px; object-fit: cover;",
82
+ );
83
+ const replacement = imageHtml(
84
+ src,
85
+ cleanAlt(
86
+ options.alt ||
87
+ textContent(
88
+ content.slice(match.index + match[0].length, closing.index),
89
+ ) ||
90
+ "Uploaded image",
91
+ ),
92
+ style,
93
+ );
94
+ return `${content.slice(0, match.index)}${replacement}${content.slice(closing.index + closing[0].length)}`;
95
+ }
96
+
97
+ const fullBleed = imageHtml(
98
+ src,
99
+ cleanAlt(options.alt),
100
+ "position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: -1;",
101
+ );
102
+ const slideRoot =
103
+ /<([a-z][\w:-]*)([^>]*\bclass\s*=\s*(["'])[^"']*\bfmd-slide\b[^"']*\3[^>]*)>/i.exec(
104
+ content,
105
+ );
106
+ if (!slideRoot || slideRoot.index === undefined)
107
+ return `${fullBleed}${content}`;
108
+
109
+ let openingTag = slideRoot[0];
110
+ const style = attribute(slideRoot[2], "style") || "";
111
+ if (!hasStyleProperty(style, "position")) {
112
+ const nextStyle =
113
+ `${style.trim().replace(/;+\s*$/, "")}; position: relative;`.replace(
114
+ /^;\s*/,
115
+ "",
116
+ );
117
+ if (/\bstyle\s*=\s*(["'])/i.test(openingTag)) {
118
+ openingTag = openingTag.replace(
119
+ /\bstyle\s*=\s*(["'])[^"']*\1/i,
120
+ `style="${escapeAttribute(nextStyle)}"`,
121
+ );
122
+ } else {
123
+ openingTag = openingTag.replace(
124
+ />$/,
125
+ ` style="${escapeAttribute(nextStyle)}">`,
126
+ );
127
+ }
128
+ }
129
+ return `${content.slice(0, slideRoot.index)}${openingTag}${fullBleed}${content.slice(slideRoot.index + slideRoot[0].length)}`;
130
+ }
131
+
132
+ export function slideHtmlContainsImageSource(
133
+ html: string,
134
+ src: string,
135
+ ): boolean {
136
+ const images = /<img\b([^>]*)>/gi;
137
+ for (const match of html.matchAll(images)) {
138
+ if (decodeHtml(attribute(match[1], "src") || "") === src) return true;
139
+ }
140
+ return false;
141
+ }
@@ -62,6 +62,8 @@ Layout-fit workflow is strict. When the user asks to fix overflow, first call vi
62
62
 
63
63
  Fit means the main content fits the native content area. A small outer-wrapper spill is tolerated by the measurement, but cards, text, columns, and other visible content must fit. Never use zoom, transform: scale(), overflow: hidden/scroll, clipping, or a smaller-than-16px body font to hide overflow. Preserve manually positioned freeform objects and their data-slide-object-id values; repair normal-flow structure, copy, gaps, or slide padding instead. A successful action result must include the affected slide IDs; if it does not, report that no verified write occurred.
64
64
 
65
+ Image workflow is strict. For direct insertion, call generate-image-api with insertIntoSlide: true plus deckId and slideId. Claim that an image was added only when that action returns inserted: true; a preview URL or completed generation alone is not a slide edit. For preview-only variations, call generate-image-api without insertIntoSlide, then use update-slide to place the chosen URL and re-read the target with get-deck to confirm its persisted HTML contains that image source before claiming success.
66
+
65
67
  Provider-specific Slides actions are shortcuts, not limits. If a first-class action cannot express the exact Google Drive endpoint, file metadata field, export format, query, request body, pagination mode, payload shape, or API version needed, call provider-api-catalog and provider-api-docs as needed, then call provider-api-request against the real provider API. Use the raw provider API escape hatch instead of weakening the answer or claiming Slides cannot do something the underlying Google Drive API can do.
66
68
 
67
69
  Slides' Google Drive provider API uses the user's connected Google Docs OAuth account. The drive.file scope is intentionally limited to files the user selected or the app created. For large Drive file lists or metadata sweeps, pass stageAs and pagination options to provider-api-request, then use query-staged-dataset to count, filter, group, or project the staged rows.
@@ -27,6 +27,10 @@ The build location depends on the host. Treat this as a hard routing rule:
27
27
  `resourceIds` when available. This is the Builder handoff.
28
28
  - Do not substitute the generic `create_workspace_app` MCP tool in an online
29
29
  host. That tool is a local workspace scaffolder, not the Builder handoff.
30
+ - Connect the Agent-Native Dispatch MCP connector only. Dispatch uses the
31
+ authenticated Builder Projects API to reuse or provision the workspace
32
+ project before starting the Builder Cloud Agent; a separate Builder CMS MCP
33
+ connection is not required for this workflow.
30
34
  - If `start-workspace-app-creation` is not available or Dispatch is not
31
35
  authenticated, stop with the connector setup needed. Do not fall back to a
32
36
  local build or claim that the app exists.
@@ -50,6 +50,14 @@ skills. For a ChatGPT Project or any host that exposes only the MCP connector,
50
50
  also place the skill instructions or this reference in the Project
51
51
  instructions/knowledge files. Then add and authenticate the Dispatch MCP
52
52
  connector from the packaged `adapters/chatgpt-mcp/connector.json`.
53
+ This single connector is enough: Dispatch provisions or reuses the Builder
54
+ project through the Builder Projects API, so do not ask the user to add a
55
+ separate Builder CMS MCP for app creation.
56
+ Builder's separate Fusion MCP is available as a custom remote connector at
57
+ `https://mcp.builder.io/mcp/fusion` for direct Builder work. It can run an
58
+ existing project, but it cannot provision the repo-backed Agent-Native
59
+ workspace project, so it is optional and not a replacement for the Dispatch
60
+ handoff in this skill. Do not add Builder's CMS MCP for this workflow.
53
61
 
54
62
  For Claude Web, ChatGPT Web, and their web Projects, the host is an
55
63
  orchestrator, not the build environment. After forming the source brief, call
@@ -38,7 +38,13 @@ export declare function shouldShowAgentPanelSidebarChatTabs(tabs: MultiTabAssist
38
38
  export declare function shouldShowAgentPanelPageNewChatButton(tabs: MultiTabAssistantChatHeaderProps["tabs"], activeTabId: string, activeTabMessageCount: number): boolean;
39
39
  export declare function shouldShowAgentPanelCliTabBar(cliTabs: string[]): boolean;
40
40
  export declare function shouldShowAgentPanelModeButtons(isSidebar: boolean): boolean;
41
- export declare function shouldShowAgentPanelFullViewAction(agentPageHref: string | undefined, mode: PanelMode, isSidebar?: boolean): boolean;
41
+ export declare function shouldShowAgentPanelFullViewAction(agentPageHref: string | undefined, mode: PanelMode, isSidebar?: boolean, currentPath?: string): boolean;
42
+ export declare function getAgentPanelShortcutHints(isMac: boolean): {
43
+ closeTab: string;
44
+ closeAllTabs: string;
45
+ toggleSidebar: string;
46
+ widenChat: string;
47
+ };
42
48
  export interface AgentPanelCodeAccess {
43
49
  /** Whether this surface can safely edit source and run shell commands. */
44
50
  enabled: boolean;
@@ -68,7 +74,7 @@ export interface AgentPanelProps extends Omit<AssistantChatProps, "onSwitchToCli
68
74
  onCollapse?: () => void;
69
75
  /** Whether the panel is currently in fullscreen (Claude-style centered) mode. */
70
76
  isFullscreen?: boolean;
71
- /** Called when the user clicks the maximize/minimize button. If provided, the button appears next to the collapse button. */
77
+ /** @deprecated Fullscreen sidebar controls are no longer rendered. */
72
78
  onToggleFullscreen?: () => void;
73
79
  /** Called when the user asks the sidebar to use the wide chat width preset. */
74
80
  onSnapTo75Percent?: () => void;
@@ -180,7 +186,7 @@ export interface AgentSidebarProps {
180
186
  storageKey?: string;
181
187
  /** Open the sidebar when a chat run is active or reconnects. */
182
188
  openOnChatRunning?: boolean;
183
- /** Override the fullscreen menu action, for templates with a chat-first page. */
189
+ /** @deprecated Fullscreen sidebar actions are no longer rendered. */
184
190
  onFullscreenRequest?: () => void;
185
191
  /** Ambient resource context rendered as a composer chip. */
186
192
  scope?: import("./use-chat-threads.js").ChatThreadScope | null;
@@ -199,7 +205,7 @@ export interface AgentSidebarProps {
199
205
  * Wraps app content with a toggleable agent sidebar.
200
206
  * Use AgentToggleButton in your header to open/close it.
201
207
  */
202
- export declare function AgentSidebar({ children, emptyStateText, suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position, defaultOpen, animateMobile, animateDesktop, chatViewTransition, chatViewTransitionHandoff, storageKey, openOnChatRunning, onFullscreenRequest, scope, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding, }: AgentSidebarProps): React.JSX.Element;
208
+ export declare function AgentSidebar({ children, emptyStateText, suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position, defaultOpen, animateMobile, animateDesktop, chatViewTransition, chatViewTransitionHandoff, storageKey, openOnChatRunning, scope, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding, }: AgentSidebarProps): React.JSX.Element;
203
209
  /**
204
210
  * Focus the agent chat composer input.
205
211
  * Opens the sidebar if closed, then focuses the text input.
@@ -22,7 +22,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
22
22
  * <AgentChatSurface mode="page" className="h-screen" />
23
23
  */
24
24
  import { Tooltip as DesignSystemTooltip } from "@agent-native/toolkit/design-system";
25
- import { IconMessageCircle, IconMessageDots, IconTerminal2, IconSettings, IconLayoutSidebarRightCollapse, IconLayoutGrid, IconCheck, IconPlus, IconX, IconDotsVertical, IconHistory, IconArrowsHorizontal, IconArrowsMaximize, IconArrowsMinimize, IconExternalLink, } from "@tabler/icons-react";
25
+ import { IconMessageCircle, IconMessageDots, IconTerminal2, IconSettings, IconLayoutSidebarRightCollapse, IconLayoutGrid, IconCheck, IconPlus, IconX, IconDotsVertical, IconHistory, IconArrowsHorizontal, IconArrowsMaximize, IconExternalLink, } from "@tabler/icons-react";
26
26
  import React, { useState, useEffect, useRef, useCallback, useMemo, lazy, Suspense, startTransition, } from "react";
27
27
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger, } from "./components/ui/dropdown-menu.js";
28
28
  import { normalizeTooltipText } from "./components/ui/tooltip.js";
@@ -240,10 +240,19 @@ export function shouldShowAgentPanelCliTabBar(cliTabs) {
240
240
  export function shouldShowAgentPanelModeButtons(isSidebar) {
241
241
  return !isSidebar;
242
242
  }
243
- export function shouldShowAgentPanelFullViewAction(agentPageHref, mode, isSidebar = false) {
243
+ export function shouldShowAgentPanelFullViewAction(agentPageHref, mode, isSidebar = false, currentPath) {
244
244
  return (Boolean(agentPageHref) &&
245
+ currentPath !== agentPageHref &&
245
246
  (isSidebar || mode === "resources" || mode === "settings"));
246
247
  }
248
+ export function getAgentPanelShortcutHints(isMac) {
249
+ return {
250
+ closeTab: isMac ? "⌃W" : "⌥W",
251
+ closeAllTabs: isMac ? "⌃⌥W" : "^⌥W",
252
+ toggleSidebar: isMac ? "⌘\\" : "^\\",
253
+ widenChat: isMac ? "⌘⇧\\" : "^⇧\\",
254
+ };
255
+ }
247
256
  function useBuilderConnectUrl() {
248
257
  const [connectUrl, setConnectUrl] = useState(null);
249
258
  const [configured, setConfigured] = useState(false);
@@ -391,6 +400,7 @@ function CodeAccessUnavailablePanel({ title, description, ctaLabel, ctaHref, sec
391
400
  function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, emptyStateText, emptyStateAddon, suggestions, dynamicSuggestions, showHeader = true, onCollapse, isFullscreen, onToggleFullscreen, onSnapTo75Percent, isWideDrawer, onExitWideDrawer, devAppUrl, storageKey, restoreActiveThread = true, scope, showScopeBadge, browserTabId, threadUrlSync, chatNotice, showTabBar = true, showPageNewChatButton = false, allowSettingsMode = true, chatOnly = false, agentPageHref, codeAccess, ...assistantChatProps }) {
392
401
  const t = useT();
393
402
  const navigate = useNavigate();
403
+ const location = useLocation();
394
404
  const mounted = useClientOnly();
395
405
  const onboardingPreviewMode = useOnboardingPreviewMode();
396
406
  const showFirstRunOnboarding = SHOW_FIRST_RUN_ONBOARDING || onboardingPreviewMode;
@@ -402,11 +412,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
402
412
  const panelModeKey = `agent-native-panel-mode${keyPrefix}`;
403
413
  const isMac = useMemo(() => typeof navigator !== "undefined" &&
404
414
  /Mac|iPhone|iPad/.test(navigator.userAgent), []);
405
- const closeTabHint = isMac ? "\u2303W" : "Alt+W";
406
- const closeAllTabsHint = isMac ? "\u2303\u2325W" : "Ctrl+Alt+W";
407
- const toggleSidebarHint = isMac ? "\u2318\\" : "Ctrl+\\";
408
- const widenChatHint = isMac ? "\u2318\u21e7\\" : "Ctrl+Shift+\\";
409
- const fullscreenHint = isMac ? "\u2318\u21e7F" : "Ctrl+Shift+F";
415
+ const { closeTab: closeTabHint, closeAllTabs: closeAllTabsHint, toggleSidebar: toggleSidebarHint, widenChat: widenChatHint, } = getAgentPanelShortcutHints(isMac);
410
416
  const [execMode, setExecMode] = useState(() => {
411
417
  try {
412
418
  const saved = localStorage.getItem(execModeKey);
@@ -651,7 +657,7 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
651
657
  const wideDrawerAction = isWideDrawer ? onExitWideDrawer : onSnapTo75Percent;
652
658
  const wideDrawerLabel = t(isWideDrawer ? "agentPanel.returnChatToLayout" : "agentPanel.widenChat");
653
659
  const showFullViewAction = Boolean(agentPageHref &&
654
- shouldShowAgentPanelFullViewAction(agentPageHref, mode, chatOnly));
660
+ shouldShowAgentPanelFullViewAction(agentPageHref, mode, chatOnly, location.pathname));
655
661
  useEffect(() => {
656
662
  const handleKeyDown = (event) => {
657
663
  if (!(event.metaKey || event.ctrlKey) ||
@@ -667,14 +673,10 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
667
673
  wideDrawerAction();
668
674
  return;
669
675
  }
670
- if (event.code === "KeyF" && onToggleFullscreen) {
671
- event.preventDefault();
672
- onToggleFullscreen();
673
- }
674
676
  };
675
677
  document.addEventListener("keydown", handleKeyDown);
676
678
  return () => document.removeEventListener("keydown", handleKeyDown);
677
- }, [mode, onCollapse, onToggleFullscreen, wideDrawerAction]);
679
+ }, [mode, onCollapse, wideDrawerAction]);
678
680
  const renderHeaderActions = useCallback(({ activeChatSessionId, activeTabId, activeTabMessageCount, addTab, clearActiveTab, closeAllTabs, closeOtherTabs, closeTab, showHistory, tabs, toggleHistory, }) => (_jsxs("div", { className: "relative flex shrink-0 items-center gap-0.5", children: [!onCollapse && SHOW_ONBOARDING && (_jsx(Suspense, { fallback: null, children: _jsx(SetupButton, {}) })), !onCollapse &&
679
681
  (() => {
680
682
  const activeTab = mode === "chat" && activeChatSessionId
@@ -686,11 +688,8 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
686
688
  }
687
689
  return (_jsx(ShareButton, { resourceType: "chat_thread", resourceId: activeTab.id, resourceTitle: activeTab.label || "Chat", shareUrl: getChatThreadShareUrl(activeTab.id), trigger: "icon", triggerClassName: "h-7 w-7" }));
688
690
  })(), feedbackEnabled ? (_jsx(FeedbackButton, { variant: "icon", side: "bottom", align: "end", chatSessionId: activeChatSessionId, chatStorageKey: storageKey, open: feedbackOpen, onOpenChange: setFeedbackOpen, trigger: _jsx("button", { type: "button", tabIndex: -1, "aria-hidden": "true", className: "pointer-events-none absolute end-0 top-full h-px w-px opacity-0" }) })) : null, mode === "chat" && (_jsx(IconTooltip, { content: t("agentPanel.newChat"), children: _jsx("button", { onClick: addTab, "aria-label": t("agentPanel.newChat"), className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", children: _jsx(IconPlus, { size: 14 }) }) })), !onCollapse && mode === "cli" && canUseCodeTools && (_jsx(IconTooltip, { content: t("agentPanel.newTerminal"), children: _jsx("button", { onClick: addCliTab, "aria-label": t("agentPanel.newTerminal"), className: "flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", children: _jsx(IconPlus, { size: 14 }) }) })), _jsxs(DropdownMenu, { open: headerMenuOpen, onOpenChange: setHeaderMenuOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: cn("flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-accent/50", (headerMenuOpen || mode === "settings") &&
689
- "bg-accent text-foreground"), "aria-label": t("agentPanel.panelOptions"), children: _jsx(IconDotsVertical, { size: 14 }) }) }), _jsxs(DropdownMenuContent, { align: "end", sideOffset: 6, className: "w-48", children: [onCollapse && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: onCollapse, children: [_jsx(IconLayoutSidebarRightCollapse, { size: 14, className: "shrink-0" }), t("agentPanel.collapseSidebar"), _jsx(DropdownMenuShortcut, { children: toggleSidebarHint })] }), _jsx(DropdownMenuSeparator, {})] })), onCollapse && mode === "chat" && wideDrawerAction ? (_jsxs(DropdownMenuItem, { onSelect: wideDrawerAction, children: [_jsx(IconArrowsHorizontal, { size: 14, className: "shrink-0" }), wideDrawerLabel, _jsx(DropdownMenuShortcut, { children: widenChatHint })] })) : null, showFullViewAction && agentPageHref ? (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs(Link, { to: agentPageHref, "aria-label": t("agentPanel.openFullView"), children: [_jsx(IconArrowsMaximize, { size: 14, className: "shrink-0" }), t("agentPanel.openFullView")] }) })) : null, onToggleFullscreen && (_jsxs(DropdownMenuItem, { onSelect: onToggleFullscreen, children: [isFullscreen ? (_jsx(IconArrowsMinimize, { size: 14, className: "shrink-0" })) : (_jsx(IconArrowsMaximize, { size: 14, className: "shrink-0" })), isFullscreen
690
- ? t("agentPanel.exitFullscreen")
691
- : t("agentPanel.fullscreen"), _jsx(DropdownMenuShortcut, { children: fullscreenHint })] })), (onCollapse && mode === "chat" && wideDrawerAction) ||
692
- showFullViewAction ||
693
- onToggleFullscreen ? (_jsx(DropdownMenuSeparator, {})) : null, onCollapse && mode === "chat" && (_jsxs(DropdownMenuItem, { onSelect: addTab, children: [_jsx(IconPlus, { size: 14, className: "shrink-0" }), t("agentPanel.newChat")] })), mode === "chat" && toggleHistory && (_jsxs(DropdownMenuItem, { onSelect: toggleHistory, children: [_jsx(IconHistory, { size: 14, className: "shrink-0" }), showHistory
691
+ "bg-accent text-foreground"), "aria-label": t("agentPanel.panelOptions"), children: _jsx(IconDotsVertical, { size: 14 }) }) }), _jsxs(DropdownMenuContent, { align: "end", sideOffset: 6, className: "w-48", children: [onCollapse && (_jsxs(_Fragment, { children: [_jsxs(DropdownMenuItem, { onSelect: onCollapse, children: [_jsx(IconLayoutSidebarRightCollapse, { size: 14, className: "shrink-0" }), t("agentPanel.collapseSidebar"), _jsx(DropdownMenuShortcut, { children: toggleSidebarHint })] }), _jsx(DropdownMenuSeparator, {})] })), onCollapse && mode === "chat" && wideDrawerAction ? (_jsxs(DropdownMenuItem, { onSelect: wideDrawerAction, children: [_jsx(IconArrowsHorizontal, { size: 14, className: "shrink-0" }), wideDrawerLabel, _jsx(DropdownMenuShortcut, { children: widenChatHint })] })) : null, showFullViewAction && agentPageHref ? (_jsx(DropdownMenuItem, { asChild: true, children: _jsxs(Link, { to: agentPageHref, "aria-label": t("agentPanel.openFullView"), children: [_jsx(IconArrowsMaximize, { size: 14, className: "shrink-0" }), t("agentPanel.openFullView")] }) })) : null, (onCollapse && mode === "chat" && wideDrawerAction) ||
692
+ showFullViewAction ? (_jsx(DropdownMenuSeparator, {})) : null, onCollapse && mode === "chat" && (_jsxs(DropdownMenuItem, { onSelect: addTab, children: [_jsx(IconPlus, { size: 14, className: "shrink-0" }), t("agentPanel.newChat")] })), mode === "chat" && toggleHistory && (_jsxs(DropdownMenuItem, { onSelect: toggleHistory, children: [_jsx(IconHistory, { size: 14, className: "shrink-0" }), showHistory
694
693
  ? t("agentPanel.hideChats")
695
694
  : t("agentPanel.allChats")] })), mode === "chat" && (_jsx(RunsTrayMenuItem, { pollMs: 0, limit: 12, showRecent: true, onOpenThread: openRunThread })), mode === "chat" && _jsx(DropdownMenuSeparator, {}), mode === "cli" && availableClis.length > 0 && (_jsxs(_Fragment, { children: [availableClis.map((cli) => (_jsxs(DropdownMenuItem, { onSelect: () => selectCli(cli.command), className: cn(cli.command === selectedCli
696
695
  ? "font-medium"
@@ -715,14 +714,12 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
715
714
  feedbackEnabled,
716
715
  getChatThreadShareUrl,
717
716
  headerMenuOpen,
718
- isFullscreen,
719
717
  isWideDrawer,
720
718
  mode,
721
719
  agentPageHref,
722
720
  onCollapse,
723
721
  onExitWideDrawer,
724
722
  onSnapTo75Percent,
725
- onToggleFullscreen,
726
723
  openRunThread,
727
724
  selectCli,
728
725
  selectedCli,
@@ -732,7 +729,6 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
732
729
  wideDrawerAction,
733
730
  wideDrawerLabel,
734
731
  widenChatHint,
735
- fullscreenHint,
736
732
  showFullViewAction,
737
733
  ]);
738
734
  const renderPageChatOverlay = useCallback(({ activeTabId, activeTabMessageCount, addTab, tabs, }) => {
@@ -939,13 +935,11 @@ function AgentPanelInner({ defaultMode = "chat", className, style, apiUrl, empty
939
935
  }
940
936
  // ─── Resize handle ──────────────────────────────────────────────────────────
941
937
  const SIDEBAR_STORAGE_KEY = "agent-native-sidebar-width";
942
- const SIDEBAR_FULLSCREEN_KEY = "agent-native-sidebar-fullscreen";
943
938
  const SIDEBAR_DRAWER_KEY = "agent-native-sidebar-wide-drawer";
944
939
  const SIDEBAR_DRAWER_PLACEHOLDER_KEY = "agent-native-sidebar-drawer-placeholder-width";
945
940
  const SIDEBAR_ANIMATION_MS = 260;
946
941
  const SIDEBAR_OVERLAY_Z_INDEX = 70;
947
942
  const SIDEBAR_DRAWER_Z_INDEX = 80;
948
- const SIDEBAR_FULLSCREEN_Z_INDEX = 90;
949
943
  const SIDEBAR_DRAWER_VIEW_TRANSITION_NAME = "agent-native-sidebar-drawer";
950
944
  /** Shared max width of the centered fullscreen chat column and composer. */
951
945
  const FULLSCREEN_CHAT_COLUMN_MAX_PX = 684;
@@ -1365,7 +1359,7 @@ export function AgentChatSurface({ mode = "panel", className, defaultMode = "cha
1365
1359
  * Wraps app content with a toggleable agent sidebar.
1366
1360
  * Use AgentToggleButton in your header to open/close it.
1367
1361
  */
1368
- export function AgentSidebar({ children, emptyStateText = "How can I help you?", suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position = "right", defaultOpen = false, animateMobile = true, animateDesktop = true, chatViewTransition = false, chatViewTransitionHandoff = false, storageKey, openOnChatRunning = false, onFullscreenRequest, scope, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding = false, }) {
1362
+ export function AgentSidebar({ children, emptyStateText = "How can I help you?", suggestions, dynamicSuggestions, composerToolbarSlot, composerSlot, onComposerTextChange, imageModelMenu, threadFooterSlot, defaultSidebarWidth, sidebarWidth, position = "right", defaultOpen = false, animateMobile = true, animateDesktop = true, chatViewTransition = false, chatViewTransitionHandoff = false, storageKey, openOnChatRunning = false, scope, showScopeBadge, browserTabId, threadUrlSync, agentPageHref, suppressFirstRunOnboarding = false, }) {
1369
1363
  const onboardingPreviewMode = useOnboardingPreviewMode();
1370
1364
  const showFirstRunOnboarding = !suppressFirstRunOnboarding &&
1371
1365
  (SHOW_FIRST_RUN_ONBOARDING || onboardingPreviewMode);
@@ -1399,20 +1393,6 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1399
1393
  });
1400
1394
  const drawerExitTimerRef = useRef(null);
1401
1395
  const [isResizing, setIsResizing] = useState(false);
1402
- const [fullscreen, setFullscreen] = useState(() => {
1403
- // Force-disable on mobile: a Claude-style centered column makes no sense
1404
- // when the sidebar already covers most of the viewport.
1405
- if (typeof window !== "undefined" &&
1406
- window.matchMedia("(max-width: 767px)").matches) {
1407
- return false;
1408
- }
1409
- try {
1410
- return localStorage.getItem(SIDEBAR_FULLSCREEN_KEY) === "true";
1411
- }
1412
- catch {
1413
- return false;
1414
- }
1415
- });
1416
1396
  // Track mobile viewport so we can switch to overlay mode.
1417
1397
  const [isMobile, setIsMobile] = useState(() => typeof window !== "undefined" &&
1418
1398
  window.matchMedia("(max-width: 767px)").matches);
@@ -1458,16 +1438,6 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1458
1438
  if (openOnChatRunning)
1459
1439
  setOpen(true);
1460
1440
  }, [openOnChatRunning]);
1461
- const toggleFullscreen = useCallback(() => {
1462
- setFullscreen((prev) => {
1463
- const next = !prev;
1464
- try {
1465
- localStorage.setItem(SIDEBAR_FULLSCREEN_KEY, String(next));
1466
- }
1467
- catch { }
1468
- return next;
1469
- });
1470
- }, []);
1471
1441
  // Track whether the frame is controlling the sidebar (code mode = frame active).
1472
1442
  // Default to true when inside an iframe — assume the frame sidebar is active
1473
1443
  // until told otherwise. This prevents both sidebars flashing after hot reloads.
@@ -1769,13 +1739,9 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1769
1739
  const handleResizeStart = useCallback(() => setIsResizing(true), []);
1770
1740
  const handleResizeEnd = useCallback(() => setIsResizing(false), []);
1771
1741
  const isLeft = position === "left";
1772
- // Fullscreen only applies on desktop — on mobile the existing overlay is
1773
- // already viewport-covering, so the maximize button is hidden and the
1774
- // mounted state ignores any persisted value.
1775
- const effectiveFullscreen = !onFullscreenRequest && fullscreen && !isMobile;
1776
- const wideDrawerEnabled = isWideDrawer && !isMobile && !effectiveFullscreen;
1777
- const mobileAnimationEnabled = !presentationMode && !effectiveFullscreen && isMobile && animateMobile;
1778
- const desktopAnimationEnabled = !presentationMode && !effectiveFullscreen && !isMobile && animateDesktop;
1742
+ const wideDrawerEnabled = isWideDrawer && !isMobile;
1743
+ const mobileAnimationEnabled = !presentationMode && isMobile && animateMobile;
1744
+ const desktopAnimationEnabled = !presentationMode && !isMobile && animateDesktop;
1779
1745
  const sidebarAnimationEnabled = mobileAnimationEnabled || desktopAnimationEnabled;
1780
1746
  const [renderAnimatedPanel, setRenderAnimatedPanel] = useState(shouldMountPanel);
1781
1747
  useEffect(() => {
@@ -1804,17 +1770,15 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1804
1770
  const panelOpen = open && shouldMountPanel;
1805
1771
  const panelLayout = isMobile
1806
1772
  ? "mobile"
1807
- : effectiveFullscreen
1808
- ? "fullscreen"
1809
- : wideDrawerEnabled
1810
- ? "drawer"
1811
- : "desktop";
1773
+ : wideDrawerEnabled
1774
+ ? "drawer"
1775
+ : "desktop";
1812
1776
  // On desktop the resize handle is also the visual divider. Avoid painting a
1813
1777
  // second panel border next to it.
1814
- const showResizeHandle = !isMobile && !effectiveFullscreen && !wideDrawerEnabled && panelOpen;
1778
+ const showResizeHandle = !isMobile && !wideDrawerEnabled && panelOpen;
1815
1779
  // On mobile the sidebar floats as a fixed overlay so the content below isn't
1816
- // squashed. On desktop it participates in the flex layout as before, except
1817
- // in fullscreen mode where it overlays the entire viewport (Claude-style).
1780
+ // squashed. On desktop it participates in the flex layout or becomes a
1781
+ // fixed-width drawer when the user asks for more room.
1818
1782
  let panelStyle;
1819
1783
  if (isMobile) {
1820
1784
  panelStyle = {
@@ -1836,18 +1800,6 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1836
1800
  pointerEvents: mobileAnimationEnabled && !panelOpen ? "none" : undefined,
1837
1801
  };
1838
1802
  }
1839
- else if (effectiveFullscreen) {
1840
- panelStyle = {
1841
- ...AGENT_PANEL_ROOT_STYLE,
1842
- position: "fixed",
1843
- inset: 0,
1844
- width: "100%",
1845
- maxHeight: "var(--agent-native-viewport-height, 100vh)",
1846
- zIndex: SIDEBAR_FULLSCREEN_Z_INDEX,
1847
- background: "hsl(var(--background))",
1848
- display: open ? "flex" : "none",
1849
- };
1850
- }
1851
1803
  else if (wideDrawerEnabled) {
1852
1804
  panelStyle = {
1853
1805
  ...AGENT_PANEL_ROOT_STYLE,
@@ -1898,16 +1850,11 @@ export function AgentSidebar({ children, emptyStateText = "How can I help you?",
1898
1850
  ? "desktop"
1899
1851
  : undefined, "data-agent-sidebar-layout": panelLayout, "data-agent-sidebar-position": position, "data-agent-sidebar-state": panelOpen ? "open" : "closed", "data-agent-sidebar-resizing": isResizing ? "true" : undefined, "data-agent-sidebar-chat-handoff": chatViewTransitionHandoff ? "true" : undefined, style: chatViewTransition
1900
1852
  ? getAgentChatViewTransitionStyle(panelStyle)
1901
- : panelStyle, inert: sidebarAnimationEnabled && !panelOpen ? true : undefined, "aria-hidden": sidebarAnimationEnabled && !panelOpen ? true : undefined, children: _jsx("div", { className: "agent-sidebar-panel-inner flex min-h-0 flex-1 flex-col", children: _jsx(AgentPanel, { emptyStateText: emptyStateText, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, composerToolbarSlot: composerToolbarSlot, composerSlot: composerSlot, onComposerTextChange: onComposerTextChange, imageModelMenu: imageModelMenu, threadFooterSlot: threadFooterSlot, missingApiKeySetupLayout: "sidebar", onCollapse: () => setOpenPersisted(false), isFullscreen: effectiveFullscreen, onToggleFullscreen: isMobile ? undefined : (onFullscreenRequest ?? toggleFullscreen), onSnapTo75Percent: isMobile ? undefined : snapTo75Percent, isWideDrawer: isMobile ? false : isWideDrawer, onExitWideDrawer: isMobile ? undefined : exitWideDrawer, storageKey: storageKey, scope: scope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync, agentPageHref: agentPageHref, allowSettingsMode: false, chatOnly: true }) }) }), showResizeHandle && isLeft && (_jsx(ResizeHandle, { position: position, onDrag: handleDrag, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd }))] })) : null;
1853
+ : panelStyle, inert: sidebarAnimationEnabled && !panelOpen ? true : undefined, "aria-hidden": sidebarAnimationEnabled && !panelOpen ? true : undefined, children: _jsx("div", { className: "agent-sidebar-panel-inner flex min-h-0 flex-1 flex-col", children: _jsx(AgentPanel, { emptyStateText: emptyStateText, suggestions: suggestions, dynamicSuggestions: dynamicSuggestions, composerToolbarSlot: composerToolbarSlot, composerSlot: composerSlot, onComposerTextChange: onComposerTextChange, imageModelMenu: imageModelMenu, threadFooterSlot: threadFooterSlot, missingApiKeySetupLayout: "sidebar", onCollapse: () => setOpenPersisted(false), onSnapTo75Percent: isMobile ? undefined : snapTo75Percent, isWideDrawer: isMobile ? false : isWideDrawer, onExitWideDrawer: isMobile ? undefined : exitWideDrawer, storageKey: storageKey, scope: scope, showScopeBadge: showScopeBadge, browserTabId: browserTabId, threadUrlSync: threadUrlSync, agentPageHref: agentPageHref, allowSettingsMode: false, chatOnly: true }) }) }), showResizeHandle && isLeft && (_jsx(ResizeHandle, { position: position, onDrag: handleDrag, onResizeStart: handleResizeStart, onResizeEnd: handleResizeEnd }))] })) : null;
1902
1854
  const drawerPlaceholder = wideDrawerEnabled && !presentationMode && shouldRenderPanel ? (_jsx("div", { "aria-hidden": "true", className: "agent-sidebar-drawer-placeholder shrink-0", "data-agent-sidebar-placeholder": "true", style: { width: drawerPlaceholderWidth + 1 } })) : null;
1903
1855
  return (_jsx(AgentSidebarOnboardingContext.Provider, { value: true, children: _jsxs(RealtimeVoiceModeProvider, { browserTabId: browserTabId, children: [showFirstRunOnboarding && (_jsx(Suspense, { fallback: null, children: _jsx(FirstRunOnboarding, {}) })), _jsxs("div", { className: "agent-sidebar-shell flex min-w-0 flex-1 h-screen overflow-hidden", "data-agent-sidebar-position": position, "data-agent-sidebar-resizing": isResizing ? "true" : undefined, children: [isMobile &&
1904
1856
  !presentationMode &&
1905
- (mobileAnimationEnabled ? shouldRenderPanel : open) && (_jsx("div", { className: cn("agent-sidebar-backdrop fixed inset-0 bg-foreground/40", mobileAnimationEnabled && !panelOpen && "pointer-events-none"), "data-agent-sidebar-animation": mobileAnimationEnabled ? "mobile" : undefined, "data-agent-sidebar-state": panelOpen ? "open" : "closed", style: { zIndex: SIDEBAR_OVERLAY_Z_INDEX - 1 }, onClick: () => setOpenPersisted(false) })), shouldMountPanel ? _jsx(URLSync, { browserTabId: browserTabId }) : null, isLeft && !presentationMode ? sidebar : null, isLeft && !presentationMode ? drawerPlaceholder : null, _jsx("div", { className: "agent-sidebar-main-surface flex flex-1 flex-col overflow-auto min-w-0", "data-agent-sidebar-main-position": position, "data-agent-sidebar-main-state": !isMobile &&
1906
- !effectiveFullscreen &&
1907
- !presentationMode &&
1908
- panelOpen
1909
- ? "open"
1910
- : "closed", "data-agent-sidebar-resizing": isResizing ? "true" : undefined, children: _jsx(ScreenRefreshBoundary, { children: children }) }), !isLeft && !presentationMode ? drawerPlaceholder : null, !isLeft && !presentationMode ? sidebar : null] })] }) }));
1857
+ (mobileAnimationEnabled ? shouldRenderPanel : open) && (_jsx("div", { className: cn("agent-sidebar-backdrop fixed inset-0 bg-foreground/40", mobileAnimationEnabled && !panelOpen && "pointer-events-none"), "data-agent-sidebar-animation": mobileAnimationEnabled ? "mobile" : undefined, "data-agent-sidebar-state": panelOpen ? "open" : "closed", style: { zIndex: SIDEBAR_OVERLAY_Z_INDEX - 1 }, onClick: () => setOpenPersisted(false) })), shouldMountPanel ? _jsx(URLSync, { browserTabId: browserTabId }) : null, isLeft && !presentationMode ? sidebar : null, isLeft && !presentationMode ? drawerPlaceholder : null, _jsx("div", { className: "agent-sidebar-main-surface flex flex-1 flex-col overflow-auto min-w-0", "data-agent-sidebar-main-position": position, "data-agent-sidebar-main-state": !isMobile && !presentationMode && panelOpen ? "open" : "closed", "data-agent-sidebar-resizing": isResizing ? "true" : undefined, children: _jsx(ScreenRefreshBoundary, { children: children }) }), !isLeft && !presentationMode ? drawerPlaceholder : null, !isLeft && !presentationMode ? sidebar : null] })] }) }));
1911
1858
  }
1912
1859
  /**
1913
1860
  * Focus the agent chat composer input.
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
- error?: undefined;
66
65
  states: {
67
66
  clientId: number;
68
67
  state: string;
69
68
  }[];
69
+ error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
- error?: undefined;
81
80
  users: {
82
81
  clientId: number;
83
82
  lastSeen: number;
84
83
  }[];
84
+ error?: undefined;
85
85
  }>>;
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- error: string;
17
16
  ok?: undefined;
17
+ error: string;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -245,11 +245,28 @@ function isServerlessRequestRuntime() {
245
245
  process.env.VERCEL === "1");
246
246
  }
247
247
  /**
248
- * A runtime that is ALLOWED to migrate: scheduled jobs and durable background
249
- * workers, which are off the request path and may take as long as they need.
248
+ * Whether this app migrates at RELEASE time, so skipping on the request path
249
+ * is safe.
250
250
  *
251
- * Set by whichever runtime claims migration duty. Analytics sets its own
252
- * equivalent flag before running the scheduled rollup.
251
+ * Without this, "skip in serverless" silently means "never migrate" for any app
252
+ * that has no release entrypoint — and at the time this was written exactly one
253
+ * of seventeen templates had one. Existing tables keep working, so nothing
254
+ * fails; a newly added migration just never applies, and a fresh deploy comes
255
+ * up with missing tables. That is a skip indistinguishable from success, which
256
+ * is the failure mode this repo bans.
257
+ *
258
+ * So the request-path skip is opt-in per app. An app sets this once it owns a
259
+ * release migration step (see templates/analytics/scripts/migrate-production.ts
260
+ * and the `migrate:production` build step in its netlify.toml).
261
+ */
262
+ function appMigratesAtRelease() {
263
+ const raw = process.env.AGENT_NATIVE_RELEASE_MIGRATIONS?.trim();
264
+ return !!raw && ["1", "true", "yes", "on"].includes(raw.toLowerCase());
265
+ }
266
+ /**
267
+ * A runtime that is ALLOWED to migrate: release scripts, scheduled jobs, and
268
+ * durable background workers, which are off the request path and may take as
269
+ * long as they need. Claimed with {@link withMigrationRuntime}.
253
270
  */
254
271
  function isMigrationAuthorizedRuntime() {
255
272
  return (globalThis.__AGENT_NATIVE_MIGRATION_RUNTIME__ === true);
@@ -322,6 +339,7 @@ export function runMigrations(migrations, options) {
322
339
  // the explicit opt-in for a caller that genuinely cannot defer.
323
340
  if (options?.runInServerlessRequest !== true &&
324
341
  isServerlessRequestRuntime() &&
342
+ appMigratesAtRelease() &&
325
343
  !isMigrationAuthorizedRuntime()) {
326
344
  console.info(`[migrations] Skipping "${table}" migrations in a serverless request runtime`);
327
345
  return;
@@ -312,3 +312,4 @@ export declare function resolveNitroBundledYjsEntry(): string;
312
312
  * the small set above bundled to keep their package manifests traceable.
313
313
  */
314
314
  export declare function nitroNoExternalsForPreset(targetPreset: string): true | readonly string[];
315
+ export declare function resolveNitroBuildReplacements(env?: NodeJS.ProcessEnv): Record<string, string>;