@agent-native/core 0.92.4 → 0.92.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +19 -0
  3. package/corpus/core/docs/content/template-assets.mdx +139 -0
  4. package/corpus/core/package.json +1 -1
  5. package/corpus/core/src/agent/production-agent.ts +96 -6
  6. package/corpus/core/src/cli/design-connect.ts +43 -16
  7. package/corpus/core/src/cli/skills.ts +52 -16
  8. package/corpus/core/src/client/AssistantChat.tsx +127 -15
  9. package/corpus/core/src/client/chat/tool-call-display.tsx +9 -3
  10. package/corpus/core/src/client/org/TeamPage.tsx +274 -177
  11. package/corpus/core/src/client/settings/DemoModeSection.tsx +6 -6
  12. package/corpus/core/src/client/settings/SettingsPanel.tsx +1 -1
  13. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +49 -1
  14. package/corpus/core/src/demo/actions/toggle-demo-mode.ts +2 -2
  15. package/corpus/core/src/demo/config.ts +3 -3
  16. package/corpus/core/src/demo/redact.ts +720 -0
  17. package/corpus/core/src/extensions/url-safety.ts +13 -1
  18. package/corpus/core/src/localization/default-messages.ts +7 -0
  19. package/corpus/core/src/templates/default/app/i18n/ar-SA.ts +7 -0
  20. package/corpus/core/src/templates/default/app/i18n/de-DE.ts +7 -0
  21. package/corpus/core/src/templates/default/app/i18n/en-US.ts +7 -0
  22. package/corpus/core/src/templates/default/app/i18n/es-ES.ts +7 -0
  23. package/corpus/core/src/templates/default/app/i18n/fr-FR.ts +7 -0
  24. package/corpus/core/src/templates/default/app/i18n/hi-IN.ts +7 -0
  25. package/corpus/core/src/templates/default/app/i18n/ja-JP.ts +7 -0
  26. package/corpus/core/src/templates/default/app/i18n/ko-KR.ts +7 -0
  27. package/corpus/core/src/templates/default/app/i18n/pt-BR.ts +7 -0
  28. package/corpus/core/src/templates/default/app/i18n/zh-CN.ts +7 -0
  29. package/corpus/core/src/templates/default/app/i18n/zh-TW.ts +7 -0
  30. package/corpus/templates/analytics/AGENTS.md +6 -3
  31. package/corpus/templates/analytics/changelog/2026-07-09-uptime-monitors-use-scheduled-workers-in-production-serverless.md +6 -0
  32. package/corpus/templates/analytics/docs/uptime-monitoring.md +5 -0
  33. package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +14 -2
  34. package/corpus/templates/assets/.agents/skills/image-generation/SKILL.md +35 -0
  35. package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +19 -0
  36. package/corpus/templates/assets/AGENTS.md +13 -0
  37. package/corpus/templates/assets/actions/_generation-preset-settings.ts +73 -0
  38. package/corpus/templates/assets/actions/_preset-skeleton-validation.ts +63 -0
  39. package/corpus/templates/assets/actions/create-generation-preset.ts +22 -8
  40. package/corpus/templates/assets/actions/duplicate-library.ts +49 -1
  41. package/corpus/templates/assets/actions/generate-image-batch.ts +14 -1
  42. package/corpus/templates/assets/actions/generate-image.ts +195 -26
  43. package/corpus/templates/assets/actions/import-asset-from-url.ts +323 -0
  44. package/corpus/templates/assets/actions/list-generation-presets.ts +1 -1
  45. package/corpus/templates/assets/actions/rerun-generation-run.ts +37 -0
  46. package/corpus/templates/assets/actions/update-generation-preset.ts +19 -1
  47. package/corpus/templates/assets/app/i18n/ar-SA.ts +1 -0
  48. package/corpus/templates/assets/app/i18n/de-DE.ts +1 -0
  49. package/corpus/templates/assets/app/i18n/es-ES.ts +1 -0
  50. package/corpus/templates/assets/app/i18n/fr-FR.ts +1 -0
  51. package/corpus/templates/assets/app/i18n/hi-IN.ts +1 -0
  52. package/corpus/templates/assets/app/i18n/ja-JP.ts +1 -0
  53. package/corpus/templates/assets/app/i18n/ko-KR.ts +1 -0
  54. package/corpus/templates/assets/app/i18n/pt-BR.ts +1 -0
  55. package/corpus/templates/assets/app/i18n/zh-CN.ts +1 -0
  56. package/corpus/templates/assets/app/i18n/zh-TW.ts +41 -0
  57. package/corpus/templates/assets/app/i18n-data.ts +93 -0
  58. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +66 -11
  59. package/corpus/templates/assets/app/routes/brand-kits.$id_.presets.$presetId.tsx +959 -24
  60. package/corpus/templates/assets/changelog/2026-07-07-preset-skeleton-controls-fit-correctly-on-narrower-edit-page.md +6 -0
  61. package/corpus/templates/assets/changelog/2026-07-09-import-reference-images-from-url.md +6 -0
  62. package/corpus/templates/assets/changelog/2026-07-09-preset-reference-board.md +6 -0
  63. package/corpus/templates/assets/server/handlers/assets.ts +7 -27
  64. package/corpus/templates/assets/server/lib/generation.ts +83 -2
  65. package/corpus/templates/assets/server/lib/preset-chat-context.ts +17 -4
  66. package/corpus/templates/assets/server/lib/preset-references.ts +182 -0
  67. package/corpus/templates/assets/server/lib/upload-validation.ts +38 -0
  68. package/corpus/templates/assets/shared/api.ts +20 -0
  69. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +6 -0
  70. package/corpus/templates/calendar/actions/create-event.ts +8 -2
  71. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +22 -7
  72. package/corpus/templates/calendar/changelog/2026-07-09-calendar-events-with-guests-now-automatically-get-a-google-m.md +6 -0
  73. package/corpus/templates/calendar/changelog/2026-07-09-new-events-now-mark-the-creator-rsvp-as-yes.md +6 -0
  74. package/corpus/templates/calendar/server/handlers/events.ts +9 -2
  75. package/corpus/templates/calendar/server/lib/event-video-conferencing.ts +58 -0
  76. package/corpus/templates/calendar/server/lib/google-calendar.ts +1 -0
  77. package/corpus/templates/clips/changelog/2026-07-09-meeting-reminder-popovers-can-now-be-dismissed-with-a-top-le.md +6 -0
  78. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +3 -1
  79. package/corpus/templates/clips/desktop/src/styles.css +13 -9
  80. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +5 -2
  81. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +27 -15
  82. package/corpus/templates/design/actions/connect-localhost.ts +40 -11
  83. package/corpus/templates/design/actions/open-visual-edit.ts +7 -1
  84. package/dist/agent/production-agent.d.ts +34 -1
  85. package/dist/agent/production-agent.d.ts.map +1 -1
  86. package/dist/agent/production-agent.js +58 -5
  87. package/dist/agent/production-agent.js.map +1 -1
  88. package/dist/cli/design-connect.d.ts +5 -1
  89. package/dist/cli/design-connect.d.ts.map +1 -1
  90. package/dist/cli/design-connect.js +37 -16
  91. package/dist/cli/design-connect.js.map +1 -1
  92. package/dist/cli/skills.d.ts.map +1 -1
  93. package/dist/cli/skills.js +52 -16
  94. package/dist/cli/skills.js.map +1 -1
  95. package/dist/client/AssistantChat.d.ts +3 -1
  96. package/dist/client/AssistantChat.d.ts.map +1 -1
  97. package/dist/client/AssistantChat.js +96 -15
  98. package/dist/client/AssistantChat.js.map +1 -1
  99. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  100. package/dist/client/chat/tool-call-display.js +5 -1
  101. package/dist/client/chat/tool-call-display.js.map +1 -1
  102. package/dist/client/org/TeamPage.d.ts.map +1 -1
  103. package/dist/client/org/TeamPage.js +32 -13
  104. package/dist/client/org/TeamPage.js.map +1 -1
  105. package/dist/client/settings/DemoModeSection.d.ts +3 -3
  106. package/dist/client/settings/DemoModeSection.js +4 -4
  107. package/dist/client/settings/DemoModeSection.js.map +1 -1
  108. package/dist/client/settings/SettingsPanel.js +1 -1
  109. package/dist/client/settings/SettingsPanel.js.map +1 -1
  110. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  111. package/dist/client/settings/SettingsTabsPage.js +38 -2
  112. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  113. package/dist/collab/routes.d.ts +1 -1
  114. package/dist/collab/struct-routes.d.ts +1 -1
  115. package/dist/demo/actions/toggle-demo-mode.js +2 -2
  116. package/dist/demo/actions/toggle-demo-mode.js.map +1 -1
  117. package/dist/demo/config.js +3 -3
  118. package/dist/demo/config.js.map +1 -1
  119. package/dist/demo/redact.d.ts +1 -1
  120. package/dist/demo/redact.d.ts.map +1 -1
  121. package/dist/demo/redact.js +0 -0
  122. package/dist/demo/redact.js.map +1 -1
  123. package/dist/extensions/url-safety.d.ts +5 -0
  124. package/dist/extensions/url-safety.d.ts.map +1 -1
  125. package/dist/extensions/url-safety.js +10 -0
  126. package/dist/extensions/url-safety.js.map +1 -1
  127. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  128. package/dist/localization/default-messages.d.ts +7 -0
  129. package/dist/localization/default-messages.d.ts.map +1 -1
  130. package/dist/localization/default-messages.js +7 -0
  131. package/dist/localization/default-messages.js.map +1 -1
  132. package/dist/notifications/routes.d.ts +2 -2
  133. package/dist/observability/routes.d.ts +3 -3
  134. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  135. package/dist/resources/handlers.d.ts +1 -1
  136. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  137. package/dist/templates/default/app/i18n/ar-SA.ts +7 -0
  138. package/dist/templates/default/app/i18n/de-DE.ts +7 -0
  139. package/dist/templates/default/app/i18n/en-US.ts +7 -0
  140. package/dist/templates/default/app/i18n/es-ES.ts +7 -0
  141. package/dist/templates/default/app/i18n/fr-FR.ts +7 -0
  142. package/dist/templates/default/app/i18n/hi-IN.ts +7 -0
  143. package/dist/templates/default/app/i18n/ja-JP.ts +7 -0
  144. package/dist/templates/default/app/i18n/ko-KR.ts +7 -0
  145. package/dist/templates/default/app/i18n/pt-BR.ts +7 -0
  146. package/dist/templates/default/app/i18n/zh-CN.ts +7 -0
  147. package/dist/templates/default/app/i18n/zh-TW.ts +7 -0
  148. package/docs/content/template-assets.mdx +139 -0
  149. package/package.json +1 -1
  150. package/src/templates/default/app/i18n/ar-SA.ts +7 -0
  151. package/src/templates/default/app/i18n/de-DE.ts +7 -0
  152. package/src/templates/default/app/i18n/en-US.ts +7 -0
  153. package/src/templates/default/app/i18n/es-ES.ts +7 -0
  154. package/src/templates/default/app/i18n/fr-FR.ts +7 -0
  155. package/src/templates/default/app/i18n/hi-IN.ts +7 -0
  156. package/src/templates/default/app/i18n/ja-JP.ts +7 -0
  157. package/src/templates/default/app/i18n/ko-KR.ts +7 -0
  158. package/src/templates/default/app/i18n/pt-BR.ts +7 -0
  159. package/src/templates/default/app/i18n/zh-CN.ts +7 -0
  160. package/src/templates/default/app/i18n/zh-TW.ts +7 -0
