@agent-native/core 0.163.4 → 0.164.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/app/components/agents/FeatureFlagsFleetPanel.tsx +78 -19
  3. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +29 -0
  4. package/corpus/templates/analytics/app/lib/use-auto-focus-select.ts +4 -4
  5. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -0
  6. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +6 -0
  7. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +1 -2
  8. package/corpus/templates/analytics/app/routes/_index.tsx +3 -2
  9. package/corpus/templates/analytics/app/routes/adhoc.$id.tsx +7 -2
  10. package/corpus/templates/analytics/app/routes/dashboard.tsx +3 -2
  11. package/corpus/templates/analytics/app/routes/overview.tsx +3 -2
  12. package/corpus/templates/analytics/app/routes/traffic.tsx +3 -2
  13. package/corpus/templates/analytics/changelog/2026-08-18-verify-cross-app-feature-flag-changes.md +6 -0
  14. package/corpus/templates/analytics/netlify.toml +2 -0
  15. package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +196 -48
  16. package/corpus/templates/analytics/server/plugins/feature-flags.ts +5 -0
  17. package/corpus/templates/analytics/shared/feature-flags.ts +10 -0
  18. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +5 -1
  19. package/corpus/templates/assets/app/routes/brand-kits._index.tsx +4 -1
  20. package/corpus/templates/assets/app/routes/libraries.tsx +4 -1
  21. package/corpus/templates/assets/app/routes/picker.tsx +4 -1
  22. package/corpus/templates/assets/netlify.toml +2 -0
  23. package/corpus/templates/brain/netlify.toml +2 -0
  24. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +2 -1
  25. package/corpus/templates/calendar/changelog/2026-08-19-calendar-page-loads-stay-fast-after-idle-periods.md +6 -0
  26. package/corpus/templates/calendar/netlify.toml +2 -0
  27. package/corpus/templates/chat/netlify.toml +2 -0
  28. package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -3
  29. package/corpus/templates/clips/app/components/player/reaction-emojis.ts +12 -0
  30. package/corpus/templates/clips/app/components/player/reactions-tray.tsx +90 -9
  31. package/corpus/templates/clips/app/components/player/scrubber.tsx +43 -10
  32. package/corpus/templates/clips/app/components/player/video-player.tsx +105 -15
  33. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +1 -2
  34. package/corpus/templates/clips/app/routes/_app.wispr.tsx +2 -1
  35. package/corpus/templates/clips/app/routes/_index.tsx +7 -3
  36. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +13 -8
  37. package/corpus/templates/clips/app/routes/share.$shareId.tsx +15 -8
  38. package/corpus/templates/clips/netlify.toml +2 -0
  39. package/corpus/templates/clips/server/routes/[...page].head.ts +5 -0
  40. package/corpus/templates/content/netlify.toml +2 -0
  41. package/corpus/templates/design/.agents/skills/design-editor-architecture/SKILL.md +103 -0
  42. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +2 -1
  43. package/corpus/templates/design/AGENTS.md +3 -1
  44. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +3 -2
  45. package/corpus/templates/design/app/components/design/editor/AgentNativeMenuMark.tsx +25 -0
  46. package/corpus/templates/design/app/components/design/editor/DesignBottomToolbar.tsx +411 -0
  47. package/corpus/templates/design/app/components/design/editor/DesignCollaborators.tsx +153 -0
  48. package/corpus/templates/design/app/components/design/editor/DesignWorkspaceRail.tsx +231 -0
  49. package/corpus/templates/design/app/components/design/editor/MakeRealDialog.tsx +223 -0
  50. package/corpus/templates/design/app/components/design/editor/PendingScreenDeletionDialog.tsx +58 -0
  51. package/corpus/templates/design/app/components/design/editor/PendingVisualStyleWarningDialog.tsx +56 -0
  52. package/corpus/templates/design/app/components/design/editor/ReadOnlyEditorPanel.tsx +23 -0
  53. package/corpus/templates/design/app/components/design/editor/SaveTemplateDialog.tsx +161 -0
  54. package/corpus/templates/design/app/components/design/editor/toolbar-controls.tsx +185 -0
  55. package/corpus/templates/design/app/pages/DesignEditor.tsx +20913 -0
  56. package/corpus/templates/design/app/pages/design-editor/command-types.ts +120 -0
  57. package/corpus/templates/design/app/pages/design-editor/commands/README.md +193 -0
  58. package/corpus/templates/design/app/pages/design-editor/commands/add-auto-layout.ts +416 -0
  59. package/corpus/templates/design/app/pages/design-editor/commands/add-screen.ts +112 -0
  60. package/corpus/templates/design/app/pages/design-editor/commands/align-selection.ts +183 -0
  61. package/corpus/templates/design/app/pages/design-editor/commands/apply-design-editor-command.ts +243 -0
  62. package/corpus/templates/design/app/pages/design-editor/commands/apply-file-content-update.ts +236 -0
  63. package/corpus/templates/design/app/pages/design-editor/commands/apply-local-content-update.ts +304 -0
  64. package/corpus/templates/design/app/pages/design-editor/commands/apply-pending-visual-styles-with-agent.ts +376 -0
  65. package/corpus/templates/design/app/pages/design-editor/commands/apply-to-source.ts +132 -0
  66. package/corpus/templates/design/app/pages/design-editor/commands/can-move-layer.ts +117 -0
  67. package/corpus/templates/design/app/pages/design-editor/commands/change-selected-z-index.ts +212 -0
  68. package/corpus/templates/design/app/pages/design-editor/commands/commit-relative-style-delta-to-selected-layers.ts +176 -0
  69. package/corpus/templates/design/app/pages/design-editor/commands/commit-styles-to-selected-layers.ts +175 -0
  70. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +710 -0
  71. package/corpus/templates/design/app/pages/design-editor/commands/confirm-make-real.ts +99 -0
  72. package/corpus/templates/design/app/pages/design-editor/commands/copy-as-figma-svg.ts +95 -0
  73. package/corpus/templates/design/app/pages/design-editor/commands/copy-selection.ts +156 -0
  74. package/corpus/templates/design/app/pages/design-editor/commands/create-primitive.ts +291 -0
  75. package/corpus/templates/design/app/pages/design-editor/commands/create-screen-frame.ts +131 -0
  76. package/corpus/templates/design/app/pages/design-editor/commands/cross-screen-element-drop.ts +577 -0
  77. package/corpus/templates/design/app/pages/design-editor/commands/delete-files.ts +364 -0
  78. package/corpus/templates/design/app/pages/design-editor/commands/delete-selection.ts +521 -0
  79. package/corpus/templates/design/app/pages/design-editor/commands/detach-instance-menu-action.ts +88 -0
  80. package/corpus/templates/design/app/pages/design-editor/commands/distribute-selection.ts +114 -0
  81. package/corpus/templates/design/app/pages/design-editor/commands/download-all-screens-pdf.ts +116 -0
  82. package/corpus/templates/design/app/pages/design-editor/commands/download-pdf.ts +103 -0
  83. package/corpus/templates/design/app/pages/design-editor/commands/download-svg.ts +159 -0
  84. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-screen.ts +201 -0
  85. package/corpus/templates/design/app/pages/design-editor/commands/duplicate-selection.ts +279 -0
  86. package/corpus/templates/design/app/pages/design-editor/commands/editor-paste.ts +81 -0
  87. package/corpus/templates/design/app/pages/design-editor/commands/enter-hotkey.ts +155 -0
  88. package/corpus/templates/design/app/pages/design-editor/commands/enter-single-screen.ts +168 -0
  89. package/corpus/templates/design/app/pages/design-editor/commands/escape-hotkey.ts +212 -0
  90. package/corpus/templates/design/app/pages/design-editor/commands/frame-selection.ts +119 -0
  91. package/corpus/templates/design/app/pages/design-editor/commands/geometry-commit.ts +169 -0
  92. package/corpus/templates/design/app/pages/design-editor/commands/get-selected-layer-snapshots.ts +177 -0
  93. package/corpus/templates/design/app/pages/design-editor/commands/group-selection.ts +127 -0
  94. package/corpus/templates/design/app/pages/design-editor/commands/iframe-context-menu.ts +121 -0
  95. package/corpus/templates/design/app/pages/design-editor/commands/import-figma-clipboard-into-design.ts +119 -0
  96. package/corpus/templates/design/app/pages/design-editor/commands/layer-marquee-selection-change.ts +163 -0
  97. package/corpus/templates/design/app/pages/design-editor/commands/layer-move-to-screen.ts +331 -0
  98. package/corpus/templates/design/app/pages/design-editor/commands/layer-move.ts +517 -0
  99. package/corpus/templates/design/app/pages/design-editor/commands/layer-rename.ts +247 -0
  100. package/corpus/templates/design/app/pages/design-editor/commands/layer-selection-change.ts +161 -0
  101. package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +122 -0
  102. package/corpus/templates/design/app/pages/design-editor/commands/nudge-selection.ts +213 -0
  103. package/corpus/templates/design/app/pages/design-editor/commands/overview-primitive-reparent.ts +318 -0
  104. package/corpus/templates/design/app/pages/design-editor/commands/paste-copied-screens.ts +114 -0
  105. package/corpus/templates/design/app/pages/design-editor/commands/paste-over-selection.ts +70 -0
  106. package/corpus/templates/design/app/pages/design-editor/commands/paste-selection.ts +436 -0
  107. package/corpus/templates/design/app/pages/design-editor/commands/paste-to-replace.ts +135 -0
  108. package/corpus/templates/design/app/pages/design-editor/commands/pasted-image-files.ts +209 -0
  109. package/corpus/templates/design/app/pages/design-editor/commands/persist-frame-geometry-save.ts +113 -0
  110. package/corpus/templates/design/app/pages/design-editor/commands/persist-tweak-save.ts +160 -0
  111. package/corpus/templates/design/app/pages/design-editor/commands/primitive-created.ts +168 -0
  112. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-layer-state-edit.ts +149 -0
  113. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-structure-edit.ts +200 -0
  114. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-live-text-edit.ts +152 -0
  115. package/corpus/templates/design/app/pages/design-editor/commands/record-pending-visual-style-edit.ts +247 -0
  116. package/corpus/templates/design/app/pages/design-editor/commands/redo.ts +1086 -0
  117. package/corpus/templates/design/app/pages/design-editor/commands/render-png-blob.ts +200 -0
  118. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +207 -0
  119. package/corpus/templates/design/app/pages/design-editor/commands/screen-element-select.ts +262 -0
  120. package/corpus/templates/design/app/pages/design-editor/commands/screen-text-content-change.ts +216 -0
  121. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-duplicate-change.ts +108 -0
  122. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-structure-change.ts +280 -0
  123. package/corpus/templates/design/app/pages/design-editor/commands/screen-visual-style-change.ts +151 -0
  124. package/corpus/templates/design/app/pages/design-editor/commands/send-overview-annotations.ts +88 -0
  125. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-move-semantic-handoff.ts +122 -0
  126. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-semantic-handoff.ts +143 -0
  127. package/corpus/templates/design/app/pages/design-editor/commands/send-runtime-layer-state-semantic-handoff.ts +115 -0
  128. package/corpus/templates/design/app/pages/design-editor/commands/start-retry-generation.ts +152 -0
  129. package/corpus/templates/design/app/pages/design-editor/commands/start-sidebar-resize.ts +99 -0
  130. package/corpus/templates/design/app/pages/design-editor/commands/style-change.ts +152 -0
  131. package/corpus/templates/design/app/pages/design-editor/commands/styles-change.ts +155 -0
  132. package/corpus/templates/design/app/pages/design-editor/commands/suggest-auto-layout.ts +125 -0
  133. package/corpus/templates/design/app/pages/design-editor/commands/text-content-change.ts +201 -0
  134. package/corpus/templates/design/app/pages/design-editor/commands/tidy-up.ts +123 -0
  135. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-hidden.ts +167 -0
  136. package/corpus/templates/design/app/pages/design-editor/commands/toggle-layer-locked.ts +175 -0
  137. package/corpus/templates/design/app/pages/design-editor/commands/toggle-motion-keyframe.ts +99 -0
  138. package/corpus/templates/design/app/pages/design-editor/commands/tweak-prompt-submit.ts +84 -0
  139. package/corpus/templates/design/app/pages/design-editor/commands/undo.ts +906 -0
  140. package/corpus/templates/design/app/pages/design-editor/commands/ungroup-selection.ts +170 -0
  141. package/corpus/templates/design/app/pages/design-editor/commands/visual-duplicate-change.ts +119 -0
  142. package/corpus/templates/design/app/pages/design-editor/commands/visual-structure-change.ts +274 -0
  143. package/corpus/templates/design/app/pages/design-editor/commands/write-frame-geometry-snapshot.ts +99 -0
  144. package/corpus/templates/design/app/pages/design-editor/derive/design-breakpoints.ts +56 -0
  145. package/corpus/templates/design/app/pages/design-editor/derive/overview-screens.ts +146 -0
  146. package/corpus/templates/design/app/pages/design-editor/domains/use-tweaks.ts +326 -0
  147. package/corpus/templates/design/app/pages/design-editor/editor-constants.ts +54 -0
  148. package/corpus/templates/design/app/pages/design-editor/editor-helpers.ts +183 -0
  149. package/corpus/templates/design/app/pages/design-editor/effects/adopt-db-file-content.ts +270 -0
  150. package/corpus/templates/design/app/pages/design-editor/effects/mirror-selection-to-agent-chat.ts +134 -0
  151. package/corpus/templates/design/app/pages/design-editor/effects/motion-autosave.ts +335 -0
  152. package/corpus/templates/design/app/pages/design-editor/effects/observe-collab-text.ts +183 -0
  153. package/corpus/templates/design/app/pages/design-editor/effects/publish-agent-selection-context.ts +252 -0
  154. package/corpus/templates/design/app/pages/design-editor/effects/resume-pending-generation.ts +178 -0
  155. package/corpus/templates/design/app/pages/design-editor/effects/seed-collab-content.ts +157 -0
  156. package/corpus/templates/design/app/pages/design-editor/preview-html.ts +19 -0
  157. package/corpus/templates/design/app/routes/examples.tsx +2 -1
  158. package/corpus/templates/design/netlify.toml +2 -0
  159. package/corpus/templates/design/shared/board-file.ts +2 -1
  160. package/corpus/templates/dispatch/netlify.toml +2 -0
  161. package/corpus/templates/forms/app/routes/_index.tsx +3 -2
  162. package/corpus/templates/forms/netlify.toml +2 -0
  163. package/corpus/templates/mail/app/routes/_index.tsx +3 -2
  164. package/corpus/templates/mail/netlify.toml +2 -0
  165. package/corpus/templates/plan/netlify.toml +2 -0
  166. package/corpus/templates/slides/netlify.toml +2 -0
  167. package/corpus/templates/tasks/app/routes/_index.tsx +2 -1
  168. package/dist/agent/engine/builder-engine.d.ts +6 -4
  169. package/dist/agent/engine/builder-engine.js +91 -26
  170. package/dist/agent/engine/registry.js +16 -0
  171. package/dist/cli/code-agent-agent-tools.d.ts +2 -0
  172. package/dist/cli/code-agent-agent-tools.js +188 -0
  173. package/dist/cli/code-agent-collaboration.d.ts +34 -0
  174. package/dist/cli/code-agent-collaboration.js +104 -0
  175. package/dist/cli/code-agent-executor.js +6 -0
  176. package/dist/cli/code-agent-schedules.d.ts +54 -0
  177. package/dist/cli/code-agent-schedules.js +198 -0
  178. package/dist/client/AgentPanel.js +1 -1
  179. package/dist/client/ConnectBuilderCard.js +5 -2
  180. package/dist/client/EnvironmentBadge.js +6 -5
  181. package/dist/client/chat/run-recovery.js +3 -2
  182. package/dist/client/chat/tool-call-display.js +2 -3
  183. package/dist/client/components/CodeRequiredDialog.js +4 -2
  184. package/dist/client/i18n.js +5 -4
  185. package/dist/client/settings/SettingsPanel.js +1 -1
  186. package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
  187. package/dist/client/settings/useBuilderStatus.d.ts +6 -1
  188. package/dist/client/settings/useBuilderStatus.js +22 -33
  189. package/dist/client/transcription/BuilderTranscriptionCta.js +1 -1
  190. package/dist/code-agents/index.d.ts +2 -0
  191. package/dist/code-agents/index.js +2 -0
  192. package/dist/collab/awareness.d.ts +2 -2
  193. package/dist/collab/routes.d.ts +1 -1
  194. package/dist/deploy/build.js +10 -4
  195. package/dist/localization/shared.d.ts +2 -0
  196. package/dist/localization/shared.js +5 -0
  197. package/dist/mcp-client/oauth-client.d.ts +1 -0
  198. package/dist/mcp-client/oauth-client.js +50 -1
  199. package/dist/notifications/routes.d.ts +3 -3
  200. package/dist/observability/routes.d.ts +1 -1
  201. package/dist/provider-api/actions/provider-api.d.ts +2 -2
  202. package/dist/server/auth-marketing-locales.d.ts +7 -0
  203. package/dist/server/auth-marketing-locales.js +1322 -0
  204. package/dist/server/auth-marketing.d.ts +1 -0
  205. package/dist/server/auth-marketing.js +35 -6
  206. package/dist/server/auth.js +4 -17
  207. package/dist/server/builder-browser.d.ts +22 -0
  208. package/dist/server/builder-browser.js +77 -1
  209. package/dist/server/builder-oauth.d.ts +50 -0
  210. package/dist/server/builder-oauth.js +263 -0
  211. package/dist/server/core-routes-plugin.d.ts +11 -0
  212. package/dist/server/core-routes-plugin.js +291 -415
  213. package/dist/server/credential-provider.d.ts +1 -1
  214. package/dist/server/credential-provider.js +1 -1
  215. package/dist/server/csrf.js +2 -2
  216. package/dist/server/onboarding-html.js +52 -120
  217. package/dist/server/ssr-handler.js +13 -4
  218. package/dist/settings/store.d.ts +9 -0
  219. package/dist/settings/store.js +19 -0
  220. package/dist/shared/cache-control.d.ts +22 -0
  221. package/dist/shared/cache-control.js +25 -0
  222. package/dist/shared/index.d.ts +1 -0
  223. package/dist/shared/index.js +1 -0
  224. package/dist/templates/chat/netlify.toml +2 -0
  225. package/docs/content/deployment.mdx +2 -0
  226. package/docs/content/multi-app-workspace.mdx +1 -1
  227. package/package.json +2 -2
  228. package/src/templates/chat/netlify.toml +2 -0
