@agent-native/core 0.92.10 → 0.92.12

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 (354) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +29 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/production-agent.ts +214 -17
  5. package/corpus/core/src/agent/run-manager.ts +72 -14
  6. package/corpus/core/src/agent/run-store.ts +152 -27
  7. package/corpus/core/src/cli/design-connect.ts +433 -151
  8. package/corpus/core/src/cli/skills.ts +14 -0
  9. package/corpus/core/src/client/AssistantChat.tsx +25 -0
  10. package/corpus/core/src/client/MultiTabAssistantChat.tsx +40 -6
  11. package/corpus/core/src/client/agent-chat-adapter.ts +56 -12
  12. package/corpus/core/src/client/agent-chat.ts +165 -1
  13. package/corpus/core/src/client/chat/index.ts +9 -1
  14. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +38 -2
  15. package/corpus/core/src/client/index.ts +5 -0
  16. package/corpus/core/src/client/rich-markdown-editor/useCollabReconcile.ts +18 -7
  17. package/corpus/core/src/collab/awareness-store.ts +23 -12
  18. package/corpus/core/src/collab/client.ts +16 -5
  19. package/corpus/core/src/collab/index.ts +4 -0
  20. package/corpus/core/src/collab/recent-edits.ts +65 -2
  21. package/corpus/core/src/index.browser.ts +1 -0
  22. package/corpus/core/src/server/agent-chat-plugin.ts +98 -11
  23. package/corpus/core/src/server/builder-design-systems.ts +30 -4
  24. package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +1 -1
  25. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +12 -6
  26. package/corpus/templates/clips/AGENTS.md +4 -0
  27. package/corpus/templates/clips/actions/create-recording.ts +2 -0
  28. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +5 -3
  29. package/corpus/templates/clips/actions/request-transcript.ts +102 -33
  30. package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -0
  31. package/corpus/templates/clips/app/components/player/share-dialog.tsx +59 -19
  32. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +75 -9
  33. package/corpus/templates/clips/app/components/player/video-player.tsx +17 -12
  34. package/corpus/templates/clips/app/hooks/use-player-shortcuts.ts +1 -0
  35. package/corpus/templates/clips/app/i18n/ar-SA.ts +1 -0
  36. package/corpus/templates/clips/app/i18n/de-DE.ts +1 -0
  37. package/corpus/templates/clips/app/i18n/en-US.ts +1 -0
  38. package/corpus/templates/clips/app/i18n/es-ES.ts +1 -0
  39. package/corpus/templates/clips/app/i18n/fr-FR.ts +1 -0
  40. package/corpus/templates/clips/app/i18n/hi-IN.ts +1 -0
  41. package/corpus/templates/clips/app/i18n/ja-JP.ts +1 -0
  42. package/corpus/templates/clips/app/i18n/ko-KR.ts +1 -0
  43. package/corpus/templates/clips/app/i18n/pt-BR.ts +1 -0
  44. package/corpus/templates/clips/app/i18n/zh-CN.ts +1 -0
  45. package/corpus/templates/clips/app/i18n/zh-TW.ts +1 -0
  46. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +70 -37
  47. package/corpus/templates/clips/app/routes/record.tsx +17 -4
  48. package/corpus/templates/clips/app/routes/share.$shareId.tsx +2 -0
  49. package/corpus/templates/clips/changelog/2026-07-09-desktop-popover-sits-closer-to-the-menu-bar.md +6 -0
  50. package/corpus/templates/clips/changelog/2026-07-09-desktop-recording-optimization-progress-stays-visible-until-the-clip-opens.md +6 -0
  51. package/corpus/templates/clips/changelog/2026-07-09-fixed-large-video-file-uploads-failing-before-their-resumabl.md +6 -0
  52. package/corpus/templates/clips/changelog/2026-07-09-fixed-new-video-uploads-defaulting-to-private.md +6 -0
  53. package/corpus/templates/clips/changelog/2026-07-09-long-recordings-now-get-enough-time-to-download-their-media-.md +6 -0
  54. package/corpus/templates/clips/changelog/2026-07-09-meeting-notes-no-longer-lowers-your-microphone-volume-for-ot.md +6 -0
  55. package/corpus/templates/clips/changelog/2026-07-09-on-mobile-tapping-a-playing-clip-reveals-playback-controls-w.md +6 -0
  56. package/corpus/templates/clips/changelog/2026-07-09-regenerate-any-clip-transcript-from-its-saved-recording-or-r.md +6 -0
  57. package/corpus/templates/clips/changelog/2026-07-09-slack-previews-now-show-the-clip-length-alongside-its-descri.md +6 -0
  58. package/corpus/templates/clips/changelog/2026-07-10-fixed-copying-agent-responses-and-transcript-text-from-recor.md +6 -0
  59. package/corpus/templates/clips/desktop/src/app.tsx +12 -2
  60. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +8 -1
  61. package/corpus/templates/clips/desktop/src/lib/recorder.ts +8 -5
  62. package/corpus/templates/clips/desktop/src/lib/transcription-engine.ts +5 -5
  63. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +3 -0
  64. package/corpus/templates/clips/desktop/src/styles.css +6 -3
  65. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +29 -11
  66. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  67. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +103 -21
  68. package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +117 -39
  69. package/corpus/templates/clips/server/lib/slack-unfurls.ts +20 -1
  70. package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +10 -4
  71. package/corpus/templates/clips/shared/recording-core.ts +12 -0
  72. package/corpus/templates/content/AGENTS.md +7 -6
  73. package/corpus/templates/content/actions/_builder-cms-read-client.ts +82 -7
  74. package/corpus/templates/content/actions/_database-source-utils.ts +149 -30
  75. package/corpus/templates/content/actions/_document-flush.ts +132 -17
  76. package/corpus/templates/content/actions/_notion-action-utils.ts +15 -0
  77. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +4 -7
  78. package/corpus/templates/content/actions/attach-content-database-source.ts +41 -17
  79. package/corpus/templates/content/actions/change-content-database-source-role.ts +46 -8
  80. package/corpus/templates/content/actions/create-and-link-notion-page.ts +2 -0
  81. package/corpus/templates/content/actions/link-notion-page.ts +2 -0
  82. package/corpus/templates/content/actions/pull-document.ts +5 -7
  83. package/corpus/templates/content/actions/pull-notion-page.ts +2 -0
  84. package/corpus/templates/content/actions/push-notion-page.ts +10 -0
  85. package/corpus/templates/content/actions/resolve-notion-sync-conflict.ts +2 -0
  86. package/corpus/templates/content/actions/suggest-source-join-key.ts +8 -1
  87. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +88 -23
  88. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +32 -2
  89. package/corpus/templates/content/app/hooks/use-notion.ts +6 -6
  90. package/corpus/templates/content/app/i18n/zh-TW.ts +1 -0
  91. package/corpus/templates/content/app/i18n-data.ts +18 -0
  92. package/corpus/templates/content/changelog/2026-07-09-builder-source-refreshes-now-keep-mapped-fields-such-as-topi.md +6 -0
  93. package/corpus/templates/content/changelog/2026-07-09-notion-conflict-choices-and-version-restores-preserve-docume.md +6 -0
  94. package/corpus/templates/content/server/lib/notion-sync.ts +80 -64
  95. package/corpus/templates/content/shared/api.ts +1 -0
  96. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +29 -2
  97. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +86 -0
  98. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +57 -0
  99. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +11 -0
  100. package/corpus/templates/design/AGENTS.md +45 -10
  101. package/corpus/templates/design/actions/add-localhost-screens.ts +66 -13
  102. package/corpus/templates/design/actions/apply-component-prop-edit.ts +45 -36
  103. package/corpus/templates/design/actions/apply-motion-edit.ts +25 -31
  104. package/corpus/templates/design/actions/apply-shader-fill.ts +29 -42
  105. package/corpus/templates/design/actions/detach-component-instance.ts +337 -0
  106. package/corpus/templates/design/actions/export-design-as-figma-svg.ts +219 -0
  107. package/corpus/templates/design/actions/generate-design.ts +79 -12
  108. package/corpus/templates/design/actions/generate-screens.ts +57 -12
  109. package/corpus/templates/design/actions/get-figma-styles.ts +115 -0
  110. package/corpus/templates/design/actions/go-to-main-component.ts +254 -0
  111. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +4 -3
  112. package/corpus/templates/design/actions/import-design-source.ts +4 -31
  113. package/corpus/templates/design/actions/import-figma-clipboard.ts +134 -0
  114. package/corpus/templates/design/actions/import-figma-frame.ts +95 -0
  115. package/corpus/templates/design/actions/index-design-system-with-builder.ts +16 -1
  116. package/corpus/templates/design/actions/list-design-components.ts +104 -0
  117. package/corpus/templates/design/actions/list-figma-library-assets.ts +1 -24
  118. package/corpus/templates/design/actions/open-component-source.ts +2 -4
  119. package/corpus/templates/design/actions/present-design-variants.ts +412 -16
  120. package/corpus/templates/design/actions/preview-source-edit.ts +14 -1
  121. package/corpus/templates/design/actions/run-design-audit.ts +25 -3
  122. package/corpus/templates/design/actions/show-design-questions.ts +10 -0
  123. package/corpus/templates/design/actions/swap-component-instance.ts +544 -0
  124. package/corpus/templates/design/actions/take-design-screenshot.ts +45 -60
  125. package/corpus/templates/design/actions/write-local-file.ts +25 -1
  126. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +152 -0
  127. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +1077 -58
  128. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +5 -5
  129. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +73 -21
  130. package/corpus/templates/design/app/components/design/EditPanel.tsx +16 -11
  131. package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +364 -0
  132. package/corpus/templates/design/app/components/design/LayersPanel.tsx +180 -53
  133. package/corpus/templates/design/app/components/design/LocalhostWriteConsentDialog.tsx +0 -2
  134. package/corpus/templates/design/app/components/design/MotionDock.tsx +144 -21
  135. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1152 -167
  136. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +27 -5
  137. package/corpus/templates/design/app/components/design/ReviewPanel.tsx +23 -2
  138. package/corpus/templates/design/app/components/design/TokensPanel.tsx +63 -49
  139. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +1293 -54
  140. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +7 -0
  141. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +6 -1
  142. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +23 -6
  143. package/corpus/templates/design/app/components/design/bridge/sample.bridge.ts +7 -0
  144. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbench.tsx +5 -0
  145. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +14 -2
  146. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +37 -2
  147. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open-guard.ts +25 -0
  148. package/corpus/templates/design/app/components/design/code-workbench/format/format-on-open.ts +19 -1
  149. package/corpus/templates/design/app/components/design/code-workbench/model-registry.ts +43 -5
  150. package/corpus/templates/design/app/components/design/code-workbench/search/SearchView.tsx +27 -2
  151. package/corpus/templates/design/app/components/design/code-workbench/search/search-engine.ts +10 -1
  152. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +19 -2
  153. package/corpus/templates/design/app/components/design/design-canvas/annotation-snapshot.ts +265 -0
  154. package/corpus/templates/design/app/components/design/design-canvas/annotation-submit.ts +16 -5
  155. package/corpus/templates/design/app/components/design/design-canvas/coordinate-transforms.ts +16 -2
  156. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +7 -2
  157. package/corpus/templates/design/app/components/design/design-canvas/iframe-events.ts +3 -1
  158. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +78 -8
  159. package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +451 -73
  160. package/corpus/templates/design/app/components/design/edit-panel/document-colors.ts +12 -4
  161. package/corpus/templates/design/app/components/design/edit-panel/effects-properties.tsx +398 -272
  162. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +41 -7
  163. package/corpus/templates/design/app/components/design/edit-panel/element-identity.ts +16 -3
  164. package/corpus/templates/design/app/components/design/edit-panel/field-primitives.tsx +24 -4
  165. package/corpus/templates/design/app/components/design/edit-panel/fill-gradient-helpers.ts +222 -4
  166. package/corpus/templates/design/app/components/design/edit-panel/fill-properties.tsx +171 -78
  167. package/corpus/templates/design/app/components/design/edit-panel/inspector-controls.tsx +45 -5
  168. package/corpus/templates/design/app/components/design/edit-panel/layout-properties.tsx +299 -28
  169. package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +120 -150
  170. package/corpus/templates/design/app/components/design/edit-panel/position-helpers.ts +53 -1
  171. package/corpus/templates/design/app/components/design/edit-panel/position-layout-properties.tsx +284 -117
  172. package/corpus/templates/design/app/components/design/edit-panel/selection-helpers.ts +81 -0
  173. package/corpus/templates/design/app/components/design/edit-panel/stroke-properties.tsx +19 -14
  174. package/corpus/templates/design/app/components/design/edit-panel/style-options.ts +7 -0
  175. package/corpus/templates/design/app/components/design/edit-panel/typography-helpers.ts +155 -0
  176. package/corpus/templates/design/app/components/design/edit-panel/typography-properties.tsx +268 -46
  177. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +350 -31
  178. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +13 -4
  179. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +233 -23
  180. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +72 -2
  181. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +51 -11
  182. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +84 -11
  183. package/corpus/templates/design/app/components/design/inspector/index.ts +3 -0
  184. package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +552 -0
  185. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +157 -0
  186. package/corpus/templates/design/app/components/design/multi-screen/frame-geometry.ts +53 -0
  187. package/corpus/templates/design/app/components/design/multi-screen/overview-layout.ts +184 -0
  188. package/corpus/templates/design/app/components/design/multi-screen/primitive-drop-target.ts +403 -26
  189. package/corpus/templates/design/app/components/design/multi-screen/types.ts +20 -1
  190. package/corpus/templates/design/app/components/design/types.ts +33 -0
  191. package/corpus/templates/design/app/components/editor/PromptDialog.tsx +59 -7
  192. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +119 -17
  193. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +275 -63
  194. package/corpus/templates/design/app/hooks/use-question-flow.ts +14 -3
  195. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +67 -10
  196. package/corpus/templates/design/app/i18n/zh-TW.ts +91 -1
  197. package/corpus/templates/design/app/i18n-data.ts +1217 -10
  198. package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +577 -0
  199. package/corpus/templates/design/app/lib/agent-chat.ts +23 -0
  200. package/corpus/templates/design/app/lib/design-import.ts +24 -0
  201. package/corpus/templates/design/app/lib/figma-clipboard.ts +157 -0
  202. package/corpus/templates/design/app/lib/figma-svg-copy.ts +221 -0
  203. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +143 -30
  204. package/corpus/templates/design/app/pages/design-editor/collab-sync.ts +15 -0
  205. package/corpus/templates/design/app/pages/design-editor/editor-session.ts +33 -0
  206. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +38 -0
  207. package/corpus/templates/design/app/pages/design-editor/overview-annotation-context.ts +157 -0
  208. package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +62 -6
  209. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +245 -0
  210. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +586 -0
  211. package/corpus/templates/design/app/pages/design-editor/screen-auto-layout.ts +169 -0
  212. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +201 -1
  213. package/corpus/templates/design/changelog/2026-07-09-added-underline-strikethrough-and-text-case-uppercase-lowerc.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-09-annotate-now-draws-across-the-all-screens-canvas-without-swi.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-09-auto-layout-children-can-now-be-dragged-into-freeform-screen.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-09-board-shapes-now-stay-visible-and-nest-correctly-across-the-.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-09-component-source-navigation-now-opens-reliably-from-the-insp.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-09-cross-screen-local-react-layer-moves-now-hand-off-exact-sour.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-09-cross-screen-moves-undo-and-redo-now-keep-both-screens-stabl.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-09-detach-a-component-instance-into-plain-editable-eleme.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-09-dragging-layers-into-auto-layout-now-respects-ignore-auto-la.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-09-drawing-annotations-sent-to-the-agent-now-include-a-rendered.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-09-editor-sidebars-now-use-figma-matched-default-widths-for-a-r.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-09-empty-frames-and-rectangles-now-expose-full-auto-layout-cont.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-09-export-any-design-screen-as-a-real-vector-svg-for-figma-with.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-09-grid-auto-layout-now-uses-true-responsive-rows-and-columns-w.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-09-hidden-and-locked-screens-now-behave-consistently-between-la.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-09-image-fills-now-preserve-valid-urls-and-layered-fit-settings.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-09-import-figma-frames-as-pixel-accurate-design-screens-via-a-s.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-09-inspect-code-now-hides-internal-styling-metadata-and-wraps-l.md +6 -0
  231. package/corpus/templates/design/changelog/2026-07-09-inspector-blur-and-shadow-effects-now-preserve-other-effects.md +6 -0
  232. package/corpus/templates/design/changelog/2026-07-09-jump-to-a-components-other-instances-from-the-inspecto.md +6 -0
  233. package/corpus/templates/design/changelog/2026-07-09-keyboard-shortcuts-are-now-discoverable-in-a-figma-style-bot.md +6 -0
  234. package/corpus/templates/design/changelog/2026-07-09-layer-selection-now-keeps-cmd-or-ctrl-toggles-and-shift-rang.md +6 -0
  235. package/corpus/templates/design/changelog/2026-07-09-layers-dropped-into-auto-layout-now-participate-correctly-in.md +6 -0
  236. package/corpus/templates/design/changelog/2026-07-09-local-live-edit-screens-now-enter-and-leave-full-view-withou.md +6 -0
  237. package/corpus/templates/design/changelog/2026-07-09-local-preview-urls-now-live-in-the-inspector-instead-of-taki.md +6 -0
  238. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-can-now-be-hidden-and-locked-from-layers-.md +6 -0
  239. package/corpus/templates/design/changelog/2026-07-09-local-react-layers-now-keep-exact-source-provenance-and-supp.md +6 -0
  240. package/corpus/templates/design/changelog/2026-07-09-local-react-visual-editing-now-loads-reliably-and-shows-live.md +6 -0
  241. package/corpus/templates/design/changelog/2026-07-09-paste-from-figma-cmd-v-now-imports-exact-editable-nodes-when.md +6 -0
  242. package/corpus/templates/design/changelog/2026-07-09-pressing-escape-after-selecting-a-screen-now-reveals-page-pr.md +6 -0
  243. package/corpus/templates/design/changelog/2026-07-09-right-click-overlapping-or-nested-objects-to-choose-the-exac.md +6 -0
  244. package/corpus/templates/design/changelog/2026-07-09-scale-edge-stretch-and-center-constraints-now-preserve-layer.md +6 -0
  245. package/corpus/templates/design/changelog/2026-07-09-screen-rows-can-now-be-reordered-in-layers-with-matching-can.md +6 -0
  246. package/corpus/templates/design/changelog/2026-07-09-screen-selection-stays-responsive-in-designs-with-hundreds-o.md +6 -0
  247. package/corpus/templates/design/changelog/2026-07-09-shift-a-now-enables-auto-layout-on-a-selected-screen-with-fl.md +6 -0
  248. package/corpus/templates/design/changelog/2026-07-09-swap-a-component-instance-for-another-from-the-libra.md +6 -0
  249. package/corpus/templates/design/changelog/2026-07-10-drawing-annotations-now-stay-available-to-retry-when-the.md +6 -0
  250. package/corpus/templates/design/e2e/global-setup.ts +3 -6
  251. package/corpus/templates/design/e2e/helpers.ts +27 -6
  252. package/corpus/templates/design/server/lib/design-to-figma-svg.ts +1412 -0
  253. package/corpus/templates/design/server/lib/figma-clipboard-match.ts +196 -0
  254. package/corpus/templates/design/server/lib/figma-node-import.ts +311 -0
  255. package/corpus/templates/design/server/lib/figma-node-to-html.ts +1334 -0
  256. package/corpus/templates/design/server/lib/figma-paste-fallback.ts +64 -0
  257. package/corpus/templates/design/server/lib/playwright-runtime.ts +78 -0
  258. package/corpus/templates/design/server/plugins/db.ts +41 -0
  259. package/corpus/templates/design/server/source-workspace.ts +64 -0
  260. package/corpus/templates/design/shared/board-file.ts +13 -2
  261. package/corpus/templates/design/shared/canvas-math.ts +231 -33
  262. package/corpus/templates/design/shared/code-layer.ts +122 -8
  263. package/corpus/templates/design/shared/component-library.ts +161 -0
  264. package/corpus/templates/design/shared/db-conflict.ts +27 -0
  265. package/corpus/templates/design/shared/design-source-capabilities.ts +2 -1
  266. package/corpus/templates/design/shared/figma-url.ts +149 -0
  267. package/corpus/templates/design/shared/pen-path.ts +16 -7
  268. package/corpus/templates/design/shared/source-mode.ts +1 -0
  269. package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +7 -19
  270. package/corpus/templates/plan/changelog/2026-07-09-canvas-panning-now-keeps-zoom-fixed-even-at-the-edges.md +6 -0
  271. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +16 -1
  272. package/corpus/templates/slides/app/context/DeckContext.tsx +311 -70
  273. package/corpus/templates/slides/changelog/2026-07-09-fixed-the-slide-rail-going-permanently-stale-during-a-long-a.md +6 -0
  274. package/dist/agent/production-agent.d.ts +82 -7
  275. package/dist/agent/production-agent.d.ts.map +1 -1
  276. package/dist/agent/production-agent.js +180 -17
  277. package/dist/agent/production-agent.js.map +1 -1
  278. package/dist/agent/run-manager.d.ts.map +1 -1
  279. package/dist/agent/run-manager.js +63 -14
  280. package/dist/agent/run-manager.js.map +1 -1
  281. package/dist/agent/run-store.d.ts +46 -0
  282. package/dist/agent/run-store.d.ts.map +1 -1
  283. package/dist/agent/run-store.js +129 -22
  284. package/dist/agent/run-store.js.map +1 -1
  285. package/dist/cli/design-connect.d.ts +6 -0
  286. package/dist/cli/design-connect.d.ts.map +1 -1
  287. package/dist/cli/design-connect.js +336 -139
  288. package/dist/cli/design-connect.js.map +1 -1
  289. package/dist/cli/skills.d.ts.map +1 -1
  290. package/dist/cli/skills.js +14 -0
  291. package/dist/cli/skills.js.map +1 -1
  292. package/dist/client/AssistantChat.d.ts +2 -0
  293. package/dist/client/AssistantChat.d.ts.map +1 -1
  294. package/dist/client/AssistantChat.js +19 -3
  295. package/dist/client/AssistantChat.js.map +1 -1
  296. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  297. package/dist/client/MultiTabAssistantChat.js +38 -18
  298. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  299. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  300. package/dist/client/agent-chat-adapter.js +48 -12
  301. package/dist/client/agent-chat-adapter.js.map +1 -1
  302. package/dist/client/agent-chat.d.ts +60 -0
  303. package/dist/client/agent-chat.d.ts.map +1 -1
  304. package/dist/client/agent-chat.js +122 -1
  305. package/dist/client/agent-chat.js.map +1 -1
  306. package/dist/client/chat/index.d.ts +1 -1
  307. package/dist/client/chat/index.d.ts.map +1 -1
  308. package/dist/client/chat/index.js +1 -1
  309. package/dist/client/chat/index.js.map +1 -1
  310. package/dist/client/extensions/EmbeddedExtension.d.ts +12 -0
  311. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  312. package/dist/client/extensions/EmbeddedExtension.js +36 -2
  313. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  314. package/dist/client/index.d.ts +1 -1
  315. package/dist/client/index.d.ts.map +1 -1
  316. package/dist/client/index.js +1 -1
  317. package/dist/client/index.js.map +1 -1
  318. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  319. package/dist/client/rich-markdown-editor/useCollabReconcile.js +18 -6
  320. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  321. package/dist/collab/awareness-store.d.ts +5 -0
  322. package/dist/collab/awareness-store.d.ts.map +1 -1
  323. package/dist/collab/awareness-store.js +19 -12
  324. package/dist/collab/awareness-store.js.map +1 -1
  325. package/dist/collab/awareness.d.ts +2 -2
  326. package/dist/collab/awareness.d.ts.map +1 -1
  327. package/dist/collab/client.d.ts.map +1 -1
  328. package/dist/collab/client.js +13 -5
  329. package/dist/collab/client.js.map +1 -1
  330. package/dist/collab/index.d.ts +1 -0
  331. package/dist/collab/index.d.ts.map +1 -1
  332. package/dist/collab/index.js +1 -0
  333. package/dist/collab/index.js.map +1 -1
  334. package/dist/collab/recent-edits.d.ts +3 -1
  335. package/dist/collab/recent-edits.d.ts.map +1 -1
  336. package/dist/collab/recent-edits.js +57 -2
  337. package/dist/collab/recent-edits.js.map +1 -1
  338. package/dist/collab/struct-routes.d.ts +1 -1
  339. package/dist/index.browser.d.ts +1 -1
  340. package/dist/index.browser.d.ts.map +1 -1
  341. package/dist/index.browser.js +1 -1
  342. package/dist/index.browser.js.map +1 -1
  343. package/dist/observability/routes.d.ts +5 -5
  344. package/dist/progress/routes.d.ts +1 -1
  345. package/dist/resources/handlers.d.ts +3 -3
  346. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  347. package/dist/server/agent-chat-plugin.js +87 -10
  348. package/dist/server/agent-chat-plugin.js.map +1 -1
  349. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  350. package/dist/server/builder-design-systems.d.ts +15 -0
  351. package/dist/server/builder-design-systems.d.ts.map +1 -1
  352. package/dist/server/builder-design-systems.js +9 -4
  353. package/dist/server/builder-design-systems.js.map +1 -1
  354. package/package.json +1 -1
