@agent-native/core 0.131.5 → 0.131.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/corpus/README.md +3 -3
  2. package/corpus/core/CHANGELOG.md +70 -0
  3. package/corpus/core/docs/content/agent-native-toolkit.mdx +13 -6
  4. package/corpus/core/docs/content/cloneable-saas.mdx +5 -5
  5. package/corpus/core/docs/content/creating-templates.mdx +9 -0
  6. package/corpus/core/docs/content/deployment.mdx +18 -12
  7. package/corpus/core/docs/content/doctor.mdx +8 -1
  8. package/corpus/core/docs/content/drop-in-agent.mdx +1 -1
  9. package/corpus/core/docs/content/extensions.mdx +18 -18
  10. package/corpus/core/docs/content/faq.mdx +4 -4
  11. package/corpus/core/docs/content/key-concepts.mdx +30 -21
  12. package/corpus/core/docs/content/security.mdx +2 -2
  13. package/corpus/core/docs/content/template-brain-developers.mdx +4 -1
  14. package/corpus/core/docs/content/template-mail-developers.mdx +4 -1
  15. package/corpus/core/docs/content/what-is-agent-native.mdx +6 -6
  16. package/corpus/core/package.json +1 -1
  17. package/corpus/core/src/a2a/client.ts +449 -66
  18. package/corpus/core/src/a2a/correlation.ts +31 -0
  19. package/corpus/core/src/a2a/invoke.ts +4 -2
  20. package/corpus/core/src/a2a/types.ts +4 -0
  21. package/corpus/core/src/action.ts +3 -0
  22. package/corpus/core/src/agent/action-continuation-guidance.ts +2 -1
  23. package/corpus/core/src/agent/engine/registry.ts +98 -6
  24. package/corpus/core/src/agent/production-agent.ts +151 -18
  25. package/corpus/core/src/agent/run-loop-with-resume.ts +190 -3
  26. package/corpus/core/src/agent/run-manager.ts +117 -41
  27. package/corpus/core/src/agent/thread-debug-history.ts +86 -0
  28. package/corpus/core/src/agent/types.ts +3 -1
  29. package/corpus/core/src/cli/create.ts +11 -1
  30. package/corpus/core/src/cli/templates-meta.ts +2 -2
  31. package/corpus/core/src/client/agent-chat-adapter.ts +91 -13
  32. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +11 -9
  33. package/corpus/core/src/client/sse-event-processor.ts +17 -3
  34. package/corpus/core/src/db/client.ts +57 -17
  35. package/corpus/core/src/guards/no-env-credentials.ts +2 -0
  36. package/corpus/core/src/integrations/adapters/slack.ts +8 -3
  37. package/corpus/core/src/localization/default-messages.ts +44 -0
  38. package/corpus/core/src/observability/traces.ts +78 -5
  39. package/corpus/core/src/scripts/call-agent.ts +294 -11
  40. package/corpus/core/src/scripts/describe-workspace-apps.ts +2 -2
  41. package/corpus/core/src/secrets/crypto.ts +156 -35
  42. package/corpus/core/src/secrets/storage.ts +64 -26
  43. package/corpus/core/src/server/agent-capabilities.ts +1 -1
  44. package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +113 -7
  45. package/corpus/core/src/server/agent-chat/context-tools.ts +2 -0
  46. package/corpus/core/src/server/agent-chat/plugin-options.ts +34 -1
  47. package/corpus/core/src/server/agent-chat/prompt-resources.ts +1 -1
  48. package/corpus/core/src/server/agent-chat-plugin.ts +91 -59
  49. package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
  50. package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
  51. package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
  52. package/corpus/core/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
  53. package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
  54. package/corpus/core/src/vite/client.ts +1 -0
  55. package/corpus/templates/analytics/.agents/skills/dashboard-ops/SKILL.md +5 -4
  56. package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +6 -4
  57. package/corpus/templates/analytics/AGENTS.md +7 -4
  58. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -3
  59. package/corpus/templates/analytics/changelog/2026-07-30-delegated-analytics-questions-now-return-a-useful-current-me.md +6 -0
  60. package/corpus/templates/analytics/changelog/2026-07-30-scheduled-dashboard-emails-complete-every-panel-without-pool.md +6 -0
  61. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +28 -1
  62. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +18 -11
  63. package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +4 -4
  64. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -5
  65. package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +6 -2
  66. package/corpus/templates/analytics/server/lib/dashboard-panel-source-resolver.ts +11 -4
  67. package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +49 -31
  68. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +16 -8
  69. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +9 -3
  70. package/corpus/templates/analytics/server/lib/production-serverless-runtime.ts +11 -0
  71. package/corpus/templates/analytics/server/lib/real-data-actions.ts +10 -0
  72. package/corpus/templates/analytics/server/plugins/agent-chat.ts +3 -2
  73. package/corpus/templates/analytics/server/plugins/analytics-alert-jobs.ts +2 -1
  74. package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +2 -1
  75. package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -12
  76. package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +54 -6
  77. package/corpus/templates/brain/.agents/skills/brain/SKILL.md +20 -9
  78. package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +33 -1
  79. package/corpus/templates/brain/AGENTS.md +3 -1
  80. package/corpus/templates/brain/README.md +4 -1
  81. package/corpus/templates/brain/actions/_schemas.ts +30 -8
  82. package/corpus/templates/brain/actions/ask-brain.ts +124 -14
  83. package/corpus/templates/brain/actions/create-source.ts +18 -2
  84. package/corpus/templates/brain/actions/enqueue-captures-distillation.ts +8 -123
  85. package/corpus/templates/brain/actions/enqueue-distillation.ts +6 -127
  86. package/corpus/templates/brain/actions/import-capture.ts +17 -5
  87. package/corpus/templates/brain/actions/import-transcript.ts +17 -5
  88. package/corpus/templates/brain/actions/update-source.ts +25 -5
  89. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
  90. package/corpus/templates/brain/changelog/2026-07-30-blessed-resources-can-feed-cited-company-answers.md +6 -0
  91. package/corpus/templates/brain/changelog/2026-07-30-the-left-sidebar-no-longer-shows-a-blank-organization-placeh.md +6 -0
  92. package/corpus/templates/brain/server/lib/brain.ts +49 -19
  93. package/corpus/templates/brain/server/lib/distillation-queue.ts +147 -0
  94. package/corpus/templates/brain/server/lib/source-policy.ts +264 -0
  95. package/corpus/templates/brain/server/plugins/agent-chat.ts +3 -2
  96. package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +116 -19
  97. package/corpus/templates/clips/app/components/meetings/google-oauth-identity-notice.tsx +51 -0
  98. package/corpus/templates/clips/app/i18n/en-US.ts +4 -0
  99. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +3 -1
  100. package/corpus/templates/clips/changelog/2026-07-30-google-calendar-warning.md +6 -0
  101. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +72 -8
  102. package/corpus/templates/content/changelog/2026-07-30-the-slash-command-hint-now-stays-on-only-the-active-empty-li.md +6 -0
  103. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +506 -11
  104. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +148 -17
  105. package/corpus/templates/design/app/components/design/canvas-interactions/design-canvas-interactions.ts +357 -0
  106. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +7 -466
  107. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +15 -311
  108. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +19 -997
  109. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +8 -1216
  110. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +31 -38
  111. package/corpus/templates/design/package.json +0 -1
  112. package/corpus/templates/dispatch/AGENTS.md +8 -0
  113. package/corpus/templates/dispatch/DEVELOPING.md +29 -0
  114. package/corpus/templates/dispatch/actions/view-screen.ts +34 -2
  115. package/corpus/templates/dispatch/app/i18n-data.ts +457 -0
  116. package/corpus/templates/dispatch/changelog/2026-07-30-delegated-dispatch-work-continues-reliably-in-the-background.md +6 -0
  117. package/corpus/templates/dispatch/changelog/2026-07-30-find-failed-runs-across-apps.md +6 -0
  118. package/corpus/templates/dispatch/netlify.toml +3 -0
  119. package/corpus/templates/plan/actions/get-visual-plan.ts +10 -6
  120. package/corpus/templates/plan/actions/patch-visual-plan-source.ts +117 -1
  121. package/corpus/templates/plan/actions/update-visual-plan.ts +56 -14
  122. package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -1
  123. package/corpus/templates/plan/changelog/2026-07-30-plan-agents-recover-with-targeted-writes.md +6 -0
  124. package/corpus/templates/plan/shared/plan-content.ts +35 -0
  125. package/corpus/templates/slides/.agents/skills/analytics-data-for-decks/SKILL.md +8 -12
  126. package/corpus/templates/slides/AGENTS.md +4 -4
  127. package/corpus/templates/slides/actions/create-deck.ts +1 -1
  128. package/corpus/templates/slides/actions/duplicate-deck.ts +19 -4
  129. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -10
  130. package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +12 -1
  131. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +30 -13
  132. package/corpus/templates/slides/app/components/editor/DeckEditorSkeleton.tsx +35 -0
  133. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +63 -56
  134. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -76
  135. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1110 -292
  136. package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +53 -0
  137. package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +235 -18
  138. package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +4 -1
  139. package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +11 -0
  140. package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +129 -0
  141. package/corpus/templates/slides/app/components/editor/selection-overlay-measurement.ts +70 -0
  142. package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +519 -65
  143. package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +36 -426
  144. package/corpus/templates/slides/app/components/visual-editor/DrawOverlay.tsx +19 -458
  145. package/corpus/templates/slides/app/context/DeckContext.tsx +179 -72
  146. package/corpus/templates/slides/app/global.css +105 -0
  147. package/corpus/templates/slides/app/i18n/en-US.ts +8 -0
  148. package/corpus/templates/slides/app/lib/deck-editor-loading.ts +26 -0
  149. package/corpus/templates/slides/app/lib/mermaid-blocks.ts +27 -0
  150. package/corpus/templates/slides/app/pages/DeckEditor.tsx +71 -38
  151. package/corpus/templates/slides/app/pages/Index.tsx +98 -41
  152. package/corpus/templates/slides/changelog/2026-07-30-deck-loading-no-longer-flashes-an-unavailable-message.md +6 -0
  153. package/corpus/templates/slides/changelog/2026-07-30-duplicating-a-deck-from-the-deck-list-now-opens-the-copy-imm.md +6 -0
  154. package/corpus/templates/slides/changelog/2026-07-30-layout-overflow-warning-is-readable-and-dismissible.md +6 -0
  155. package/corpus/templates/slides/changelog/2026-07-30-new-decks-now-start-with-an-empty-slide-list-when-no-slides-.md +6 -0
  156. package/corpus/templates/slides/changelog/2026-07-30-shared-canvas-annotations.md +6 -0
  157. package/corpus/templates/slides/changelog/2026-07-30-slide-objects-can-be-moved-as-a-group-copied-and-pasted-with.md +6 -0
  158. package/corpus/templates/slides/changelog/2026-07-30-slide-text-editing-and-object-controls-now-match-google-slides.md +6 -0
  159. package/corpus/templates/slides/changelog/2026-07-30-style-panel-now-scrolls-with-speaker-notes-open-shows-the-sl.md +6 -0
  160. package/corpus/templates/slides/changelog/2026-07-30-top-toolbar-controls-now-use-consistent-sizing-with-undo-and.md +6 -0
  161. package/corpus/templates/slides/netlify.toml +3 -1
  162. package/corpus/templates/slides/package.json +0 -1
  163. package/corpus/templates/slides/server/lib/chat-attachments.ts +18 -0
  164. package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -0
  165. package/corpus/templates/slides/vite.config.ts +0 -1
  166. package/corpus/toolkit/CHANGELOG.md +13 -0
  167. package/corpus/toolkit/README.md +19 -0
  168. package/corpus/toolkit/agent-native.eject.json +38 -0
  169. package/corpus/toolkit/package.json +29 -1
  170. package/corpus/toolkit/src/canvas-annotations/CanvasCommentPins.tsx +861 -0
  171. package/corpus/toolkit/src/canvas-annotations/DrawOverlay.tsx +1233 -0
  172. package/corpus/toolkit/src/canvas-annotations/index.ts +15 -0
  173. package/corpus/toolkit/src/canvas-annotations/types.ts +14 -0
  174. package/corpus/toolkit/src/canvas-interactions/canvas-interactions.ts +933 -0
  175. package/corpus/toolkit/src/canvas-interactions/index.ts +67 -0
  176. package/corpus/toolkit/src/design-tweaks/scrub-input-utils.ts +311 -0
  177. package/corpus/toolkit/src/design-tweaks/scrub-input.tsx +491 -0
  178. package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +26 -171
  179. package/corpus/toolkit/src/index.ts +1 -0
  180. package/dist/a2a/client.d.ts +23 -0
  181. package/dist/a2a/client.d.ts.map +1 -1
  182. package/dist/a2a/client.js +309 -44
  183. package/dist/a2a/client.js.map +1 -1
  184. package/dist/a2a/correlation.d.ts +1 -0
  185. package/dist/a2a/correlation.d.ts.map +1 -1
  186. package/dist/a2a/correlation.js +27 -0
  187. package/dist/a2a/correlation.js.map +1 -1
  188. package/dist/a2a/invoke.d.ts.map +1 -1
  189. package/dist/a2a/invoke.js +4 -2
  190. package/dist/a2a/invoke.js.map +1 -1
  191. package/dist/a2a/types.d.ts +4 -0
  192. package/dist/a2a/types.d.ts.map +1 -1
  193. package/dist/a2a/types.js.map +1 -1
  194. package/dist/action.d.ts +3 -0
  195. package/dist/action.d.ts.map +1 -1
  196. package/dist/action.js.map +1 -1
  197. package/dist/agent/action-continuation-guidance.d.ts.map +1 -1
  198. package/dist/agent/action-continuation-guidance.js +2 -1
  199. package/dist/agent/action-continuation-guidance.js.map +1 -1
  200. package/dist/agent/engine/registry.d.ts.map +1 -1
  201. package/dist/agent/engine/registry.js +82 -8
  202. package/dist/agent/engine/registry.js.map +1 -1
  203. package/dist/agent/production-agent.d.ts +40 -1
  204. package/dist/agent/production-agent.d.ts.map +1 -1
  205. package/dist/agent/production-agent.js +113 -15
  206. package/dist/agent/production-agent.js.map +1 -1
  207. package/dist/agent/run-loop-with-resume.d.ts +21 -0
  208. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  209. package/dist/agent/run-loop-with-resume.js +163 -4
  210. package/dist/agent/run-loop-with-resume.js.map +1 -1
  211. package/dist/agent/run-manager.d.ts +16 -9
  212. package/dist/agent/run-manager.d.ts.map +1 -1
  213. package/dist/agent/run-manager.js +93 -42
  214. package/dist/agent/run-manager.js.map +1 -1
  215. package/dist/agent/thread-debug-history.d.ts +10 -0
  216. package/dist/agent/thread-debug-history.d.ts.map +1 -0
  217. package/dist/agent/thread-debug-history.js +68 -0
  218. package/dist/agent/thread-debug-history.js.map +1 -0
  219. package/dist/agent/types.d.ts +3 -1
  220. package/dist/agent/types.d.ts.map +1 -1
  221. package/dist/agent/types.js.map +1 -1
  222. package/dist/cli/create.d.ts.map +1 -1
  223. package/dist/cli/create.js +6 -1
  224. package/dist/cli/create.js.map +1 -1
  225. package/dist/cli/templates-meta.js +2 -2
  226. package/dist/cli/templates-meta.js.map +1 -1
  227. package/dist/client/agent-chat-adapter.d.ts +1 -1
  228. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  229. package/dist/client/agent-chat-adapter.js +76 -13
  230. package/dist/client/agent-chat-adapter.js.map +1 -1
  231. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  232. package/dist/client/resources/McpIntegrationDialog.js +1 -1
  233. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  234. package/dist/client/sse-event-processor.d.ts.map +1 -1
  235. package/dist/client/sse-event-processor.js +12 -3
  236. package/dist/client/sse-event-processor.js.map +1 -1
  237. package/dist/collab/routes.d.ts +1 -1
  238. package/dist/db/client.d.ts.map +1 -1
  239. package/dist/db/client.js +48 -17
  240. package/dist/db/client.js.map +1 -1
  241. package/dist/guards/no-env-credentials.d.ts.map +1 -1
  242. package/dist/guards/no-env-credentials.js +2 -0
  243. package/dist/guards/no-env-credentials.js.map +1 -1
  244. package/dist/integrations/adapters/slack.js +7 -3
  245. package/dist/integrations/adapters/slack.js.map +1 -1
  246. package/dist/localization/default-messages.d.ts +43 -0
  247. package/dist/localization/default-messages.d.ts.map +1 -1
  248. package/dist/localization/default-messages.js +43 -0
  249. package/dist/localization/default-messages.js.map +1 -1
  250. package/dist/observability/traces.d.ts +3 -1
  251. package/dist/observability/traces.d.ts.map +1 -1
  252. package/dist/observability/traces.js +73 -4
  253. package/dist/observability/traces.js.map +1 -1
  254. package/dist/progress/routes.d.ts +1 -1
  255. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  256. package/dist/provider-api/actions/provider-api.d.ts +13 -13
  257. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  258. package/dist/scripts/call-agent.d.ts.map +1 -1
  259. package/dist/scripts/call-agent.js +237 -12
  260. package/dist/scripts/call-agent.js.map +1 -1
  261. package/dist/scripts/describe-workspace-apps.js +2 -2
  262. package/dist/scripts/describe-workspace-apps.js.map +1 -1
  263. package/dist/secrets/crypto.d.ts +36 -21
  264. package/dist/secrets/crypto.d.ts.map +1 -1
  265. package/dist/secrets/crypto.js +134 -33
  266. package/dist/secrets/crypto.js.map +1 -1
  267. package/dist/secrets/storage.d.ts +6 -6
  268. package/dist/secrets/storage.d.ts.map +1 -1
  269. package/dist/secrets/storage.js +43 -26
  270. package/dist/secrets/storage.js.map +1 -1
  271. package/dist/server/agent-capabilities.js +1 -1
  272. package/dist/server/agent-capabilities.js.map +1 -1
  273. package/dist/server/agent-chat/action-filters-a2a.d.ts +17 -3
  274. package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
  275. package/dist/server/agent-chat/action-filters-a2a.js +94 -3
  276. package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
  277. package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
  278. package/dist/server/agent-chat/context-tools.js +2 -0
  279. package/dist/server/agent-chat/context-tools.js.map +1 -1
  280. package/dist/server/agent-chat/plugin-options.d.ts +23 -0
  281. package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
  282. package/dist/server/agent-chat/plugin-options.js +4 -1
  283. package/dist/server/agent-chat/plugin-options.js.map +1 -1
  284. package/dist/server/agent-chat/prompt-resources.js +1 -1
  285. package/dist/server/agent-chat/prompt-resources.js.map +1 -1
  286. package/dist/server/agent-chat-plugin.d.ts +2 -1
  287. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  288. package/dist/server/agent-chat-plugin.js +79 -56
  289. package/dist/server/agent-chat-plugin.js.map +1 -1
  290. package/dist/server/realtime-token.d.ts +1 -1
  291. package/dist/templates/default/.agents/skills/secrets/SKILL.md +19 -7
  292. package/dist/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
  293. package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
  294. package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
  295. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
  296. package/dist/vite/client.d.ts.map +1 -1
  297. package/dist/vite/client.js +1 -0
  298. package/dist/vite/client.js.map +1 -1
  299. package/docs/content/agent-native-toolkit.mdx +13 -6
  300. package/docs/content/cloneable-saas.mdx +5 -5
  301. package/docs/content/creating-templates.mdx +9 -0
  302. package/docs/content/deployment.mdx +18 -12
  303. package/docs/content/doctor.mdx +8 -1
  304. package/docs/content/drop-in-agent.mdx +1 -1
  305. package/docs/content/extensions.mdx +18 -18
  306. package/docs/content/faq.mdx +4 -4
  307. package/docs/content/key-concepts.mdx +30 -21
  308. package/docs/content/security.mdx +2 -2
  309. package/docs/content/template-brain-developers.mdx +4 -1
  310. package/docs/content/template-mail-developers.mdx +4 -1
  311. package/docs/content/what-is-agent-native.mdx +6 -6
  312. package/package.json +3 -3
  313. package/src/a2a/client.ts +449 -66
  314. package/src/a2a/correlation.ts +31 -0
  315. package/src/a2a/invoke.ts +4 -2
  316. package/src/a2a/types.ts +4 -0
  317. package/src/action.ts +3 -0
  318. package/src/agent/action-continuation-guidance.ts +2 -1
  319. package/src/agent/engine/registry.ts +98 -6
  320. package/src/agent/production-agent.ts +151 -18
  321. package/src/agent/run-loop-with-resume.ts +190 -3
  322. package/src/agent/run-manager.ts +117 -41
  323. package/src/agent/thread-debug-history.ts +86 -0
  324. package/src/agent/types.ts +3 -1
  325. package/src/cli/create.ts +11 -1
  326. package/src/cli/templates-meta.ts +2 -2
  327. package/src/client/agent-chat-adapter.ts +91 -13
  328. package/src/client/resources/McpIntegrationDialog.tsx +11 -9
  329. package/src/client/sse-event-processor.ts +17 -3
  330. package/src/db/client.ts +57 -17
  331. package/src/guards/no-env-credentials.ts +2 -0
  332. package/src/integrations/adapters/slack.ts +8 -3
  333. package/src/localization/default-messages.ts +44 -0
  334. package/src/observability/traces.ts +78 -5
  335. package/src/scripts/call-agent.ts +294 -11
  336. package/src/scripts/describe-workspace-apps.ts +2 -2
  337. package/src/secrets/crypto.ts +156 -35
  338. package/src/secrets/storage.ts +64 -26
  339. package/src/server/agent-capabilities.ts +1 -1
  340. package/src/server/agent-chat/action-filters-a2a.ts +113 -7
  341. package/src/server/agent-chat/context-tools.ts +2 -0
  342. package/src/server/agent-chat/plugin-options.ts +34 -1
  343. package/src/server/agent-chat/prompt-resources.ts +1 -1
  344. package/src/server/agent-chat-plugin.ts +91 -59
  345. package/src/templates/default/.agents/skills/secrets/SKILL.md +19 -7
  346. package/src/templates/headless/.agents/skills/secrets/SKILL.md +19 -7
  347. package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
  348. package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
  349. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +19 -7
  350. package/src/vite/client.ts +1 -0