package/corpus/README.md CHANGED
@@ -27,5 +27,5 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
27
27
 
28
28
  ## Generated Counts
29
29
 
30
- - core files: 2154
31
- - template files: 5047
30
+ - core files: 2155
31
+ - template files: 5057
@@ -1,5 +1,24 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.92.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 5f187db: Highlight run-code source snippets in chat and keep raw tool output popovers within the available viewport.
8
+ - 5f187db: Fix duplicate in-progress chat output during reconnect handoff so streaming text and tool calls render once while the live assistant message catches up. Also stop a reconnect tool spinner from rendering a second card beside its live pending tool card when their reader-local and server-scoped ids don't match, in every reconnect/handoff window.
9
+ - 5f187db: Focus the settings search field automatically when desktop users open settings.
10
+ - 5f187db: Improve organization settings so invites stay above the fold and members appear in a separate table card.
11
+ - 6a29ba0: `ssrfSafeFetch` hardening: a new `httpsOnly` option validates the URL scheme on the initial request and on every redirect hop, so HTTPS-only callers cannot be downgraded to plain HTTP by a redirect from the untrusted origin. Followed redirect responses now also have their bodies cancelled so each hop's connection is released immediately instead of being held until GC.
12
+ - 5f187db: Remove the divider between settings tabs and their tab content.
13
+ - 5f187db: Preserve dashboard, chart, and label names in demo mode while continuing to anonymize free-text/contact names, emails, and numbers.
14
+ - c32c174: add preset-driven-flow to asset template docs
15
+
16
+ ## 0.92.5
17
+
18
+ ### Patch Changes
19
+
20
+ - eed1710: Fix `/visual-edit` live-edit 401s. The design server now mints the bridge token in `connect-localhost` and returns it from `open-visual-edit`, and `design connect` adopts it via a new `--bridge-token` flag (or `AGENT_NATIVE_BRIDGE_TOKEN`) instead of minting its own. The local bridge and the user's connection row now share the secret without the CLI needing its own auth to self-register — which was impossible under OAuth-based MCP and was the root cause of the 401s. The `visual-edit` skill is updated to call `open-visual-edit` first and start the bridge with the returned token.
21
+
3
22
  ## 0.92.4