@@ -0,0 +1,252 @@
1
+ import { setClientAppState } from "@agent-native/core/client/hooks";
2
+ import type { RefObject } from "react";
3
+
4
+ import type { CodeWorkbenchActiveFile } from "@/components/design/code-workbench/CodeWorkbench";
5
+ import type { InspectorTab } from "@/components/design/EditPanel";
6
+ import type { ElementInfo } from "@/components/design/types";
7
+ import type { ResponsiveEditScope } from "@/pages/design-editor/command-types";
8
+ import { DESIGN_SELECTION_ZOOM_SAVE_DELAY_MS } from "@/pages/design-editor/editor-constants";
9
+ import { designSelectionStateKeys } from "@/pages/design-editor/editor-helpers";
10
+ import type {
11
+ DesignData,
12
+ DesignFile,
13
+ DesignLeftPanel,
14
+ DesignTool,
15
+ EditorMode,
16
+ } from "@/pages/design-editor/types";
17
+
18
+ export interface PublishAgentSelectionContextArgs {
19
+ activeBreakpointWidthState: number | undefined;
20
+ activeCodeFile: CodeWorkbenchActiveFile | null;
21
+ activeFile: DesignFile;
22
+ activeInspectorTab: InspectorTab;
23
+ activeLeftPanel: DesignLeftPanel;
24
+ activeTool: DesignTool;
25
+ design: DesignData | null;
26
+ designDataJson: Record<string, unknown>;
27
+ designSelectionOwnerIdRef: RefObject<string>;
28
+ files: DesignFile[];
29
+ hoveredElement: ElementInfo | null;
30
+ id: string | undefined;
31
+ isSignedIn: boolean;
32
+ mode: EditorMode;
33
+ motionDockOpen: boolean;
34
+ pendingPersistedSelectionWriteRef: RefObject<{
35
+ key: string;
36
+ contextKey: string;
37
+ value: Record<string, unknown>;
38
+ } | null>;
39
+ persistedSelectionContextRef: RefObject<string | null>;
40
+ persistedSelectionStateRef: RefObject<string | null>;
41
+ persistedSelectionWriteTimerRef: RefObject<number | null>;
42
+ responsiveEditScope: ResponsiveEditScope;
43
+ selectedElement: ElementInfo | null;
44
+ selectedScreenIds: string[];
45
+ selectedStateId: string | null;
46
+ viewMode: "single" | "overview";
47
+ zoom: number;
48
+ }
49
+
50
+ export function runPublishAgentSelectionContext({
51
+ activeBreakpointWidthState,
52
+ activeCodeFile,
53
+ activeFile,
54
+ activeInspectorTab,
55
+ activeLeftPanel,
56
+ activeTool,
57
+ design,
58
+ designDataJson,
59
+ designSelectionOwnerIdRef,
60
+ files,
61
+ hoveredElement,
62
+ id,
63
+ isSignedIn,
64
+ mode,
65
+ motionDockOpen,
66
+ pendingPersistedSelectionWriteRef,
67
+ persistedSelectionContextRef,
68
+ persistedSelectionStateRef,
69
+ persistedSelectionWriteTimerRef,
70
+ responsiveEditScope,
71
+ selectedElement,
72
+ selectedScreenIds,
73
+ selectedStateId,
74
+ viewMode,
75
+ zoom,
76
+ }: PublishAgentSelectionContextArgs) {
77
+ if (!id || !isSignedIn) return;
78
+ const selection = {
79
+ designId: id,
80
+ designTitle: design?.title ?? null,
81
+ activeFileId: activeFile?.id ?? null,
82
+ activeFilename: activeFile?.filename ?? null,
83
+ viewMode,
84
+ zoom,
85
+ screens: files.map((file) => ({
86
+ id: file.id,
87
+ filename: file.filename,
88
+ fileType: file.fileType,
89
+ })),
90
+ selectedScreenIds,
91
+ selectedElement,
92
+ hoveredElement,
93
+ mode,
94
+ activeTool,
95
+ inspectorTab: activeInspectorTab,
96
+ leftPanel: activeLeftPanel,
97
+ codeWorkspace: {
98
+ open: activeLeftPanel === "code",
99
+ backendKind: activeCodeFile?.backendKind ?? "virtual-inline",
100
+ activePath: activeCodeFile?.path ?? null,
101
+ activeFileId: activeCodeFile?.fileId ?? null,
102
+ dirty: activeCodeFile?.dirty ?? false,
103
+ versionHash: activeCodeFile?.versionHash ?? null,
104
+ },
105
+ // §8 DesignNavigationState additions — dock + breakpoint context
106
+ dock: { kind: "motion" as const, open: motionDockOpen },
107
+ motion: {
108
+ previewing: false,
109
+ playheadMs: 0,
110
+ timelineId: undefined as string | undefined,
111
+ selectedTrackId: undefined as string | undefined,
112
+ selectedKeyframeId: undefined as string | undefined,
113
+ },
114
+ // §8 breakpoint fields — "auto" = no specific breakpoint focused.
115
+ breakpoint: (activeBreakpointWidthState != null
116
+ ? activeBreakpointWidthState < 500
117
+ ? "mobile"
118
+ : activeBreakpointWidthState < 1024
119
+ ? "tablet"
120
+ : "desktop"
121
+ : "auto") as "auto" | "mobile" | "tablet" | "desktop",
122
+ activeBreakpointId: (() => {
123
+ if (activeBreakpointWidthState == null) return undefined;
124
+ try {
125
+ const raw = (designDataJson as Record<string, unknown>)?.breakpointSet;
126
+ if (
127
+ raw &&
128
+ typeof raw === "object" &&
129
+ Array.isArray((raw as Record<string, unknown>).breakpoints)
130
+ ) {
131
+ const bps = (
132
+ raw as { breakpoints: Array<{ id: string; widthPx: number }> }
133
+ ).breakpoints;
134
+ return bps.find((b) => b.widthPx === activeBreakpointWidthState)?.id;
135
+ }
136
+ // coercion-ok: an unreadable breakpointSet means "none configured", which the undefined return already expresses.
137
+ } catch {
138
+ // ignore
139
+ }
140
+ return undefined;
141
+ })(),
142
+ responsiveEditScope,
143
+ breakpointSetId: (() => {
144
+ try {
145
+ const raw = (designDataJson as Record<string, unknown>)?.breakpointSet;
146
+ if (raw && typeof raw === "object") {
147
+ return (raw as Record<string, unknown>).id as string | undefined;
148
+ }
149
+ // coercion-ok: an unreadable breakpointSet means "none configured", which the undefined return already expresses.
150
+ } catch {
151
+ // ignore
152
+ }
153
+ return undefined;
154
+ })(),
155
+ // §8 — active design state (null = Default / live view)
156
+ selectedStateId,
157
+ };
158
+ (window as any).__designSelection = selection;
159
+ const persistedSelection = {
160
+ designId: selection.designId,
161
+ designTitle: selection.designTitle,
162
+ activeFileId: selection.activeFileId,
163
+ activeFilename: selection.activeFilename,
164
+ viewMode: selection.viewMode,
165
+ zoom: selection.zoom,
166
+ screens: selection.screens,
167
+ selectedScreenIds: selection.selectedScreenIds,
168
+ selectedElement: selection.selectedElement,
169
+ mode: selection.mode,
170
+ activeTool: selection.activeTool,
171
+ inspectorTab: selection.inspectorTab,
172
+ leftPanel: selection.leftPanel,
173
+ codeWorkspace: selection.codeWorkspace,
174
+ dock: selection.dock,
175
+ motion: selection.motion,
176
+ breakpoint: selection.breakpoint,
177
+ activeBreakpointId: selection.activeBreakpointId,
178
+ responsiveEditScope: selection.responsiveEditScope,
179
+ breakpointSetId: selection.breakpointSetId,
180
+ selectedStateId: selection.selectedStateId,
181
+ ownerId: designSelectionOwnerIdRef.current,
182
+ };
183
+ const persistedKey = JSON.stringify(persistedSelection);
184
+ const { zoom: _zoom, ...persistedContext } = persistedSelection;
185
+ const persistedContextKey = JSON.stringify(persistedContext);
186
+ const writePersistedSelection = (pending: {
187
+ key: string;
188
+ contextKey: string;
189
+ value: Record<string, unknown>;
190
+ }) => {
191
+ persistedSelectionStateRef.current = pending.key;
192
+ persistedSelectionContextRef.current = pending.contextKey;
193
+ for (const key of designSelectionStateKeys()) {
194
+ setClientAppState(key, pending.value, { keepalive: true }).catch(
195
+ () => {},
196
+ );
197
+ }
198
+ };
199
+ if (persistedSelectionStateRef.current === persistedKey) {
200
+ // A zoom gesture can return to the last persisted value before its
201
+ // trailing write fires. Cancel that now-stale queued intermediate.
202
+ if (persistedSelectionWriteTimerRef.current !== null) {
203
+ window.clearTimeout(persistedSelectionWriteTimerRef.current);
204
+ persistedSelectionWriteTimerRef.current = null;
205
+ }
206
+ pendingPersistedSelectionWriteRef.current = null;
207
+ } else if (persistedSelectionContextRef.current !== persistedContextKey) {
208
+ // Screen/selection/tool/panel changes are agent context and stay
209
+ // immediate. Only zoom-only churn is coalesced below.
210
+ if (persistedSelectionWriteTimerRef.current !== null) {
211
+ window.clearTimeout(persistedSelectionWriteTimerRef.current);
212
+ persistedSelectionWriteTimerRef.current = null;
213
+ }
214
+ pendingPersistedSelectionWriteRef.current = null;
215
+ writePersistedSelection({
216
+ key: persistedKey,
217
+ contextKey: persistedContextKey,
218
+ value: persistedSelection,
219
+ });
220
+ } else if (pendingPersistedSelectionWriteRef.current?.key !== persistedKey) {
221
+ // Wheel/pinch zoom can render at 60+ Hz. Writing both scoped and global
222
+ // application-state keys for every tick creates an avoidable request +
223
+ // sync-event storm; keep the immediate in-memory context above and
224
+ // persist only the settled zoom after a short trailing delay.
225
+ pendingPersistedSelectionWriteRef.current = {
226
+ key: persistedKey,
227
+ contextKey: persistedContextKey,
228
+ value: persistedSelection,
229
+ };
230
+ if (persistedSelectionWriteTimerRef.current !== null) {
231
+ window.clearTimeout(persistedSelectionWriteTimerRef.current);
232
+ }
233
+ persistedSelectionWriteTimerRef.current = window.setTimeout(() => {
234
+ persistedSelectionWriteTimerRef.current = null;
235
+ const pending = pendingPersistedSelectionWriteRef.current;
236
+ pendingPersistedSelectionWriteRef.current = null;
237
+ if (pending) writePersistedSelection(pending);
238
+ }, DESIGN_SELECTION_ZOOM_SAVE_DELAY_MS);
239
+ }
240
+ const el = document.documentElement;
241
+ el.dataset.designId = id;
242
+ if (activeFile?.id) el.dataset.fileId = activeFile.id;
243
+ el.dataset.viewMode = viewMode;
244
+ el.dataset.zoom = String(zoom);
245
+ return () => {
246
+ delete (window as any).__designSelection;
247
+ delete el.dataset.designId;
248
+ delete el.dataset.fileId;
249
+ delete el.dataset.viewMode;
250
+ delete el.dataset.zoom;
251
+ };
252
+ }
@@ -0,0 +1,178 @@
1
+ import type { AgentChatMessage } from "@agent-native/core/client/agent-chat";
2
+ import type { PromptComposerSubmitOptions } from "@agent-native/core/client/composer";
3
+ import type { Dispatch, RefObject, SetStateAction } from "react";
4
+
5
+ import {
6
+ isPendingGenerationStale,
7
+ patchPendingGeneration,
8
+ readPendingGeneration,
9
+ } from "@/lib/pending-generation";
10
+ import {
11
+ designGenerationDirectives,
12
+ designIntakeQuestionDirectives,
13
+ designTemplateRefinementDirectives,
14
+ designVariantGenerationDirectives,
15
+ formatUploadedFileContext,
16
+ imageAttachmentsFromUploadedFiles,
17
+ loadDesignSystemGenerationContext,
18
+ promptRequestsVariantExploration,
19
+ } from "@/pages/design-editor/generation-prompt-directives";
20
+ import type { DesignData, DesignFile } from "@/pages/design-editor/types";
21
+
22
+ export interface ResumePendingGenerationArgs {
23
+ agentSubmit: (
24
+ message: string,
25
+ context: string,
26
+ options?: Omit<AgentChatMessage, "message" | "context">,
27
+ ) => string;
28
+ clearGenerationCompleteTimer: () => void;
29
+ design: DesignData | null;
30
+ files: DesignFile[];
31
+ generationModelRef: RefObject<{
32
+ model?: string;
33
+ engine?: string;
34
+ effort?: PromptComposerSubmitOptions["effort"];
35
+ } | null>;
36
+ id: string | undefined;
37
+ markGenerationStale: () => void;
38
+ setGenerationChatTabId: Dispatch<SetStateAction<string | null>>;
39
+ setGenerationIssue: Dispatch<SetStateAction<string | null>>;
40
+ setHasPendingGeneration: Dispatch<SetStateAction<boolean>>;
41
+ trackAgentGeneration: (tabId: string) => void;
42
+ }
43
+
44
+ export function runResumePendingGeneration({
45
+ agentSubmit,
46
+ clearGenerationCompleteTimer,
47
+ design,
48
+ files,
49
+ generationModelRef,
50
+ id,
51
+ markGenerationStale,
52
+ setGenerationChatTabId,
53
+ setGenerationIssue,
54
+ setHasPendingGeneration,
55
+ trackAgentGeneration,
56
+ }: ResumePendingGenerationArgs) {
57
+ if (!id || !design) return;
58
+
59
+ const pending = readPendingGeneration(id);
60
+ if (!pending) {
61
+ setHasPendingGeneration(false);
62
+ return;
63
+ }
64
+ const templateRefinement = Boolean(pending.templateId && files.length > 0);
65
+ if (files.length > 0 && !templateRefinement) return;
66
+
67
+ if (isPendingGenerationStale(pending)) {
68
+ markGenerationStale();
69
+ return;
70
+ }
71
+
72
+ if (pending.runTabId) {
73
+ setGenerationIssue(null);
74
+ setHasPendingGeneration(true);
75
+ setGenerationChatTabId(pending.runTabId);
76
+ trackAgentGeneration(pending.runTabId);
77
+ return;
78
+ }
79
+
80
+ const prompt =
81
+ pending.prompt?.trim() || `Create an initial design for ${design.title}.`;
82
+ const uploadedFiles = Array.isArray(pending.files) ? pending.files : [];
83
+ const fileContext = formatUploadedFileContext(uploadedFiles);
84
+ const images = imageAttachmentsFromUploadedFiles(uploadedFiles);
85
+ const sourceContext = pending.source
86
+ ? `The user picked the "${pending.source}" template${pending.templateId ? ` (id: "${pending.templateId}")` : ""}.`
87
+ : "The user just created a new empty design.";
88
+ const pendingDesignSystemId =
89
+ pending.designSystemId === undefined
90
+ ? design.designSystemId
91
+ : pending.designSystemId;
92
+
93
+ if (pending.autoGenerate === false) {
94
+ setGenerationIssue(null);
95
+ setHasPendingGeneration(true);
96
+ return;
97
+ }
98
+
99
+ let cancelled = false;
100
+ void (async () => {
101
+ const shouldExploreVariants = promptRequestsVariantExploration(prompt);
102
+ // A reference screenshot already answers the questions the intake flow
103
+ // asks. Spending the one turn that can see the image on a questionnaire
104
+ // means the turn that writes HTML never sees it.
105
+ const hasReferenceImages = images.length > 0;
106
+ const shouldSkipQuestions =
107
+ pending.skipQuestions === true ||
108
+ shouldExploreVariants ||
109
+ hasReferenceImages;
110
+ const designSystemContext = await loadDesignSystemGenerationContext(
111
+ pendingDesignSystemId,
112
+ );
113
+ if (cancelled) return;
114
+ const context = [
115
+ sourceContext,
116
+ `Design id: "${id}"`,
117
+ `Design title: "${design.title}"`,
118
+ `User request: "${prompt}"`,
119
+ pendingDesignSystemId
120
+ ? `Design system id: "${pendingDesignSystemId}"`
121
+ : "",
122
+ designSystemContext,
123
+ fileContext,
124
+ "",
125
+ ...(pending.templateId
126
+ ? designTemplateRefinementDirectives(
127
+ id,
128
+ pending.templateId,
129
+ pendingDesignSystemId,
130
+ )
131
+ : shouldExploreVariants
132
+ ? designVariantGenerationDirectives(id, pendingDesignSystemId)
133
+ : shouldSkipQuestions
134
+ ? designGenerationDirectives(
135
+ id,
136
+ pendingDesignSystemId,
137
+ images.length,
138
+ )
139
+ : designIntakeQuestionDirectives(
140
+ id,
141
+ pendingDesignSystemId,
142
+ images.length,
143
+ )),
144
+ ].join("\n");
145
+
146
+ clearGenerationCompleteTimer();
147
+ setGenerationIssue(null);
148
+ generationModelRef.current = {
149
+ model: pending.model,
150
+ engine: pending.engine,
151
+ effort: pending.effort,
152
+ };
153
+ const runTabId = agentSubmit(
154
+ shouldSkipQuestions
155
+ ? `Generate design for "${design.title}": ${prompt}`
156
+ : `Create design: ${prompt}`,
157
+ context,
158
+ {
159
+ model: pending.model,
160
+ engine: pending.engine,
161
+ effort: pending.effort,
162
+ newTab: true,
163
+ images,
164
+ },
165
+ );
166
+ setGenerationChatTabId(runTabId);
167
+ patchPendingGeneration(id, {
168
+ runTabId,
169
+ attempt: pending.attempt ?? 1,
170
+ designSystemId: pendingDesignSystemId,
171
+ startedAt: Date.now(),
172
+ });
173
+ setHasPendingGeneration(true);
174
+ })();
175
+ return () => {
176
+ cancelled = true;
177
+ };
178
+ }
@@ -0,0 +1,157 @@
1
+ import type { Dispatch, RefObject, SetStateAction } from "react";
2
+ import * as Y from "yjs";
3
+
4
+ import { shouldRebaseCollabDocFromStoredContent } from "@/pages/design-editor/collab-sync";
5
+ import { TAB_ID } from "@/pages/design-editor/editor-session";
6
+ import type { PreviewContentReplaceResult } from "@/pages/design-editor/editor-state";
7
+ import { previewContentReplaceNeedsRenderFallback } from "@/pages/design-editor/editor-state";
8
+ import type { DesignFile } from "@/pages/design-editor/types";
9
+
10
+ export interface SeedCollabContentArgs {
11
+ activeFile: DesignFile;
12
+ activeFileId: string | null;
13
+ collabContentFileIdRef: RefObject<string | null>;
14
+ isSynced: boolean;
15
+ lastAppliedFileUpdatedAtRef: RefObject<string | null>;
16
+ lastLocalContentRef: RefObject<string | null>;
17
+ latestActiveContentRef: RefObject<string | null>;
18
+ pendingLocalFileContentsRef: RefObject<
19
+ Map<
20
+ string,
21
+ { content: string; startedAt: number; baseUpdatedAt?: string | null }
22
+ >
23
+ >;
24
+ replacePreviewContent: (
25
+ nextContent: string,
26
+ selector?: string | null,
27
+ options?: { forceFullDocument?: boolean },
28
+ ) => PreviewContentReplaceResult;
29
+ setCollabContent: Dispatch<SetStateAction<string | null>>;
30
+ setCollabContentFileId: Dispatch<SetStateAction<string | null>>;
31
+ setContentRenderRevision: Dispatch<SetStateAction<number>>;
32
+ undoManagerRef: RefObject<Y.UndoManager | null>;
33
+ ydoc: Y.Doc | null;
34
+ }
35
+
36
+ export function runSeedCollabContent({
37
+ activeFile,
38
+ activeFileId,
39
+ collabContentFileIdRef,
40
+ isSynced,
41
+ lastAppliedFileUpdatedAtRef,
42
+ lastLocalContentRef,
43
+ latestActiveContentRef,
44
+ pendingLocalFileContentsRef,
45
+ replacePreviewContent,
46
+ setCollabContent,
47
+ setCollabContentFileId,
48
+ setContentRenderRevision,
49
+ undoManagerRef,
50
+ ydoc,
51
+ }: SeedCollabContentArgs) {
52
+ if (!ydoc || !isSynced || !activeFileId) return;
53
+ const fileId = activeFileId;
54
+ const ytext = ydoc.getText("content");
55
+ const text = ytext.toString();
56
+ const pendingLocalContent =
57
+ pendingLocalFileContentsRef.current.get(fileId)?.content;
58
+ if (pendingLocalContent && text !== pendingLocalContent) {
59
+ setCollabContent(pendingLocalContent);
60
+ setCollabContentFileId(fileId);
61
+ lastLocalContentRef.current = pendingLocalContent;
62
+ latestActiveContentRef.current = pendingLocalContent;
63
+ if (
64
+ previewContentReplaceNeedsRenderFallback(
65
+ replacePreviewContent(pendingLocalContent, null, {
66
+ forceFullDocument: true,
67
+ }),
68
+ )
69
+ ) {
70
+ setContentRenderRevision((revision) => revision + 1);
71
+ }
72
+ // Full delete+insert with an untracked origin: the UndoManager only
73
+ // tracks LOCAL_EDIT_ORIGIN, so this rewrite is invisible to it. Clear
74
+ // the undo stack so a subsequent Cmd+Z can't replay a tracked delta
75
+ // from before this rewrite against content it no longer matches
76
+ // (see the DE:5135-style mitigation below for the same hazard).
77
+ undoManagerRef.current?.clear(true, false);
78
+ ydoc.transact(() => {
79
+ ytext.delete(0, ytext.length);
80
+ ytext.insert(0, pendingLocalContent);
81
+ }, TAB_ID);
82
+ return;
83
+ }
84
+ if (text.length > 0) {
85
+ const storedContent = activeFile?.content ?? "";
86
+ // §gesture-persistence — a freshly-connected/just-synced doc snapshot
87
+ // has no proven watermark yet in this session. Don't let it outrank
88
+ // SQL merely because it looks like well-formed HTML: rebase from SQL
89
+ // whenever they differ and no baseline has been established (or the
90
+ // live text is outright malformed). See
91
+ // shouldRebaseCollabDocFromStoredContent's doc comment for the full
92
+ // clobber this closes.
93
+ if (
94
+ shouldRebaseCollabDocFromStoredContent({
95
+ liveContent: text,
96
+ storedContent,
97
+ storedUpdatedAt: activeFile?.updatedAt ?? null,
98
+ lastAppliedUpdatedAt: lastAppliedFileUpdatedAtRef.current,
99
+ fileType: activeFile?.fileType ?? "html",
100
+ })
101
+ ) {
102
+ setCollabContent(storedContent);
103
+ setCollabContentFileId(fileId);
104
+ lastLocalContentRef.current = storedContent;
105
+ latestActiveContentRef.current = storedContent;
106
+ if (activeFile?.updatedAt) {
107
+ lastAppliedFileUpdatedAtRef.current = activeFile.updatedAt;
108
+ }
109
+ if (
110
+ previewContentReplaceNeedsRenderFallback(
111
+ replacePreviewContent(storedContent, null, {
112
+ forceFullDocument: true,
113
+ }),
114
+ )
115
+ ) {
116
+ setContentRenderRevision((revision) => revision + 1);
117
+ }
118
+ // Untracked full rewrite — see clear() note above.
119
+ undoManagerRef.current?.clear(true, false);
120
+ ydoc.transact(() => {
121
+ ytext.delete(0, ytext.length);
122
+ ytext.insert(0, storedContent);
123
+ }, TAB_ID);
124
+ return;
125
+ }
126
+ // Y.Doc snapshots are a render seed, not the SQL source of truth; the
127
+ // reconcile effect below advances the updatedAt watermark only after it
128
+ // confirms or applies the current DB content.
129
+ //
130
+ // Item 5 (edit-flash) root cause: this effect's deps include
131
+ // `activeFile?.content`/`activeFile?.updatedAt`, which change on EVERY
132
+ // save (useActionMutation's default onSuccess invalidates all
133
+ // `["action"]` queries, so `get-design` refetches after every single
134
+ // edit and hands back a new `activeFile` object with a bumped
135
+ // `updatedAt`). That refire lands here even when the Y.Doc's `text`
136
+ // hasn't changed at all since the last time this ran — previously this
137
+ // unconditionally re-adopted `text` and bumped contentRenderRevision on
138
+ // every such refire, forcing a full srcdoc rebuild after nearly every
139
+ // commit. Only touch collab/render state when `text` genuinely differs
140
+ // from what is already reflected.
141
+ if (
142
+ text !== latestActiveContentRef.current ||
143
+ collabContentFileIdRef.current !== fileId
144
+ ) {
145
+ setCollabContent(text);
146
+ setCollabContentFileId(fileId);
147
+ latestActiveContentRef.current = text;
148
+ if (
149
+ previewContentReplaceNeedsRenderFallback(
150
+ replacePreviewContent(text, null, { forceFullDocument: true }),
151
+ )
152
+ ) {
153
+ setContentRenderRevision((revision) => revision + 1);
154
+ }
155
+ }
156
+ }
157
+ }
@@ -0,0 +1,19 @@
1
+ export function externalPreviewUrlForContent(content: string): string | null {
2
+ const trimmed = content.trim();
3
+ if (!/^https?:\/\//i.test(trimmed)) return null;
4
+ try {
5
+ const url = new URL(trimmed);
6
+ url.hash = "";
7
+ return url.toString();
8
+ } catch {
9
+ // coercion-ok: a non-parseable string is "not an external URL", which is
10
+ // exactly what the null return means to every caller here.
11
+ return null;
12
+ }
13
+ }
14
+
15
+ export function fullPreviewHtml(content: string): string {
16
+ const trimmed = content.trim();
17
+ if (/<!doctype html|<html[\s>]/i.test(trimmed)) return content;
18
+ return `<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head><body>${content}</body></html>`;
19
+ }
@@ -1,7 +1,8 @@
1
+ import { withSsrHtmlContentType } from "@agent-native/core/shared";
1
2
  import { redirect } from "react-router";
2
3
 
3
4
  export function loader() {
4
- return redirect("/", 302);
5
+ return withSsrHtmlContentType(redirect("/", 302));
5
6
  }
6
7
 
7
8
  export default function ExamplesRedirect() {
@@ -13,6 +13,8 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
13
13
 
14
14
  [context.production.environment]
15
15
  AGENT_NATIVE_HOSTED_HARNESS = "true"
16
+ AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
17
+ AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
16
18
 
17
19
  # Emit the Netlify background function used by Design's durable agent-chat opt-in.
18
20
  # The normal server timeout still protects non-background HTTP handlers.
@@ -678,7 +678,8 @@ export function stripBoardSurfaceOffsetFromCoord(value: number): number {
678
678
  * Parent-relative position for a node being reparented INTO a target
679
679
  * container, from the two elements' persisted absolute positions in the same
680
680
  * coordinate space. This is the pure seam used by
681
- * `handleOverviewPrimitiveReparent` (DesignEditor.tsx).
681
+ * `handleOverviewPrimitiveReparent`
682
+ * (app/pages/design-editor/commands/overview-primitive-reparent.ts).
682
683
  *
683
684
  * The flat subtraction below is only correct when both `source` and `target`
684
685
  * are already expressed in the SAME coordinate space — historically that
@@ -15,6 +15,8 @@ AGENT_CHAT_DURABLE_BACKGROUND = "true"
15
15
 
16
16
  [context.production.environment]
17
17
  AGENT_NATIVE_HOSTED_HARNESS = "true"
18
+ AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
19
+ AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
18
20
 
19
21
  # A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
20
22
  # The framework does not impose a default agent-run timeout; if a template sets
@@ -1,4 +1,5 @@
1
1
  import { DefaultSpinner } from "@agent-native/core/client/ui";
2
+ import { withSsrHtmlContentType } from "@agent-native/core/shared";
2
3
  import { redirect, type LoaderFunctionArgs } from "react-router";
3
4
 
4
5
  import messages from "@/i18n/en-US";
@@ -11,11 +12,11 @@ function target(url: URL): string {
11
12
  }
12
13
 
13
14
  export function loader({ url }: LoaderFunctionArgs) {
14
- throw redirect(target(url));
15
+ throw withSsrHtmlContentType(redirect(target(url)), { varyByQuery: true });
15
16
  }
16
17
 
17
18
  export function clientLoader({ url }: LoaderFunctionArgs) {
18
- throw redirect(target(url));
19
+ throw withSsrHtmlContentType(redirect(target(url)), { varyByQuery: true });
19
20
  }
20
21
 
21
22
  export function meta() {
@@ -12,6 +12,8 @@ NPM_CONFIG_PRODUCTION = "false"
12
12
 
13
13
  [context.production.environment]
14
14
  AGENT_NATIVE_HOSTED_HARNESS = "true"
15
+ AGENT_NATIVE_ENABLE_KEEP_WARM = "1"
16
+ AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND = "1"
15
17
 
16
18
  # A2A delegation calls run a full agent loop (LLM + tool calls + DB queries).
17
19
  # The framework does not impose a default agent-run timeout; if a template sets