package/corpus/README.md CHANGED
@@ -28,4 +28,4 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
28
28
  ## Generated Counts
29
29
 
30
30
  - core files: 2155
31
- - template files: 5119
31
+ - template files: 5197
@@ -1,5 +1,34 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.92.12
4
+
5
+ ### Patch Changes
6
+
7
+ - 1ecce26: Surface missing LLM credentials and earlier stream errors as failed agent runs instead of successful completions.
8
+
9
+ ## 0.92.11
10
+
11
+ ### Patch Changes
12
+
13
+ - 4552931: Keep local React visual-edit previews hydrated by preserving Vite request metadata and response lengths through the bridge, and recover exact development source locations from React jsxDEV Fiber stacks.
14
+ - 4552931: Expose strict active-collaboration awareness reads so safety-critical sync flows can distinguish no open editor from presence-storage failures.
15
+ - 4552931: Add cancellation-safe `sendToAgentChatAndConfirm` delivery confirmation and its submit-result event contract so callers can preserve user work when a local chat message is rejected or times out, without allowing that timed-out message to arrive later.
16
+ - 4552931: Fix `.fig` and other binary files silently corrupting when uploaded through `index-design-system-with-builder`: `buildBuilderDesignSystemIndexFiles` always UTF-8-encoded file content even though `mimeTypeForBuilderDesignSystemFilename` already recognized `.fig` as binary (`application/octet-stream`). Add an optional per-file `encoding: "utf8" | "base64"` (defaults to `utf8`, unchanged for existing text-file callers) so binary files can be base64-encoded by the caller and decoded byte-exact here.
17
+ - 4552931: Prevent pre-seed collaborative editor updates from reaching autosave, and allow newer authoritative restores to reapply content previously emitted during mount while retaining stale-echo protection during active typing.
18
+ - 4552931: Prevent PR recap workflows from publishing screenshots of failed recap pages, and report SSR render failures through configured error monitoring.
19
+ - 4552931: Fix long agent-chat turns dying with a Netlify "508 Loop Detected" error after several server-driven continuation chunks: nested self-dispatch chains now break proactively before the platform's undocumented loop-protection limit, and a 508 that does occur is classified distinctly and deferred to the existing unclaimed-run sweep for recovery instead of failing the turn outright.
20
+ - 4552931: Recover background chat turns whose continuation handoff dispatch failed instead of failing them immediately: the pre-inserted successor run is left claimable so the unclaimed-run sweep can redispatch it (and the client poll defers to that sweep within a bound), backed by a backstop that still fails loud if the handoff never lands.
21
+
22
+ Operational note — terminal-reason change: when a continuation handoff exhausts its dispatch retry budget but the successor row exists, the run's `terminal_reason` is now `background_continuation_dispatch_deferred` (recoverable, awaiting sweep redispatch) instead of `background_continuation_dispatch_failed`. The old `background_continuation_dispatch_failed` reason is still emitted, but now only in the narrower case where the successor row itself could not be pre-inserted. Any dashboards, alerts, or audit queries matching `background_continuation_dispatch_failed` should add `background_continuation_dispatch_deferred` to keep covering this failure class.
23
+
24
+ - 4552931: Fix an awareness "storm" in `useCollaborativeDoc`: the shared collab connection's fast awareness-push path re-broadcast a client's own (unchanged) presence state whenever ANY remote participant's awareness changed, not just when the local user actually moved their cursor or updated their presence. With several people collaborating on the same document, every peer's cursor move caused every other connected client to also re-POST its own state, multiplying awareness traffic roughly quadratically with participant count. The fast-push listener now only fires on locally-originated awareness changes (`origin === "local"`), matching the equivalent guard already present in `usePresence`.
25
+
26
+ Also add a defensive size cap to the collab `recentEdits` ring (`appendRecentEdit`): descriptor strings (`quote`, `selector`, path entries) and the edit `label` are now truncated to 500 characters. The ring was already bounded in length, but an app forgetting to trim its own excerpt before publishing (e.g. passing a whole paragraph or document as `quote`) could otherwise push an oversized payload through the awareness fast-path broadcast and into the `_collab_awareness` SQL mirror.
27
+
28
+ - 4552931: Give the Design localhost bridge a per-boot `bridgeInstanceId`, exposed on `/health`, the `/live-edit-bridge` registration response, and the `/live-edit` "unknown bridge key" 409 (now carrying a machine-readable `code: "unknown-bridge-key"` and the echoed `bridgeKey`). This lets a client distinguish a bridge process restart — which silently empties the in-memory live-edit bridge script registry — from a genuine registration bug, instead of guessing from free-text error strings.
29
+ - 4552931: Fix a recurring `DataCloneError` console error when any embedded extension (Design's Tools/Extensions panel, or any other app using `EmbeddedExtension`) loads or its slot context updates. Slot contexts commonly carry live callback functions the host uses internally; `postMessage`'s structured clone algorithm throws on function-valued properties, so the context update silently never reached the extension iframe. Sanitize the context through a JSON round-trip (dropping functions) before posting it.
30
+ - 4552931: Harden local Design source writes with SHA-256 version checks, per-file serialization, atomic replacement, and repeated symlink containment validation.
31
+
3
32
  ## 0.92.10
4
33
 
5
34
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.92.10",
3
+ "version": "0.92.12",
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": {
@@ -136,6 +136,7 @@ import {
136
136
  insertRun,
137
137
  updateRunHeartbeat,
138
138
  updateRunStatusIfRunning,
139
+ setRunError,
139
140
  setRunTerminalReason,
140
141
  claimBackgroundRun,
141
142
  readBackgroundRunClaim,
@@ -4881,18 +4882,47 @@ export function shouldChainBackgroundContinuation(opts: {
4881
4882
  export async function markBackgroundContinuationChunkTerminal(opts: {
4882
4883
  runId: string;
4883
4884
  continuationReason: AgentLoopContinuationReason;
4885
+ terminalEvent?: AgentChatEvent;
4884
4886
  deps?: {
4885
4887
  updateRunStatusIfRunning?: typeof updateRunStatusIfRunning;
4888
+ setRunError?: typeof setRunError;
4886
4889
  setRunTerminalReason?: typeof setRunTerminalReason;
4887
4890
  };
4888
4891
  }): Promise<boolean> {
4889
4892
  const updateStatus =
4890
4893
  opts.deps?.updateRunStatusIfRunning ?? updateRunStatusIfRunning;
4894
+ const persistError = opts.deps?.setRunError ?? setRunError;
4891
4895
  const setTerminalReason =
4892
4896
  opts.deps?.setRunTerminalReason ?? setRunTerminalReason;
4893
- const updated = await updateStatus(opts.runId, "completed");
4897
+ const terminalEvent = opts.terminalEvent;
4898
+ const isTerminalFailure =
4899
+ terminalEvent?.type === "error" ||
4900
+ terminalEvent?.type === "missing_api_key";
4901
+ const terminalReason =
4902
+ terminalEvent?.type === "error"
4903
+ ? `error:${terminalEvent.errorCode || "unknown"}`
4904
+ : terminalEvent?.type === "missing_api_key"
4905
+ ? "missing_api_key"
4906
+ : opts.continuationReason;
4907
+ const updated = await updateStatus(
4908
+ opts.runId,
4909
+ isTerminalFailure ? "errored" : "completed",
4910
+ );
4894
4911
  if (updated) {
4895
- await setTerminalReason(opts.runId, opts.continuationReason);
4912
+ await setTerminalReason(opts.runId, terminalReason);
4913
+ if (terminalEvent?.type === "error") {
4914
+ await persistError(
4915
+ opts.runId,
4916
+ terminalEvent.errorCode,
4917
+ terminalEvent.details || terminalEvent.error,
4918
+ );
4919
+ } else if (terminalEvent?.type === "missing_api_key") {
4920
+ await persistError(
4921
+ opts.runId,
4922
+ LLM_MISSING_CREDENTIALS_ERROR_CODE,
4923
+ LLM_MISSING_CREDENTIALS_MESSAGE,
4924
+ );
4925
+ }
4896
4926
  }
4897
4927
  return updated;
4898
4928
  }
@@ -5038,6 +5068,70 @@ export function resolveContinuationDispatchBudget(opts: {
5038
5068
  };
5039
5069
  }
5040
5070
 
5071
+ /**
5072
+ * True when a `fireInternalDispatch` failure is Netlify's Functions platform
5073
+ * loop-protection response (`HTTP 508 Loop Detected`), matched against the
5074
+ * exact message `self-dispatch.ts`'s `dispatchResponseError` constructs
5075
+ * (`Self-dispatch to ${path} returned HTTP ${res.status} ${res.statusText}...`).
5076
+ * Matches on the status code alone (not the reason phrase text) so it is
5077
+ * robust to any wording Netlify's edge puts in `statusText`.
5078
+ *
5079
+ * VERIFIED: production `agent_runs` diagnostics show exactly this failure mode
5080
+ * — 8 successful `chain_dispatch_sent` self-dispatches followed by a 9th/10th
5081
+ * that dies with `HTTP 508 Loop Detected`, terminal reason
5082
+ * `background_continuation_dispatch_failed`. UNVERIFIED (Netlify does not
5083
+ * document the mechanism — checked the Functions overview, Functions API
5084
+ * reference, Background Functions overview, Status Codes reference, and
5085
+ * Request Chain troubleshooting docs, none mention it): the ONLY public
5086
+ * confirmation is a Netlify staff forum reply — "we prevent multiple
5087
+ * executions after one-another as that's a 'loop' ... I believe we enforce
5088
+ * this after 9 or 10 self-invocations"
5089
+ * (https://answers.netlify.com/t/self-invoke-background-function-via-post-requests/146012)
5090
+ * — which also confirms Background Functions do NOT escape the limit (the
5091
+ * reporter's case was already a `-background` function self-invoking). See
5092
+ * `MAX_NESTED_SELF_DISPATCH_DEPTH` for how this classification is used.
5093
+ */
5094
+ export function isLoopProtectionDispatchError(err: unknown): boolean {
5095
+ if (!(err instanceof Error)) return false;
5096
+ return /\bHTTP\s*508\b/i.test(err.message);
5097
+ }
5098
+
5099
+ /**
5100
+ * Conservative safety margin on NESTED self-dispatch chaining — a
5101
+ * continuation dispatched directly from within the live invocation that is
5102
+ * about to finish, as opposed to being picked up later by the
5103
+ * unclaimed-background-run sweep (`agent-chat-plugin.ts`), which fires its
5104
+ * redispatch from an unrelated, timer-driven invocation rather than from
5105
+ * inside the chain's own execution.
5106
+ *
5107
+ * Netlify's loop-protection ceiling is undocumented and platform-reported
5108
+ * only approximately ("I believe... 9 or 10" — see
5109
+ * `isLoopProtectionDispatchError`), so hard-coding that exact number here
5110
+ * would be pinning behavior to a number Netlify itself won't commit to, and
5111
+ * production evidence shows it can trigger by the 9th self-dispatch. Rather
5112
+ * than only reacting after triggering that undocumented limit,
5113
+ * `chainServerDrivenContinuation` proactively hands a chunk to the sweep once
5114
+ * `backgroundContinuationCount` (nested hops since the last chain break)
5115
+ * reaches this value — comfortably below the observed ~9-10 failure point —
5116
+ * instead of attempting another nested dispatch. This is the SAME deferred-
5117
+ * recovery path already used when a dispatch fails outright: the row is left
5118
+ * `status='running', dispatch_mode='background'` for the sweep to redispatch,
5119
+ * never silently dropped.
5120
+ *
5121
+ * The sweep's redispatch marker intentionally omits `continuationCount` (see
5122
+ * the "Unclaimed background-run sweep" in `agent-chat-plugin.ts`), so a
5123
+ * sweep-recovered chunk's own `backgroundContinuationCount` resets to 0 —
5124
+ * this constant therefore bounds each NESTED segment between chain breaks,
5125
+ * not the whole turn. The TRUE ceiling on total turn length is unaffected by
5126
+ * that reset: it is the durable per-turn SQL ledger (`countRunsForTurn`,
5127
+ * checked above in this function) plus `MAX_BACKGROUND_RUN_CONTINUATIONS` —
5128
+ * both counted independently of this in-marker value — so a legitimately
5129
+ * long turn keeps making progress in bounded nested segments, each recovered
5130
+ * by the sweep, until it genuinely exhausts the intentional overall budget
5131
+ * and fails loud with `turn_continuation_budget_exhausted`.
5132
+ */
5133
+ export const MAX_NESTED_SELF_DISPATCH_DEPTH = 6;
5134
+
5041
5135
  /**
5042
5136
  * Server-driven continuation handoff for a chunk that hit its soft-timeout
5043
5137
  * boundary still unfinished: mint the next chunk's runId, PRE-INSERT its run
@@ -5046,12 +5140,26 @@ export function resolveContinuationDispatchBudget(opts: {
5046
5140
  * `_process-run` self-dispatch carrying ids only (the body is persisted on
5047
5141
  * the row as `dispatch_payload`), fully AWAIT the dispatch acknowledgment
5048
5142
  * with retries, and mark this chunk terminal only after the handoff landed.
5049
- * On failure every path is loud: the successor row is errored, the failure
5050
- * is recorded as the run's diag stage, and this chunk is flipped to errored
5051
- * with a terminal reason never a silent loss. (For a FOREGROUND self-chain
5052
- * the client additionally still receives the terminal `auto_continue` event
5053
- * run-manager emits it after this callback so the existing client
5054
- * re-POST path takes over as the fallback.)
5143
+ * On failure this chunk always goes terminal loudly (diag stage + terminal
5144
+ * reason recorded never a silent loss), but the successor row's fate
5145
+ * depends on WHY the dispatch failed:
5146
+ * - the pre-insert itself failed (no successor row exists) — nothing for a
5147
+ * sweep to find, so this is unrecoverable: fail loud immediately with
5148
+ * `background_continuation_dispatch_failed`.
5149
+ * - every dispatch attempt failed but the successor row DOES exist with its
5150
+ * `dispatch_payload` intact — this is RECOVERABLE: the row is left alone
5151
+ * (`status='running', dispatch_mode='background'`) instead of being
5152
+ * errored, so the unclaimed-background-run sweep in `agent-chat-plugin.ts`
5153
+ * can redispatch it once `UNCLAIMED_BACKGROUND_RUN_GRACE_MS` has passed.
5154
+ * This chunk is flipped to errored with the distinct, honest
5155
+ * `background_continuation_dispatch_deferred` reason — the TURN is
5156
+ * deferred, not dead. The sweep still bounds this by
5157
+ * `UNCLAIMED_BACKGROUND_RUN_REDISPATCH_BOUND_MS`: past that it falls back
5158
+ * to the existing loud reap, so a genuinely dead handoff never hangs
5159
+ * silently forever. (For a FOREGROUND self-chain the client additionally
5160
+ * still receives the terminal `auto_continue` event — run-manager emits
5161
+ * it after this callback — so the existing client re-POST path is a
5162
+ * second, faster fallback alongside the sweep.)
5055
5163
  *
5056
5164
  * `chainViaDurableBackground` selects the dispatch target:
5057
5165
  * - true → the durable-background worker chain (unchanged behavior): the
@@ -5244,9 +5352,23 @@ export async function chainServerDrivenContinuation(opts: {
5244
5352
  };
5245
5353
  let dispatched = false;
5246
5354
  let lastDispatchErr: unknown;
5355
+ // Proactive nested-chain safety margin — see `MAX_NESTED_SELF_DISPATCH_DEPTH`.
5356
+ // Skip the nested dispatch attempt entirely once this segment's hop count
5357
+ // reaches the cap; a nested attempt at this depth is expected to trip
5358
+ // Netlify's loop protection, so there is nothing to gain by trying it and
5359
+ // burning this worker's remaining wall clock on a doomed call. Falls
5360
+ // straight into the same deferred-recovery path below as an exhausted
5361
+ // retry budget.
5362
+ const nestedDepthExceeded =
5363
+ opts.backgroundContinuationCount >= MAX_NESTED_SELF_DISPATCH_DEPTH;
5364
+ if (nestedDepthExceeded) {
5365
+ lastDispatchErr = new Error(
5366
+ `proactive nested-dispatch depth cap reached (backgroundContinuationCount=${opts.backgroundContinuationCount} >= MAX_NESTED_SELF_DISPATCH_DEPTH=${MAX_NESTED_SELF_DISPATCH_DEPTH}) — deferring to the unclaimed-background-run sweep instead of risking Netlify loop protection`,
5367
+ );
5368
+ }
5247
5369
  for (
5248
5370
  let attempt = 0;
5249
- attempt < maxDispatchAttempts && !dispatched;
5371
+ !nestedDepthExceeded && attempt < maxDispatchAttempts && !dispatched;
5250
5372
  attempt++
5251
5373
  ) {
5252
5374
  try {
@@ -5313,25 +5435,99 @@ export async function chainServerDrivenContinuation(opts: {
5313
5435
  `[agent-chat] background continuation dispatch attempt ${attempt + 1} failed:`,
5314
5436
  dispatchErr instanceof Error ? dispatchErr.message : dispatchErr,
5315
5437
  );
5438
+ // Netlify loop protection (see `isLoopProtectionDispatchError`) is a
5439
+ // property of this same live, nested call chain — retrying the exact
5440
+ // same nested self-dispatch within the next few seconds will not
5441
+ // change that, so further attempts are a guaranteed-doomed use of
5442
+ // this worker's remaining wall clock. Stop immediately (instead of
5443
+ // burning the full `maxDispatchAttempts` budget) and fall into the
5444
+ // same deferred-recovery path below, which hands the successor to the
5445
+ // sweep — a genuinely different, non-nested invocation.
5446
+ if (isLoopProtectionDispatchError(dispatchErr)) {
5447
+ break;
5448
+ }
5316
5449
  }
5317
5450
  }
5318
5451
  if (!dispatched) {
5319
- // The pre-inserted successor row would otherwise sit unclaimed until
5320
- // the sweep reaps it — error it now so the failure is immediate and
5321
- // truthful.
5322
5452
  if (nextRowInserted) {
5323
- const nextStatusUpdated = await d
5324
- .updateRunStatusIfRunning(nextRunId, "errored")
5453
+ // Classify WHY this handoff is being deferred — distinct, greppable
5454
+ // tags so production diagnostics (which is all that is readable from
5455
+ // a background worker) can tell "we proactively avoided Netlify loop
5456
+ // protection", "we hit loop protection and stopped retrying", and "a
5457
+ // generic transient dispatch failure exhausted its retry budget"
5458
+ // apart, instead of lumping every deferred handoff into one bucket.
5459
+ const deferralClassification = nestedDepthExceeded
5460
+ ? "proactive_depth_cap"
5461
+ : isLoopProtectionDispatchError(lastDispatchErr)
5462
+ ? "netlify_loop_protection"
5463
+ : "dispatch_budget_exhausted";
5464
+ // RECOVERABLE: the successor row already exists in SQL with its
5465
+ // rehydration payload (`dispatch_payload`) intact and is still
5466
+ // `status='running', dispatch_mode='background'` — exactly the state
5467
+ // the unclaimed-background-run sweep (`agent-chat-plugin.ts`) already
5468
+ // scans for. Do NOT error it here: leave it alone so the sweep can
5469
+ // redispatch it once `UNCLAIMED_BACKGROUND_RUN_GRACE_MS` has passed,
5470
+ // bounded by `UNCLAIMED_BACKGROUND_RUN_REDISPATCH_BOUND_MS` before it
5471
+ // falls back to the existing loud reap
5472
+ // (`background_worker_never_started`) — so this is deferred, never a
5473
+ // silent hang. This chunk still goes terminal (its own soft-timeout
5474
+ // budget is genuinely spent), but with an honest reason: the TURN is
5475
+ // not dead, only this handoff attempt was.
5476
+ //
5477
+ // THREE-SITE INVARIANT (keep in lockstep — a future reader must not
5478
+ // "fix" one without the others): this deferral only survives because
5479
+ // the ~1s client poll in `getActiveRunForThreadAsync`
5480
+ // (run-manager.ts) ALSO skips `reapUnclaimedBackgroundRun` while the
5481
+ // successor is within `UNCLAIMED_BACKGROUND_RUN_REDISPATCH_BOUND_MS`
5482
+ // (via `shouldRedispatchUnclaimedBackgroundRun`). Without that guard a
5483
+ // connected client would reap this row at the 25s grace, before the
5484
+ // ~2-min sweep, defeating the deferral. The sweep in agent-chat-plugin
5485
+ // is the recovery actor; run-manager is the guard; this is the
5486
+ // producer.
5487
+ await d
5488
+ .recordRunDiagnostic(
5489
+ nextRunId,
5490
+ RUN_DIAG_STAGE.workerThrew,
5491
+ `chain_dispatch_deferred[${deferralClassification}]: dispatch budget exhausted (${maxDispatchAttempts} attempts) awaiting unclaimed-run sweep redispatch; ${
5492
+ lastDispatchErr instanceof Error
5493
+ ? lastDispatchErr.message
5494
+ : String(lastDispatchErr)
5495
+ }`,
5496
+ )
5497
+ .catch(() => {});
5498
+ await d
5499
+ .recordRunDiagnostic(
5500
+ runId,
5501
+ RUN_DIAG_STAGE.workerThrew,
5502
+ `chain_dispatch_deferred[${deferralClassification}] nextRunId=${nextRunId} ${
5503
+ lastDispatchErr instanceof Error
5504
+ ? lastDispatchErr.message
5505
+ : String(lastDispatchErr)
5506
+ }`,
5507
+ )
5508
+ .catch(() => {});
5509
+ console.error(
5510
+ `[agent-chat] background continuation dispatch deferred (${deferralClassification}); leaving the pre-inserted successor for the unclaimed-run sweep to redispatch:`,
5511
+ lastDispatchErr instanceof Error
5512
+ ? lastDispatchErr.message
5513
+ : lastDispatchErr,
5514
+ );
5515
+ const statusUpdated = await d
5516
+ .updateRunStatusIfRunning(runId, "errored")
5325
5517
  .catch(() => false);
5326
- if (nextStatusUpdated) {
5518
+ if (statusUpdated) {
5327
5519
  await d
5328
5520
  .setRunTerminalReason(
5329
- nextRunId,
5330
- "background_continuation_dispatch_failed",
5521
+ runId,
5522
+ "background_continuation_dispatch_deferred",
5331
5523
  )
5332
5524
  .catch(() => {});
5333
5525
  }
5526
+ return;
5334
5527
  }
5528
+ // No successor row exists at all (the pre-insert itself failed) — there
5529
+ // is nothing for the sweep to find and recover, so this really is
5530
+ // fatal. Fail loud immediately via the shared catch block below.
5335
5531
  throw lastDispatchErr instanceof Error
5336
5532
  ? lastDispatchErr
5337
5533
  : new Error(String(lastDispatchErr));
@@ -5347,6 +5543,7 @@ export async function chainServerDrivenContinuation(opts: {
5347
5543
  .markBackgroundContinuationChunkTerminal({
5348
5544
  runId,
5349
5545
  continuationReason,
5546
+ terminalEvent: run.events.at(-1)?.event,
5350
5547
  })
5351
5548
  .catch(() => {});
5352
5549
  } catch (chainErr) {
@@ -1,5 +1,9 @@
1
1
  import { captureError } from "../server/capture-error.js";
2
- import { isLlmCredentialError } from "./engine/credential-errors.js";
2
+ import {
3
+ isLlmCredentialError,
4
+ LLM_MISSING_CREDENTIALS_ERROR_CODE,
5
+ LLM_MISSING_CREDENTIALS_MESSAGE,
6
+ } from "./engine/credential-errors.js";
3
7
  import { EngineError } from "./engine/types.js";
4
8
  import {
5
9
  insertRun,
@@ -16,6 +20,7 @@ import {
16
20
  bumpRunProgress,
17
21
  reapIfStale,
18
22
  reapUnclaimedBackgroundRun,
23
+ shouldRedispatchUnclaimedBackgroundRun,
19
24
  reconcileTerminalRunFromEvents,
20
25
  ensureTerminalRunEvent,
21
26
  getLastTerminalRunEvent,
@@ -376,6 +381,10 @@ function isTerminalRunEvent(event: AgentChatEvent): boolean {
376
381
  );
377
382
  }
378
383
 
384
+ function terminalEventForcesErroredStatus(event: AgentChatEvent | null) {
385
+ return event?.type === "error" || event?.type === "missing_api_key";
386
+ }
387
+
379
388
  function terminalReasonForRun(
380
389
  finalStatus: "completed" | "errored" | "aborted",
381
390
  terminalEvent: AgentChatEvent | null,
@@ -898,14 +907,27 @@ export function startRun(
898
907
  // /runs/active check while we wait for SQL writes to land.
899
908
  let completionError: unknown = null;
900
909
  let terminalPersistenceError: unknown = null;
910
+ const terminalEvent = pendingTerminalEvent?.event ?? null;
901
911
  if (
902
912
  onComplete &&
903
913
  !(run.status === "aborted" && run.abortReason === "no_progress")
904
914
  ) {
905
915
  try {
906
- const completionRun: ActiveRun = pendingTerminalEvent
907
- ? { ...run, events: [...run.events, pendingTerminalEvent] }
908
- : run;
916
+ const completionStatus =
917
+ run.status !== "aborted" &&
918
+ terminalEventForcesErroredStatus(terminalEvent)
919
+ ? "errored"
920
+ : run.status;
921
+ const completionRun: ActiveRun =
922
+ pendingTerminalEvent || completionStatus !== run.status
923
+ ? {
924
+ ...run,
925
+ status: completionStatus,
926
+ events: pendingTerminalEvent
927
+ ? [...run.events, pendingTerminalEvent]
928
+ : run.events,
929
+ }
930
+ : run;
909
931
  await onComplete(completionRun);
910
932
  } catch (err) {
911
933
  completionError = err;
@@ -923,12 +945,14 @@ export function startRun(
923
945
  const finalStatus =
924
946
  run.status === "aborted"
925
947
  ? "aborted"
926
- : run.status === "errored" || completionError
948
+ : run.status === "errored" ||
949
+ completionError ||
950
+ terminalEventForcesErroredStatus(terminalEvent)
927
951
  ? "errored"
928
952
  : "completed";
929
953
  const terminalReason = terminalReasonForRun(
930
954
  finalStatus,
931
- pendingTerminalEvent?.event ?? null,
955
+ terminalEvent,
932
956
  run.abortReason,
933
957
  completionError,
934
958
  );
@@ -952,7 +976,8 @@ export function startRun(
952
976
  const terminalEvent: AgentChatEvent =
953
977
  finalStatus === "completed"
954
978
  ? (pendingTerminalEvent?.event ?? { type: "done" })
955
- : pendingTerminalEvent?.event.type === "error"
979
+ : pendingTerminalEvent?.event.type === "error" ||
980
+ pendingTerminalEvent?.event.type === "missing_api_key"
956
981
  ? pendingTerminalEvent.event
957
982
  : pendingTerminalEvent?.event.type === "auto_continue"
958
983
  ? // The run was checkpointed at a soft-timeout/loop boundary and
@@ -1029,14 +1054,21 @@ export function startRun(
1029
1054
  if (finalStatus === "errored") {
1030
1055
  let errorCode: string | undefined;
1031
1056
  let errorDetail: string | undefined;
1032
- for (let i = run.events.length - 1; i >= 0; i--) {
1033
- const ev = run.events[i].event as {
1057
+ const diagnosticEvents = pendingTerminalEvent
1058
+ ? [...run.events, pendingTerminalEvent]
1059
+ : run.events;
1060
+ for (let i = diagnosticEvents.length - 1; i >= 0; i--) {
1061
+ const ev = diagnosticEvents[i].event as {
1034
1062
  type: string;
1035
1063
  error?: string;
1036
1064
  errorCode?: string;
1037
1065
  details?: string;
1038
1066
  };
1039
- if (ev.type === "error") {
1067
+ if (ev.type === "missing_api_key") {
1068
+ errorCode = LLM_MISSING_CREDENTIALS_ERROR_CODE;
1069
+ errorDetail = LLM_MISSING_CREDENTIALS_MESSAGE;
1070
+ break;
1071
+ } else if (ev.type === "error") {
1040
1072
  errorCode = ev.errorCode;
1041
1073
  errorDetail = ev.error ?? ev.details;
1042
1074
  break;
@@ -1469,10 +1501,36 @@ export async function getActiveRunForThreadAsync(threadId: string): Promise<{
1469
1501
  // past the tight grace means the bg-fn worker never started — a silent
1470
1502
  // async-worker death that the 202-ack inline fallback can't catch. Reap it
1471
1503
  // early and recoverably (background_worker_never_started) so the run no
1472
- // longer hangs for the full 90s window and the client's recoverable-error
1473
- // path can re-drive the turn. Only fires when there is provably no live
1474
- // worker; a claimed/heartbeating run is left alone by the conditional SQL.
1475
- if (sqlRun.dispatchMode === "background") {
1504
+ // longer hangs for the full 90s window. Only fires when there is provably
1505
+ // no live worker; a claimed/heartbeating run is left alone by the
1506
+ // conditional SQL.
1507
+ //
1508
+ // REDISPATCH-BOUND GUARD (must be kept in lockstep with the "Unclaimed
1509
+ // background-run sweep" in agent-chat-plugin.ts and with
1510
+ // chainServerDrivenContinuation's deferral in production-agent.ts — do NOT
1511
+ // remove this guard without reading those two sites):
1512
+ // `chainServerDrivenContinuation` now DEFERS a dispatch-failed successor
1513
+ // instead of erroring it — it leaves the row status='running',
1514
+ // dispatch_mode='background' with its dispatch_payload intact so the sweep
1515
+ // can silently redispatch it. This client poll runs every ~1s while a
1516
+ // client is connected, so without this guard it would reap that deferred
1517
+ // successor at the 25s unclaimed grace — long before the ~2-min sweep —
1518
+ // converting the intended SILENT server-side recovery into a user-visible
1519
+ // `background_worker_never_started` manual-retry error (that terminal
1520
+ // reason does NOT auto-continue in the client follow loop; only `stale_run`
1521
+ // does). While the successor is still inside its redispatch bound we skip
1522
+ // this reap and leave it for the sweep. The outer backstops still bound it:
1523
+ // `reapIfStale` below reaps a heartbeat-stale background row at 90s
1524
+ // (BACKGROUND_RUN_STALE_MS) to the recoverable `stale_run` — which the
1525
+ // follow loop AUTO-continues — and once the redispatch bound is exceeded
1526
+ // this reap fires loudly as before. So recovery stays automatic in the
1527
+ // common case and loud failure is only moved later, never removed.
1528
+ if (
1529
+ sqlRun.dispatchMode === "background" &&
1530
+ !shouldRedispatchUnclaimedBackgroundRun({
1531
+ startedAt: sqlRun.startedAt,
1532
+ })
1533
+ ) {
1476
1534
  const recovered = await reapUnclaimedBackgroundRun(sqlRun.id).catch(
1477
1535
  () => false,
1478
1536
  );