@@ -1,23 +1,12 @@
1
- export const MIN_SLIDE_OBJECT_SIZE = 24;
1
+ import {
2
+ clientPointToCanvasPoint,
3
+ resizeCanvasRect,
4
+ type CanvasResizeHandle,
5
+ } from "@agent-native/toolkit/canvas-interactions";
2
6
 
3
- export type ResizeHandle = "nw" | "n" | "ne" | "w" | "e" | "sw" | "s" | "se";
7
+ export const MIN_SLIDE_OBJECT_SIZE = 24;
4
8
 
5
- export type SelectedObjectDragStart = "perimeter" | "body" | null;
6
-
7
- /**
8
- * Resolve a selected freeform object's drag affordance without conflating a
9
- * normal body click (which still enters inline text editing) with a drag.
10
- */
11
- export function getSelectedObjectDragStart({
12
- targetWithinSelectedObject,
13
- pointerWithinMoveBand,
14
- }: {
15
- targetWithinSelectedObject: boolean;
16
- pointerWithinMoveBand: boolean;
17
- }): SelectedObjectDragStart {
18
- if (pointerWithinMoveBand) return "perimeter";
19
- return targetWithinSelectedObject ? "body" : null;
20
- }
9
+ export type ResizeHandle = CanvasResizeHandle;
21
10
 