4
23
 
5
24
  ### Patch Changes
@@ -102,6 +102,145 @@ Live demo: [assets.agent-native.com](https://assets.agent-native.com).
102
102
  4. **Use the asset elsewhere.** Copy the export, embed the picker in another
103
103
  app, or let another agent call Assets over A2A.
104
104
 
105
+ ## The preset-driven workflow
106
+
107
+ Assets produces its best, most repeatable results when work is split into two
108
+ jobs that need very different skills:
109
+
110
+ 1. **Designing the recipe** — slow, craft-heavy, done once per use case. A
111
+ designer works with the agent to build a **generation preset**: the rules,
112
+ references, locked-in model, and a prompt template that has been QA'd until
113
+ it reliably produces on-brand output.
114
+ 2. **Running the recipe** — fast and repeatable. A marketer pastes a campaign
115
+ brief, tags the preset, and gets a finished, brand-safe asset without
116
+ touching model settings, aspect ratios, masks, or prompt wording.
117
+
118
+ A preset is the contract between these two people. The designer encodes taste
119
+ and technical judgment once; everyone else inherits it on every generation.
120
+ Think of presets as the design system for AI-generated assets: a raw prompt is
121
+ a throwaway that drifts every time it is retyped, while a preset is durable,
122
+ shared, and carries the decisions (model, aspect ratio, text and reference
123
+ rules, logo handling, fixed layout) that actually determine whether the output
124
+ is usable.
125
+
126
+ ### Designers: build one preset per use case
127
+
128
+ Create a separate preset for each repeatable format rather than one generic
129
+ "social post" preset that tries to do everything. Specific presets give the
130
+ model clearer rules and produce better output. Good examples:
131
+
132
+ - LinkedIn ad
133
+ - Webinar announcement
134
+ - Event speaker card
135
+ - Podcast guest card
136
+ - Customer story quote card
137
+ - Blog hero
138
+ - Blog body diagram
139
+
140
+ The primary way to build one is **conversationally**: describe the use case to
141
+ the agent and let it create the preset and fill in sensible defaults — model,
142
+ aspect ratio and size, a first prompt template, and text/reference/logo
143
+ policies — then refine in the same conversation ("make it darker", "tighten the
144
+ text policy", "try a square variant"). Because that reasoning shows up directly
145
+ in the defaults the agent picks, run this design conversation on the strongest
146
+ available model.
147
+
148
+ Each preset locks down:
149
+
150
+ | Setting | What it controls |
151
+ | ------------------- | -------------------------------------------------------------------------------------------------------------------- |
152
+ | Prompt template | The QA'd creative recipe, with slots for the campaign brief |
153
+ | Model | The right image engine for this job (see below) |
154
+ | Aspect ratio + size | Output format for the target channel |
155
+ | Text policy | Whether and how literal copy is rendered vs suppressed |
156
+ | Reference policy | Which brand references anchor the look |
157
+ | Reference board | Named fixed or per-run visual references, such as a usual host, guest speaker, product, backdrop, or composition cue |
158
+ | Logo | Composites the library's canonical logo pixel-perfect after generation, so the model never redraws it |
159
+ | Skeleton / mask | A fixed brand plate the subject is composited or inpainted into, for locked layouts |
160
+
161
+ ### Choosing the model per preset
162
+
163
+ Pick the model in the preset so downstream users never have to:
164
+
165
+ - **A general high-quality image model** for hero images, rich scenes, and
166
+ complex compositions, when you need broad aspect-ratio flexibility.
167
+ - **A fast image model** for high-volume social where turnaround matters more
168
+ than maximum polish.
169
+ - **gpt-image-2** for customer-facing, text-heavy, and identity-sensitive work.
170
+ It renders crisp literal copy inside the image, and it is the engine used for
171
+ **mask inpainting** into a fixed layout.
172
+ - **gpt-image-1** for transparent **cutout** subjects that are composited onto a
173
+ brand plate afterward.
174
+
175
+ When a use case has a **fixed brand frame** that must survive every generation
176
+ (a webinar template with a locked header and logo, an ad plate with a reserved
177
+ product area), build a preset skeleton: upload the background plate, choose
178
+ gpt-image-2, and optionally add a same-size mask. **Opaque areas are preserved,
179
+ transparent areas are editable**, so the logo, framing, and existing copy come
180
+ back untouched while only the art region changes. Note that gpt-image edits are
181
+ guided semantic rewrites, not pixel-only patches, so the prompt should still
182
+ describe the whole intended image and restate what must not change.
183
+
184
+ Use the **reference board** when a preset needs annotated visual inputs that
185
+ ride along with each run. Each board entry names one thing — a person, product,
186
+ style, background, or composition example — and can pin up to four images plus a
187
+ plain-language description. Fixed entries attach automatically. Variable
188
+ entries can be replaced per run with `presetReferenceFills`, and required
189
+ variable entries block generation until the agent supplies images from user
190
+ attachments or the library.
191
+
192
+ For example, a Webinar Announcement preset might use a skeleton plate and mask
193
+ for the locked event chrome, a fixed "Steve" subject entry for the usual host,
194
+ and a required variable "Guest speaker" entry for each event. Use a
195
+ `background` entry for setting context. Use a `composition` entry for layout
196
+ inspiration from a past poster; exact placement belongs in the skeleton plate,
197
+ not a reference image.
198
+
199
+ ### QA the prompt until it is reliable
200
+
201
+ The model, references, and skeleton set the stage, but the prompt template is
202
+ what turns a brief into a consistently good asset. Have the agent generate
203
+ several candidates against realistic briefs and tighten the template until it
204
+ holds. Techniques worth baking in:
205
+
206
+ - **Structure the prompt in a fixed order**: scene, subject, key details,
207
+ constraints. Name the intended use so the model picks the right polish level.
208
+ - **For literal copy, be surgical**: put exact text in quotes or ALL CAPS,
209
+ specify font, size, color, and placement, and for tricky brand names spell
210
+ them out. Ask for the text exactly once with no extra characters.
211
+ - **For skeleton or inpainting presets, restate the invariants every run**:
212
+ "change only the center region, preserve the header, logo, and framing."
213
+
214
+ Before handing a preset to the wider team, run realistic QA generations
215
+ (including briefs you did not write) and confirm it: follows the use case,
216
+ respects the mask, preserves fixed text and logos, avoids inventing extra
217
+ headlines or badges, uses references correctly, places the subject in the right
218
+ region, handles short and long briefs, and still holds up after a refinement.
219
+
220
+ ### Everyone else: paste the brief, tag the preset, generate
221
+
222
+ Once a preset exists, running it is three moves: paste the campaign brief, tag
223
+ the preset with `@`, and generate. The tagged preset injects its full aesthetic
224
+ brief into the agent, and the saved model, format, tier, logo, and skeleton all
225
+ apply automatically.
226
+
227
+ ```text
228
+ Create a LinkedIn ad for our webinar on AI landing pages.
229
+ Audience: B2B marketing teams.
230
+ Message: launch campaign pages faster while staying on brand.
231
+ CTA: Register now.
232
+
233
+ @Webinar Announcement
234
+ ```
235
+
236
+ No model picking, no aspect-ratio math, no mask files, no prompt engineering.
237
+ Rule of thumb: designers own the preset, everyone else owns the brief. If a
238
+ user finds themselves choosing models, masks, or layout rules during normal
239
+ use, the preset is not specific enough yet.
240
+
241
+ For deeper prompting guidance on the underlying models, see OpenAI's
242
+ [GPT Image prompting guide](https://developers.openai.com/cookbook/examples/multimodal/image-gen-models-prompting-guide).
243
+
105
244
  ## Useful prompts
106
245
 
107
246
  - "Generate three blog hero options using the Acme product references."
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.92.4",
3
+ "version": "0.92.6",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -4193,7 +4193,7 @@ export async function runAgentLoop(opts: {
4193
4193
  // `_agentImages` result field (stripped from the JSON the model
4194
4194
  // reads, even in demo mode). Demo mode drops the images themselves —
4195
4195
  // text redaction can't scrub pixels, and a screenshot would leak the
4196
- // real names/emails the redaction exists to hide. The images array
4196
+ // real contact names/emails/numbers the redaction exists to hide. The images array
4197
4197
  // never touches the ledger — only the compact text notes appended
4198
4198
  // below are persisted.
4199
4199
  let imageNotes: string[] = [];
@@ -4664,7 +4664,40 @@ export function backgroundContinuationReasonForRun(
4664
4664
  }
4665
4665
 
4666
4666
  export async function runAgentLoopWithMainChatInternalContinuations(
4667
- opts: Parameters<typeof runAgentLoop>[0],
4667
+ opts: Parameters<typeof runAgentLoop>[0] & {
4668
+ /**
4669
+ * Opt-in: also catch a thrown `isResumableEngineError` (gateway drop /
4670
+ * transport interruption surviving engine retries) and retry it in-process
4671
+ * — the same "continue from where you left off" treatment already applied
4672
+ * to in-loop `auto_continue` events below — instead of letting it
4673
+ * propagate out to the caller. Default false/omitted preserves the exact
4674
+ * prior behavior: such an error is NOT caught here and propagates to
4675
+ * `startRun`'s outer catch, which marks the run "errored" and (for a
4676
+ * background worker) hands off to `chainServerDrivenContinuation` for a
4677
+ * fresh invocation.
4678
+ *
4679
+ * Only safe to enable when the CALLER is a proven durable-background
4680
+ * worker (`isInBackgroundFunctionRuntime()`): such a worker has minutes of
4681
+ * remaining budget on this same invocation, so resuming in-process avoids
4682
+ * a self-dispatch entirely — critically, it avoids the bg-function
4683
+ * self-dispatch 404 (a background function cannot invoke its own public
4684
+ * URL from inside a live invocation) that this same recoverable-error
4685
+ * path would otherwise hit via `chainServerDrivenContinuation`. A
4686
+ * foreground turn (~40s budget) must NOT set this — it keeps depending on
4687
+ * the existing cross-invocation recovery (foreground self-chain or the
4688
+ * client's `auto_continue` re-POST), unchanged.
4689
+ */
4690
+ resumeResumableErrorsInProcess?: boolean;
4691
+ /**
4692
+ * Override for the per-invocation continuation attempt cap. Defaults to
4693
+ * `MAIN_CHAT_INTERNAL_CONTINUATION_LIMIT` (6) when omitted — unchanged for
4694
+ * every existing caller. A proven background-function worker (15-min
4695
+ * budget) can pass a higher cap (e.g. `MAX_BACKGROUND_RUN_CONTINUATIONS`)
4696
+ * so a run that needs several resumable-error recoveries within one
4697
+ * 13-minute chunk isn't cut off at the foreground-sized limit.
4698
+ */
4699
+ maxContinuations?: number;
4700
+ },
4668
4701
  ): Promise<Awaited<ReturnType<typeof runAgentLoop>>> {
4669
4702
  const usage: Awaited<ReturnType<typeof runAgentLoop>> = {
4670
4703
  inputTokens: 0,
@@ -4681,11 +4714,18 @@ export async function runAgentLoopWithMainChatInternalContinuations(
4681
4714
  usage.model = next.model;
4682
4715
  };
4683
4716
 
4717
+ const resumeResumableErrorsInProcess =
4718
+ opts.resumeResumableErrorsInProcess === true;
4719
+ const maxContinuations =
4720
+ typeof opts.maxContinuations === "number" && opts.maxContinuations > 0
4721
+ ? opts.maxContinuations
4722
+ : MAIN_CHAT_INTERNAL_CONTINUATION_LIMIT;
4723
+
4684
4724
  const localTurnEvents: AgentChatEvent[] = [];
4685
4725
  let lastAttemptWasUnfinishedContinuation = false;
4686
4726
  for (
4687
4727
  let attempt = 0;
4688
- !opts.signal.aborted && attempt < MAIN_CHAT_INTERNAL_CONTINUATION_LIMIT;
4728
+ !opts.signal.aborted && attempt < maxContinuations;
4689
4729
  attempt++
4690
4730
  ) {
4691
4731
  lastAttemptWasUnfinishedContinuation = false;
@@ -4703,8 +4743,23 @@ export async function runAgentLoopWithMainChatInternalContinuations(
4703
4743
  opts.send(event);
4704
4744
  };
4705
4745
 
4706
- const nextUsage = await runAgentLoop({ ...opts, send });
4707
- addUsage(nextUsage);
4746
+ try {
4747
+ const nextUsage = await runAgentLoop({ ...opts, send });
4748
+ addUsage(nextUsage);
4749
+ } catch (err) {
4750
+ // Preserve exact prior behavior unless explicitly opted in: an aborted
4751
+ // signal (real soft-timeout / user stop) or a non-resumable error is
4752
+ // always rethrown, and when `resumeResumableErrorsInProcess` is not
4753
+ // set, EVERY thrown error is rethrown exactly as before.
4754
+ if (
4755
+ !resumeResumableErrorsInProcess ||
4756
+ opts.signal.aborted ||
4757
+ !isResumableEngineError(err)
4758
+ ) {
4759
+ throw err;
4760
+ }
4761
+ continuationReason = continuationReasonForResumableError(err);
4762
+ }
4708
4763
 
4709
4764
  if (!continuationReason || opts.signal.aborted) {
4710
4765
  return usage;
@@ -6525,10 +6580,25 @@ export function createProductionAgentHandler(
6525
6580
  // chaining via the regular function — the Netlify
6526
6581
  // `-background` function is only emitted into the deploy
6527
6582
  // output when the durable flag is on.
6583
+ //
6584
+ // `&& !runsInBackgroundFunction`: a worker PROVEN to already
6585
+ // be executing inside the real `-background` function must
6586
+ // never dispatch back to that same function's own URL — a
6587
+ // background function invoking itself by URL from inside a
6588
+ // live invocation is a documented Netlify platform
6589
+ // limitation (404), unlike the initial foreground→background
6590
+ // dispatch or a worker that landed on the regular function
6591
+ // (a genuinely different function calling the background
6592
+ // one, which works). This only changes the dispatch TARGET
6593
+ // for that one proven-in-bg-function case — by this point
6594
+ // (with the in-process resumable-error resume above) it is
6595
+ // reached only when the chunk genuinely exhausted its
6596
+ // budget, so falling back to the regular `_process-run`
6597
+ // function (40s clamp) here is the correct, safe target.
6528
6598
  chainViaDurableBackground:
6529
6599
  isAgentChatDurableBackgroundEnabled({
6530
6600
  appOptIn: options.durableBackgroundRuns === true,
6531
- }),
6601
+ }) && !runsInBackgroundFunction,
6532
6602
  });
6533
6603
  }
6534
6604
  } finally {
@@ -6946,6 +7016,26 @@ export function createProductionAgentHandler(
6946
7016
  .slice(0, 200),
6947
7017
  }
6948
7018
  : {}),
7019
+ // A worker PROVEN to be running inside the real 15-min Netlify
7020
+ // `-background` function (`runsInBackgroundFunction`) has minutes of
7021
+ // budget left on THIS invocation. Let it catch a recoverable
7022
+ // transport/gateway error (isResumableEngineError) and resume the
7023
+ // agent loop in-process instead of unwinding out to `startRun`'s
7024
+ // outer catch, which would otherwise hand off to
7025
+ // `chainServerDrivenContinuation` — and for a worker already inside
7026
+ // the background function, that hand-off is a same-function
7027
+ // self-dispatch that 404s on Netlify (a background function cannot
7028
+ // invoke its own public URL from inside a live invocation). A
7029
+ // foreground turn or a worker that landed on the regular ~60s
7030
+ // function (runsInBackgroundFunction false) must NOT set this — it
7031
+ // keeps depending on the existing cross-invocation recovery
7032
+ // (foreground self-chain / client auto_continue re-POST), unchanged.
7033
+ ...(runsInBackgroundFunction
7034
+ ? {
7035
+ resumeResumableErrorsInProcess: true,
7036
+ maxContinuations: MAX_BACKGROUND_RUN_CONTINUATIONS,
7037
+ }
7038
+ : {}),
6949
7039
  };
6950
7040
 
6951
7041
  send({ type: "activity", label: "Contacting model" });
@@ -60,6 +60,10 @@ export interface DesignConnectArgs {
60
60
  * are present) the CLI POSTs to `/_agent-native/actions/connect-localhost`
61
61
  * with the real bridge token so the server can store it for grant minting. */
62
62
  appUrl?: string;
63
+ /** Server-minted bridge token to adopt instead of minting one, so the bridge
64
+ * matches the token already stored on the user's connection row (no
65
+ * self-registration). Also read from AGENT_NATIVE_BRIDGE_TOKEN. */
66
+ bridgeToken?: string;
63
67
  json: boolean;
64
68
  once: boolean;
65
69
  dryRun: boolean;
@@ -174,6 +178,11 @@ export function parseDesignConnectArgs(argv: string[]): DesignConnectArgs {
174
178
  index += 1;
175
179
  } else if (arg.startsWith("--app-url=")) {
176
180
  parsed.appUrl = arg.slice("--app-url=".length);
181
+ } else if (arg === "--bridge-token") {
182
+ parsed.bridgeToken = stringFlagValue(args, index, arg);
183
+ index += 1;
184
+ } else if (arg.startsWith("--bridge-token=")) {
185
+ parsed.bridgeToken = arg.slice("--bridge-token=".length);
177
186
  } else if (arg === "--json") {
178
187
  parsed.json = true;
179
188
  parsed.once = true;
@@ -1215,12 +1224,16 @@ async function walkBridgeFiles(rootPath: string): Promise<ListFilesResult> {
1215
1224
 
1216
1225
  export async function startDesignConnectBridge(
1217
1226
  manifest: DesignConnectManifest,
1227
+ seedToken?: string,
1218
1228
  ): Promise<DesignConnectBridge> {
1219
- // Mint a cryptographically random per-rootPath bridge token. This token is
1220
- // kept in-process only and is never emitted via the public GET routes so that
1221
- // an unauthenticated caller cannot read it. The server-side grant action
1222
- // obtains it out-of-band (via the exported bridge reference).
1223
- const bridgeToken = crypto.randomBytes(32).toString("hex");
1229
+ // Shared secret the browser sends (x-bridge-token) to unlock live-edit/read/
1230
+ // write. Bridge and the user's connection row must agree on it. Adopt a
1231
+ // server-minted seed when given (MCP flow); otherwise mint one and rely on
1232
+ // --app-url self-registration to push it up. Kept in-process, never served.
1233
+ const bridgeToken =
1234
+ seedToken ||
1235
+ process.env["AGENT_NATIVE_BRIDGE_TOKEN"] ||
1236
+ crypto.randomBytes(32).toString("hex");
1224
1237
  let liveEditBridgeScript = "";
1225
1238
 
1226
1239
  const server = http.createServer(
@@ -1795,6 +1808,12 @@ Options:
1795
1808
  --route-manifest <path> Non-destructive route manifest output path
1796
1809
  --app-url <url> Deployed design app URL for self-registration
1797
1810
  (also reads AGENT_NATIVE_URL / DESIGN_APP_URL env)
1811
+ --bridge-token <token> Adopt a bridge token minted server-side by the
1812
+ authenticated connect-localhost / open-visual-edit
1813
+ action instead of minting one. Used by the remote-MCP
1814
+ /visual-edit flow so the bridge and the user's stored
1815
+ connection token match with no self-registration.
1816
+ (also reads AGENT_NATIVE_BRIDGE_TOKEN env)
1798
1817
  --daemon Start the bridge detached, wait for /health, then exit
1799
1818
  --json Print the manifest JSON and exit
1800
1819
  --once Prepare/scaffold the manifest and exit
@@ -1931,25 +1950,33 @@ export async function runDesign(argv: string[]) {
1931
1950
  return 0;
1932
1951
  }
1933
1952
 
1934
- const bridge = await startDesignConnectBridge(manifest);
1953
+ const seedToken =
1954
+ parsed.bridgeToken || process.env["AGENT_NATIVE_BRIDGE_TOKEN"] || undefined;
1955
+ const bridge = await startDesignConnectBridge(manifest, seedToken);
1935
1956
  console.error("Design localhost bridge running");
1936
1957
  console.error(`Bridge: ${manifest.bridgeUrl}`);
1937
1958
  console.error(`Manifest: ${manifest.bridgeUrl}/manifest.json`);
1938
1959
  console.error(`Routes: ${manifest.routeCount}`);
1939
1960
  console.error(`Dev URL: ${manifest.devServerUrl}`);
1940
1961
 
1941
- // Self-register with the design app server (best-effort): POST the bridge
1942
- // token to connect-localhost so the server row stores the real token. Without
1943
- // it, the bridge and server tokens diverge and every write returns 401.
1944
- const appUrl = resolveAppUrl(parsed.appUrl);
1945
- if (appUrl) {
1946
- void registerConnectionWithServer(appUrl, bridge, resolveAuthToken());
1947
- } else {
1948
- // No app URL means the token never reaches the DB — surface it instead of
1949
- // silently skipping, since live-edit will then 401.
1962
+ if (seedToken) {
1963
+ // Server already stored this token on the row; bridge matches it, so no
1964
+ // self-registration needed. Zero-config path for the remote-MCP flow.
1950
1965
  console.error(
1951
- "[design connect] No app URL resolved (pass --app-url or set AGENT_NATIVE_URL); skipping self-registration — live-edit will fail to authorize.",
1966
+ "[design connect] Using server-provided bridge token; skipping self-registration.",
1952
1967
  );
1968
+ } else {
1969
+ // No seed: fall back to self-registration — POST the minted token to
1970
+ // connect-localhost. Needs an auth token in env or it 401s (the old gap).
1971
+ const appUrl = resolveAppUrl(parsed.appUrl);
1972
+ if (appUrl) {
1973
+ void registerConnectionWithServer(appUrl, bridge, resolveAuthToken());
1974
+ } else {
1975
+ // No token source at all — warn rather than 401 silently at edit time.
1976
+ console.error(
1977
+ "[design connect] No bridge token or app URL resolved (pass --bridge-token, or --app-url / AGENT_NATIVE_URL); skipping self-registration — live-edit will fail to authorize.",
1978
+ );
1979
+ }
1953
1980
  }
1954
1981
 
1955
1982
  return await new Promise<number>((resolve) => {
@@ -486,34 +486,66 @@ iframe-backed screens on the infinite canvas.
486
486
 
487
487
  ## Required Local Bridge
488
488
 
489
- From the target app repo, make sure its dev server is running, then run:
489
+ The live-edit bridge is unlocked by a shared secret (the "bridge token") that
490
+ must match on two sides: the local bridge process, and the user's connection row
491
+ in Design (which the browser reads to authorize \`/live-edit-bridge\`,
492
+ \`/read-file\`, \`/write-file\`). Get them to match by letting the
493
+ **authenticated** \`open-visual-edit\` action mint the token, then starting the
494
+ bridge with it. This is the only ordering that works for the remote-MCP flow —
495
+ the bridge cannot push its own token to the server without a CLI auth token, so
496
+ the server mints instead and the bridge adopts.
497
+
498
+ From the target app repo, make sure its dev server is running, then:
499
+
500
+ **1. Discover routes without starting a durable bridge** (one-shot, exits):
490
501
 
491
502
  \`\`\`bash
492
- npx @agent-native/core@latest design connect --url http://localhost:5173 --root . --daemon
503
+ npx @agent-native/core@latest design connect --url http://localhost:5173 --root . --json
493
504
  \`\`\`
494
505
 
495
- Use the app's real port. The command starts a detached local bridge on
496
- \`http://127.0.0.1:7331\` by default, waits for \`/health\`, prints the
497
- manifest JSON, and keeps the bridge alive after the agent command exits.
506
+ This prints the manifest (routes + capabilities). Parse it to build
507
+ \`routeManifest\` for the next step. (Skip this if the user already gave explicit
508
+ paths/URLs to place.)
509
+
510
+ **2. Call \`open-visual-edit\`** (see Action Flow below) with NO \`bridgeToken\`.
511
+ The server mints one, stores it on the user's connection row, copies it into the
512
+ placed screens' metadata, and returns it to you as \`bridgeToken\`. Capture it.
498
513
 
499
- For a manual health/manifest check:
514
+ **3. Start the persistent bridge adopting that token** (single line; prefer the
515
+ env var so the secret does not appear in \`ps\`):
516
+
517
+ \`\`\`bash
518
+ AGENT_NATIVE_BRIDGE_TOKEN="<bridgeToken from step 2>" npx @agent-native/core@latest design connect --url http://localhost:5173 --root . --daemon
519
+ \`\`\`
520
+
521
+ (Equivalently, pass \`--bridge-token <token>\`.) This starts a detached bridge on
522
+ \`http://127.0.0.1:7331\`, adopts the server-minted token — so bridge and row
523
+ agree and live-edit authorizes with no self-registration — and stays alive after
524
+ the command exits.
525
+
526
+ For a manual health/manifest check on the running bridge:
500
527
 
501
528
  \`\`\`bash
502
529
  curl http://127.0.0.1:7331/manifest.json
503
530
  \`\`\`
504
531
 
505
- Do not use \`--json\` for an editable session. \`--json\`, \`--once\`, and
506
- \`--dry-run\` print the manifest and exit, so Design will fall back to a
507
- non-editable live iframe as soon as it tries to refresh the snapshot.
532
+ Only use \`--json\` for the step-1 route probe. Never use \`--json\`, \`--once\`,
533
+ or \`--dry-run\` for the durable step-3 bridge: they print the manifest and exit,
534
+ so Design falls back to a non-editable live iframe.
508
535
 
509
536
  ## Action Flow
510
537
 
511
538
  Prefer the single authenticated \`open-visual-edit\` action. It registers or
512
- refreshes the localhost bridge, creates or reuses a Design project, places
513
- URL-backed screens, stores the active visual-edit context, and navigates to
514
- overview mode in one call. This avoids creating a private design under a
515
- synthetic CLI user and then handing the browser a tokenized URL that may be
516
- shadowed by an existing session.
539
+ refreshes the localhost bridge connection, mints and stores the bridge token,
540
+ creates or reuses a Design project, places URL-backed screens, stores the active
541
+ visual-edit context, and navigates to overview mode in one call. This avoids
542
+ creating a private design under a synthetic CLI user and then handing the browser
543
+ a tokenized URL that may be shadowed by an existing session.
544
+
545
+ Call it BEFORE starting the durable bridge (step 3 above): it does not contact
546
+ the bridge, so the bridge need not be running yet, and you need its returned
547
+ \`bridgeToken\` to start the bridge with a matching secret. Omit \`bridgeToken\`
548
+ on the call so the server mints one.
517
549
 
518
550
  \`\`\`bash
519
551
  pnpm action open-visual-edit '{
@@ -526,8 +558,12 @@ pnpm action open-visual-edit '{
526
558
  }'
527
559
  \`\`\`
528
560
 
529
- The action returns \`designId\`, \`connectionId\`, \`screens\`, \`urlPath\`, and
530
- \`openUrl\`. Keep those IDs in the chat context for follow-ups.
561
+ The action returns \`designId\`, \`connectionId\`, \`bridgeToken\`, \`screens\`,
562
+ \`urlPath\`, and \`openUrl\`. Keep \`designId\`/\`connectionId\` in the chat context
563
+ for follow-ups, and pass \`bridgeToken\` to \`design connect\` (step 3) to start
564
+ the bridge. On follow-up calls reusing an existing \`connectionId\`, the same
565
+ token is returned (it is minted once and reused), so the running bridge stays
566
+ valid.
531
567
 
532
568
  For a numbered flow the user describes in chat, keep the labels and order:
533
569