22
11
  export interface SlideObjectGeometry {
23
12
  x: number;
@@ -41,6 +30,23 @@ export interface SlideObjectLayoutSnapshot {
41
30
  alignSelf: string;
42
31
  }
43
32
 
33
+ export interface SlideObjectTextPresentationSnapshot {
34
+ color: string;
35
+ direction: string;
36
+ fontFamily: string;
37
+ fontSize: string;
38
+ fontStyle: string;
39
+ fontWeight: string;
40
+ letterSpacing: string;
41
+ lineHeight: string;
42
+ textAlign: string;
43
+ textDecoration: string;
44
+ textShadow: string;
45
+ textTransform: string;
46
+ whiteSpace: string;
47
+ wordSpacing: string;
48
+ }
49
+
44
50
  export interface ResizeOptions {
45
51
  handle: ResizeHandle;
46
52
  dx: number;
@@ -202,6 +208,109 @@ export function resolveSlideObjectContainingBlock(
202
208
  return slideLayer;
203
209
  }
204
210
 
211
+ export interface SlideTextBoxCanvas {
212
+ fmdSlide: HTMLElement;
213
+ positioningLayer: HTMLElement;
214
+ }
215
+
216
+ /**
217
+ * Text objects need the same persisted coordinate root as other freeform
218
+ * objects. Markdown initially renders straight into `.slide-content`, so the
219
+ * first placement promotes that live flow DOM into an fmd-slide before it is
220
+ * saved. Copying the canvas layout values keeps the current visual alignment
221
+ * when the raw HTML renderer owns it after the edit is committed.
222
+ */
223
+ export function ensureSlideTextBoxCanvas(
224
+ editorRoot: HTMLElement,
225
+ ): SlideTextBoxCanvas | null {
226
+ const existing = editorRoot.querySelector<HTMLElement>(".fmd-slide");
227
+ if (existing) {
228
+ const positioningLayer =
229
+ Array.from(existing.children).find(
230
+ (child): child is HTMLElement =>
231
+ child instanceof HTMLElement &&
232
+ child.hasAttribute("data-fmd-autofit-content"),
233
+ ) ?? existing;
234
+ return { fmdSlide: existing, positioningLayer };
235
+ }
236
+
237
+ const slideContents = Array.from(
238
+ editorRoot.querySelectorAll<HTMLElement>(".slide-content"),
239
+ );
240
+ // A two-column Markdown slide renders one independent content root per
241
+ // column. Promoting only the first and serializing it would discard the
242
+ // other column, so decline until both roots have a shared raw-HTML canvas.
243
+ if (slideContents.length !== 1) return null;
244
+ const slideContent = slideContents[0];
245
+ const canvas = slideContent?.closest<HTMLElement>("[data-slide-canvas]");
246
+ if (!slideContent || !canvas) return null;
247
+
248
+ const canvasStyle = window.getComputedStyle(canvas);
249
+ const fmdSlide = document.createElement("div");
250
+ fmdSlide.className = "fmd-slide";
251
+ fmdSlide.style.justifyContent = canvasStyle.justifyContent;
252
+ fmdSlide.style.alignItems = canvasStyle.alignItems;
253
+ fmdSlide.style.padding = canvasStyle.padding;
254
+ fmdSlide.style.textAlign = canvasStyle.textAlign;
255
+ fmdSlide.style.color = canvasStyle.color;
256
+ fmdSlide.style.fontFamily = canvasStyle.fontFamily;
257
+ fmdSlide.append(...Array.from(slideContent.childNodes));
258
+ slideContent.append(fmdSlide);
259
+
260
+ return { fmdSlide, positioningLayer: fmdSlide };
261
+ }
262
+
263
+ function hasUsableTextColor(color: string) {
264
+ return (
265
+ Boolean(color) &&
266
+ color !== "transparent" &&
267
+ !/rgba\([^)]*,\s*0\)$/.test(color)
268
+ );
269
+ }
270
+
271
+ function isTextRun(element: HTMLElement) {
272
+ return Boolean(element.textContent?.trim());
273
+ }
274
+
275
+ function isDarkColor(color: string) {
276
+ const channels = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/i);
277
+ if (!channels) return false;
278
+ const [, red, green, blue] = channels.map(Number);
279
+ return red * 0.2126 + green * 0.7152 + blue * 0.0722 < 140;
280
+ }
281
+
282
+ /** Choose rendered text before generic canvas shells, then keep the fallback legible. */
283
+ export function getSlideTextBoxDefaultColor(
284
+ target: HTMLElement | null,
285
+ positioningLayer: HTMLElement,
286
+ ): string {
287
+ const candidates = [
288
+ ...Array.from(
289
+ positioningLayer.querySelectorAll<HTMLElement>(
290
+ "h1, h2, h3, h4, h5, h6, p, li, span",
291
+ ),
292
+ ).filter(isTextRun),
293
+ target?.matches("h1, h2, h3, h4, h5, h6, p, li, span") && isTextRun(target)
294
+ ? target
295
+ : null,
296
+ ];
297
+ for (const candidate of candidates) {
298
+ if (!candidate) continue;
299
+ const color = window.getComputedStyle(candidate).color;
300
+ if (hasUsableTextColor(color)) {
301
+ return color;
302
+ }
303
+ }
304
+
305
+ const canvas = positioningLayer.closest<HTMLElement>("[data-slide-canvas]");
306
+ const canvasStyle = canvas ? window.getComputedStyle(canvas) : null;
307
+ const designSystemText = canvasStyle?.getPropertyValue("--ds-text").trim();
308
+ if (designSystemText) return designSystemText;
309
+
310
+ const background = canvasStyle?.backgroundColor ?? "";
311
+ return isDarkColor(background) ? "#ffffff" : "#111827";
312
+ }
313
+
205
314
  export function removeTransientBuilderIds(element: HTMLElement): void {
206
315
  element.removeAttribute("data-builder-id");
207
316
  element.querySelectorAll("[data-builder-id]").forEach((node) => {
@@ -209,9 +318,99 @@ export function removeTransientBuilderIds(element: HTMLElement): void {
209
318
  });
210
319
  }
211
320
 
321
+ const ID_REFERENCE_ATTRIBUTES = [
322
+ "aria-activedescendant",
323
+ "aria-controls",
324
+ "aria-describedby",
325
+ "aria-details",
326
+ "aria-errormessage",
327
+ "aria-flowto",
328
+ "aria-labelledby",
329
+ "aria-owns",
330
+ ] as const;
331
+
332
+ function escapeRegExp(value: string): string {
333
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
334
+ }
335
+
336
+ function createSlideObjectDomId(occupiedIds: Set<string>): string {
337
+ let id = `slide-object-dom-${createSlideObjectId()}`;
338
+ while (occupiedIds.has(id)) {
339
+ id = `slide-object-dom-${createSlideObjectId()}`;
340
+ }
341
+ occupiedIds.add(id);
342
+ return id;
343
+ }
344
+
345
+ function remintSlideObjectDomIds(
346
+ element: HTMLElement,
347
+ occupiedIds: Set<string> = new Set(
348
+ Array.from(element.ownerDocument.querySelectorAll<HTMLElement>("[id]")).map(
349
+ (node) => node.id,
350
+ ),
351
+ ),
352
+ ): void {
353
+ const idMap = new Map<string, string>();
354
+ const elements = [element, ...element.querySelectorAll<HTMLElement>("[id]")];
355
+
356
+ for (const node of elements) {
357
+ const id = node.getAttribute("id");
358
+ if (!id) continue;
359
+ const remintedId = createSlideObjectDomId(occupiedIds);
360
+ // Duplicate source ids are already ambiguous. Keep internal references
361
+ // pointed at the first occurrence, matching document.getElementById().
362
+ if (!idMap.has(id)) idMap.set(id, remintedId);
363
+ node.id = remintedId;
364
+ }
365
+
366
+ if (idMap.size === 0) return;
367
+
368
+ const remapIdReferences = (value: string): string =>
369
+ value
370
+ .split(/\s+/)
371
+ .map((id) => idMap.get(id) ?? id)
372
+ .join(" ");
373
+ const remapUrlReferences = (value: string): string => {
374
+ let result = value;
375
+ for (const [id, remintedId] of idMap) {
376
+ result = result.replace(
377
+ new RegExp(`url\\(\\s*#${escapeRegExp(id)}\\s*\\)`, "g"),
378
+ `url(#${remintedId})`,
379
+ );
380
+ }
381
+ return result;
382
+ };
383
+
384
+ for (const node of [element, ...element.querySelectorAll<HTMLElement>("*")]) {
385
+ const labelFor = node.getAttribute("for");
386
+ if (labelFor) node.setAttribute("for", idMap.get(labelFor) ?? labelFor);
387
+
388
+ for (const attribute of ID_REFERENCE_ATTRIBUTES) {
389
+ const value = node.getAttribute(attribute);
390
+ if (value) node.setAttribute(attribute, remapIdReferences(value));
391
+ }
392
+
393
+ for (const attribute of Array.from(node.attributes)) {
394
+ if (attribute.name === "id") continue;
395
+ if (attribute.name === "href" || attribute.name === "xlink:href") {
396
+ const remintedId = attribute.value.startsWith("#")
397
+ ? idMap.get(attribute.value.slice(1))
398
+ : undefined;
399
+ if (remintedId) node.setAttribute(attribute.name, `#${remintedId}`);
400
+ continue;
401
+ }
402
+ const remapped = remapUrlReferences(attribute.value);
403
+ if (remapped !== attribute.value) {
404
+ node.setAttribute(attribute.name, remapped);
405
+ }
406
+ }
407
+ }
408
+ }
409
+
212
410
  export function cloneSlideObject(element: HTMLElement): HTMLElement {
213
411
  const clone = element.cloneNode(true) as HTMLElement;
214
412
  removeTransientBuilderIds(clone);
413
+ remintSlideObjectDomIds(clone);
215
414
  clone.setAttribute("data-slide-object-id", createSlideObjectId());
216
415
  // Nested freeform objects are independently addressable after a clone. Each
217
416
  // one needs a new persisted identity so selector-based edits cannot resolve
@@ -233,6 +432,7 @@ export function freezeSlideElementForFreeform(
233
432
  element: HTMLElement,
234
433
  geometry: SlideObjectGeometry,
235
434
  layout: SlideObjectLayoutSnapshot,
435
+ textPresentation?: SlideObjectTextPresentationSnapshot,
236
436
  ): HTMLElement {
237
437
  const objectId = ensureSlideObjectId(element);
238
438
  const spacer = element.cloneNode(false) as HTMLElement;
@@ -254,9 +454,12 @@ export function freezeSlideElementForFreeform(
254
454
  spacer.style.minHeight = "0";
255
455
  spacer.style.maxWidth = "none";
256
456
  spacer.style.maxHeight = "none";
257
- spacer.style.flexGrow = layout.flexGrow;
258
- spacer.style.flexShrink = layout.flexShrink;
259
- spacer.style.flexBasis = layout.flexBasis;
457
+ // `geometry` is the already-laid-out border box. Letting the replacement
458
+ // flex item shrink that measured size a second time changes the autofit
459
+ // transform and makes the object jump as soon as it becomes absolute.
460
+ spacer.style.flexGrow = "0";
461
+ spacer.style.flexShrink = "0";
462
+ spacer.style.flexBasis = "auto";
260
463
  spacer.style.alignSelf = layout.alignSelf;
261
464
  // An inline placeholder cannot reserve a measured block's height. Preserve
262
465
  // inline text flow with inline-block while retaining block/grid displays.
@@ -274,6 +477,31 @@ export function freezeSlideElementForFreeform(
274
477
  // left/top describe the visible border box. Leaving flow margins on the
275
478
  // absolute element would offset it from the measured pre-freeze rect.
276
479
  element.style.margin = "0";
480
+ if (textPresentation) {
481
+ const properties: Array<
482
+ [keyof SlideObjectTextPresentationSnapshot, string]
483
+ > = [
484
+ ["color", "color"],
485
+ ["direction", "direction"],
486
+ ["fontFamily", "font-family"],
487
+ ["fontSize", "font-size"],
488
+ ["fontStyle", "font-style"],
489
+ ["fontWeight", "font-weight"],
490
+ ["letterSpacing", "letter-spacing"],
491
+ ["lineHeight", "line-height"],
492
+ ["textAlign", "text-align"],
493
+ ["textDecoration", "text-decoration"],
494
+ ["textShadow", "text-shadow"],
495
+ ["textTransform", "text-transform"],
496
+ ["whiteSpace", "white-space"],
497
+ ["wordSpacing", "word-spacing"],
498
+ ];
499
+ for (const [key, property] of properties) {
500
+ if (textPresentation[key] && !element.style.getPropertyValue(property)) {
501
+ element.style.setProperty(property, textPresentation[key]);
502
+ }
503
+ }
504
+ }
277
505
  return spacer;
278
506
  }
279
507
 
@@ -301,13 +529,13 @@ export function clientPointToSlideCoordinates(
301
529
  slideWidth: number,
302
530
  slideHeight: number,
303
531
  ): { x: number; y: number } {
304
- const x =
305
- rect.width > 0 ? ((clientX - rect.left) / rect.width) * slideWidth : 0;
306
- const y =
307
- rect.height > 0 ? ((clientY - rect.top) / rect.height) * slideHeight : 0;
532
+ const point = clientPointToCanvasPoint({ x: clientX, y: clientY }, rect, {
533
+ width: slideWidth,
534
+ height: slideHeight,
535
+ });
308
536
  return {
309
- x: Math.round(x),
310
- y: Math.round(y),
537
+ x: Math.round(point.x),
538
+ y: Math.round(point.y),
311
539
  };
312
540
  }
313
541
 
@@ -321,48 +549,274 @@ export function resizeSlideObject(
321
549
  minSize = MIN_SLIDE_OBJECT_SIZE,
322
550
  }: ResizeOptions,
323
551
  ): SlideObjectGeometry {
324
- const fromWest = handle === "nw" || handle === "w" || handle === "sw";
325
- const fromEast = handle === "ne" || handle === "e" || handle === "se";
326
- const fromNorth = handle === "nw" || handle === "n" || handle === "ne";
327
- const fromSouth = handle === "sw" || handle === "s" || handle === "se";
328
- const resizesHorizontally = fromWest || fromEast;
329
- const resizesVertically = fromNorth || fromSouth;
330
- let width = start.width + (fromWest ? -dx : fromEast ? dx : 0);
331
- let height = start.height + (fromNorth ? -dy : fromSouth ? dy : 0);
332
-
333
- // Midpoint handles intentionally resize a single axis, even with Shift.
334
- // Their opposing edge remains fixed and no implied perpendicular resize occurs.
335
- if (
336
- preserveAspectRatio &&
337
- resizesHorizontally &&
338
- resizesVertically &&
339
- start.width > 0 &&
340
- start.height > 0
341
- ) {
342
- const ratio = start.width / start.height;
343
- const horizontalScale = width / start.width;
344
- const verticalScale = height / start.height;
345
- const scale =
346
- Math.abs(horizontalScale - 1) >= Math.abs(verticalScale - 1)
347
- ? horizontalScale
348
- : verticalScale;
349
- width = start.width * scale;
350
- height = width / ratio;
552
+ return resizeCanvasRect(start, {
553
+ handle,
554
+ delta: { x: dx, y: dy },
555
+ preserveAspectRatio,
556
+ minWidth: minSize,
557
+ minHeight: minSize,
558
+ });
559
+ }
560
+
561
+ export type SlideObjectZOrderTarget = "front" | "back";
562
+
563
+ function readSlideObjectZIndex(element: HTMLElement): number {
564
+ const raw = element.style.zIndex || window.getComputedStyle(element).zIndex;
565
+ const value = Number(raw);
566
+ return Number.isFinite(value) ? value : 0;
567
+ }
568
+
569
+ export interface SlideObjectZOrderChange {
570
+ /** z-index to assign to the moved element. */
571
+ value: number;
572
+ /** Peers that must be shifted up to make room, when there was none below. */
573
+ shiftPeers: { element: HTMLElement; value: number }[];
574
+ }
575
+
576
+ function isEditableFreeformSlideObject(element: HTMLElement): boolean {
577
+ return (
578
+ element.hasAttribute("data-slide-object-id") &&
579
+ (element.style.position || window.getComputedStyle(element).position) ===
580
+ "absolute"
581
+ );
582
+ }
583
+
584
+ function createsSlideObjectStackingContext(element: HTMLElement): boolean {
585
+ const style = window.getComputedStyle(element);
586
+ const inline = element.style;
587
+ const position = inline.position || style.position || "static";
588
+ const zIndex = inline.zIndex || style.zIndex;
589
+ const containment = inline.contain || style.contain || "";
590
+ const willChange = inline.willChange || style.willChange || "";
591
+ const opacity = Number.parseFloat(inline.opacity || style.opacity || "1");
592
+ const transform = inline.transform || style.transform;
593
+ const perspective = inline.perspective || style.perspective;
594
+ const filter = inline.filter || style.filter;
595
+ const backdropFilter = inline.backdropFilter || style.backdropFilter;
596
+ const isolation = inline.isolation || style.isolation;
597
+ const mixBlendMode = inline.mixBlendMode || style.mixBlendMode;
598
+
599
+ return (
600
+ position === "fixed" ||
601
+ position === "sticky" ||
602
+ (position !== "static" && zIndex !== "" && zIndex !== "auto") ||
603
+ (Number.isFinite(opacity) && opacity < 1) ||
604
+ (transform !== "" && transform !== "none") ||
605
+ (perspective !== "" && perspective !== "none") ||
606
+ (filter !== "" && filter !== "none") ||
607
+ (backdropFilter !== "" && backdropFilter !== "none") ||
608
+ isolation === "isolate" ||
609
+ (mixBlendMode !== "" && mixBlendMode !== "normal") ||
610
+ /(?:^|\s)(?:layout|paint|strict|content)(?:\s|$)/.test(containment) ||
611
+ /(?:^|,\s*)(?:transform|opacity|filter|perspective)(?:,\s*|$)/.test(
612
+ willChange,
613
+ )
614
+ );
615
+ }
616
+
617
+ function resolveSlideObjectStackingContext(
618
+ element: HTMLElement,
619
+ container: HTMLElement,
620
+ ): HTMLElement {
621
+ let ancestor = element.parentElement;
622
+ while (ancestor && ancestor !== container) {
623
+ if (createsSlideObjectStackingContext(ancestor)) return ancestor;
624
+ ancestor = ancestor.parentElement;
625
+ }
626
+ return container;
627
+ }
628
+
629
+ interface SlideObjectZOrderPeer {
630
+ element: HTMLElement;
631
+ zIndex: number;
632
+ order: number;
633
+ }
634
+
635
+ function getSlideObjectZOrderPeers(
636
+ element: HTMLElement,
637
+ container: HTMLElement,
638
+ ): SlideObjectZOrderPeer[] {
639
+ const containingBlock = resolveSlideObjectContainingBlock(element, container);
640
+ const stackingContext = resolveSlideObjectStackingContext(element, container);
641
+
642
+ return Array.from(
643
+ container.querySelectorAll<HTMLElement>("[data-slide-object-id]"),
644
+ ).flatMap((peer, order) => {
645
+ if (
646
+ peer === element ||
647
+ element.contains(peer) ||
648
+ !isEditableFreeformSlideObject(peer) ||
649
+ resolveSlideObjectContainingBlock(peer, container) !== containingBlock ||
650
+ resolveSlideObjectStackingContext(peer, container) !== stackingContext
651
+ ) {
652
+ return [];
653
+ }
654
+ const zIndex = readSlideObjectZIndex(peer);
655
+ // Negative layers are reserved for slide backgrounds. They must remain
656
+ // below editable objects, never be pulled up by send-to-back normalization.
657
+ if (zIndex < 0) return [];
658
+ return [{ element: peer, zIndex, order }];
659
+ });
660
+ }
661
+
662
+ /**
663
+ * Compute the z-index change that puts `element` in front of / behind every
664
+ * other freeform object inside `container`. Returns null when nothing needs
665
+ * to change.
666
+ */
667
+ export function computeSlideObjectZOrder(
668
+ element: HTMLElement,
669
+ container: HTMLElement,
670
+ target: SlideObjectZOrderTarget,
671
+ ): SlideObjectZOrderChange | null {
672
+ if (!isEditableFreeformSlideObject(element)) return null;
673
+ const peers = getSlideObjectZOrderPeers(element, container);
674
+
675
+ if (peers.length === 0) return null;
676
+
677
+ const peerZIndexes = peers.map((peer) => peer.zIndex);
678
+ const currentValue = readSlideObjectZIndex(element);
679
+
680
+ if (target === "front") {
681
+ const value = Math.max(...peerZIndexes) + 1;
682
+ return value === currentValue ? null : { value, shiftPeers: [] };
351
683
  }
352
684
 
353
- width = Math.max(minSize, width);
354
- height = Math.max(minSize, height);
685
+ const minPeer = Math.min(...peerZIndexes);
686
+ const hasTiedPeers = new Set(peerZIndexes).size !== peers.length;
687
+ if (minPeer - 1 >= 0 && !hasTiedPeers) {
688
+ const value = minPeer - 1;
689
+ return value === currentValue ? null : { value, shiftPeers: [] };
690
+ }
691
+
692
+ // No room below zero, or an existing tie needs repair. Keep reserved negative
693
+ // layers untouched and give every editable peer a stable unique position.
694
+ const orderedPeers = [...peers].sort(
695
+ (left, right) => left.zIndex - right.zIndex || left.order - right.order,
696
+ );
697
+ return {
698
+ value: 0,
699
+ shiftPeers: orderedPeers.map((peer, index) => ({
700
+ element: peer.element,
701
+ value: index + 1,
702
+ })),
703
+ };
704
+ }
705
+
706
+ export interface SlideObjectMoveMember {
707
+ objectId: string;
708
+ element: HTMLElement;
709
+ start: SlideObjectGeometry;
710
+ }
711
+
712
+ function normalizeSlideObjectRoots(elements: HTMLElement[]): HTMLElement[] {
713
+ const uniqueElements = Array.from(new Set(elements));
714
+ return uniqueElements.filter(
715
+ (element) =>
716
+ !uniqueElements.some(
717
+ (candidate) => candidate !== element && candidate.contains(element),
718
+ ),
719
+ );
720
+ }
721
+
722
+ /**
723
+ * Snapshot the movable members of a multi-selection, keyed by durable object id.
724
+ * Elements that are not absolutely positioned cannot move and are excluded.
725
+ */
726
+ export function collectMovableSlideObjects(
727
+ elements: HTMLElement[],
728
+ getGeometry: (element: HTMLElement) => SlideObjectGeometry,
729
+ ): SlideObjectMoveMember[] {
730
+ const seen = new Set<string>();
731
+ const members: SlideObjectMoveMember[] = [];
732
+ for (const element of normalizeSlideObjectRoots(elements)) {
733
+ const objectId = element.getAttribute("data-slide-object-id");
734
+ if (!objectId || seen.has(objectId)) continue;
735
+ if (
736
+ (element.style.position || window.getComputedStyle(element).position) !==
737
+ "absolute"
738
+ ) {
739
+ continue;
740
+ }
741
+ seen.add(objectId);
742
+ members.push({ objectId, element, start: getGeometry(element) });
743
+ }
744
+ return members;
745
+ }
746
+
747
+ /** Apply one shared delta to every member, relative to its captured start. */
748
+ export function applySlideObjectMoveDelta(
749
+ members: SlideObjectMoveMember[],
750
+ deltaX: number,
751
+ deltaY: number,
752
+ applyGeometry: (element: HTMLElement, geometry: SlideObjectGeometry) => void,
753
+ ): void {
754
+ for (const member of members) {
755
+ applyGeometry(member.element, {
756
+ ...member.start,
757
+ x: member.start.x + deltaX,
758
+ y: member.start.y + deltaY,
759
+ });
760
+ }
761
+ }
762
+
763
+ export interface CopiedSlideObjects {
764
+ html: string[];
765
+ }
766
+
767
+ export function copySlideObjects(elements: HTMLElement[]): CopiedSlideObjects {
355
768
  return {
356
- width,
357
- height,
358
- x: fromWest ? start.x + start.width - width : start.x,
359
- y: fromNorth ? start.y + start.height - height : start.y,
769
+ html: normalizeSlideObjectRoots(elements).map((element) => {
770
+ const clone = cloneSlideObject(element);
771
+ return clone.outerHTML;
772
+ }),
360
773
  };
361
774
  }
362
775
 
363
- export function escapedEditingSelection<T>(
364
- editing: T | null,
365
- selected: T | null,
366
- ): { editing: null; selected: T | null } {
367
- return { editing: null, selected: editing ?? selected };
776
+ export const SLIDE_OBJECT_PASTE_OFFSET = 16;
777
+
778
+ function offsetInlinePx(
779
+ element: HTMLElement,
780
+ property: "left" | "top",
781
+ offset: number,
782
+ ): void {
783
+ const value = Number.parseFloat(element.style[property]);
784
+ if (!Number.isFinite(value)) return;
785
+ element.style[property] = `${value + offset}px`;
786
+ }
787
+
788
+ /**
789
+ * Rebuild pasted objects from stored HTML with fresh ids and a cascade offset,
790
+ * so a paste never collides exactly with its source.
791
+ */
792
+ export function buildPastedSlideObjects(
793
+ copied: CopiedSlideObjects,
794
+ doc: Document,
795
+ offset: number = SLIDE_OBJECT_PASTE_OFFSET,
796
+ ): HTMLElement[] {
797
+ const pasted: HTMLElement[] = [];
798
+ const occupiedDomIds = new Set(
799
+ Array.from(doc.querySelectorAll<HTMLElement>("[id]")).map(
800
+ (element) => element.id,
801
+ ),
802
+ );
803
+ for (const html of copied.html) {
804
+ const template = doc.createElement("template");
805
+ template.innerHTML = html;
806
+ const element = template.content.firstElementChild;
807
+ if (!(element instanceof HTMLElement)) continue;
808
+ remintSlideObjectDomIds(element, occupiedDomIds);
809
+ // Remint every persisted id (root + nested) the same way cloneSlideObject
810
+ // does, so a pasted object never resolves to its source via selector.
811
+ element.setAttribute("data-slide-object-id", createSlideObjectId());
812
+ element
813
+ .querySelectorAll<HTMLElement>("[data-slide-object-id]")
814
+ .forEach((descendant) => {
815
+ descendant.setAttribute("data-slide-object-id", createSlideObjectId());
816
+ });
817
+ offsetInlinePx(element, "left", offset);
818
+ offsetInlinePx(element, "top", offset);
819
+ pasted.push(element);
820
+ }
821
+ return pasted;
368
822
  }