@agent-native/core 0.114.12 → 0.114.15
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.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +21 -0
- package/corpus/core/docs/content/deployment.mdx +1 -1
- package/corpus/core/docs/content/frames.mdx +1 -1
- package/corpus/core/docs/content/integrations.mdx +27 -27
- package/corpus/core/docs/content/pr-visual-recap.mdx +17 -13
- package/corpus/core/docs/content/workspace-management.mdx +1 -1
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/agent/engine/builder-engine.ts +4 -0
- package/corpus/core/src/application-state/index.ts +3 -0
- package/corpus/core/src/application-state/script-helpers.ts +12 -1
- package/corpus/core/src/application-state/store.ts +216 -15
- package/corpus/core/src/client/AgentPanel.tsx +10 -4
- package/corpus/core/src/client/ConnectBuilderCard.tsx +5 -1
- package/corpus/core/src/client/chat/markdown-renderer.tsx +3 -5
- package/corpus/core/src/client/error-format.ts +2 -1
- package/corpus/core/src/client/index.ts +1 -0
- package/corpus/core/src/client/integrations/IntegrationsPanel.tsx +2 -18
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +43 -32
- package/corpus/core/src/client/resources/mcp-integration-catalog.ts +4 -2
- package/corpus/core/src/client/settings/BackgroundAgentSection.tsx +5 -1
- package/corpus/core/src/client/settings/SettingsPanel.tsx +4 -4
- package/corpus/core/src/client/settings/useBuilderStatus.ts +3 -0
- package/corpus/core/src/db/client.ts +44 -1
- package/corpus/core/src/localization/default-messages.ts +1 -7
- package/corpus/core/src/server/builder-browser.ts +4 -0
- package/corpus/core/src/server/builder-design-systems.ts +6 -1
- package/corpus/core/src/server/fusion-app.ts +5 -1
- package/corpus/core/src/shared/builder-link-tracking.ts +41 -0
- package/corpus/core/src/shared/index.ts +1 -0
- package/corpus/templates/analytics/AGENTS.md +10 -1
- package/corpus/templates/analytics/actions/save-dashboard-report-subscription.ts +10 -2
- package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +5 -4
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +20 -32
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/report-panel-window.ts +62 -0
- package/corpus/templates/analytics/changelog/2026-07-21-chart-series-controls-stay-open-while-moving-from-the-legend.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-21-dashboard-email-captures-complete-large-dashboards-in-chunks.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +4 -4
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +1 -8
- package/corpus/templates/analytics/server/lib/dashboard-report-subscriptions.ts +39 -7
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +341 -251
- package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +114 -0
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +84 -6
- package/corpus/templates/analytics/server/plugins/db.ts +13 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +10 -4
- package/corpus/templates/clips/changelog/2026-07-21-choose-which-whisper-model-to-use-in-settings.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-21-fixed-organization-recording-visibility-default.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-21-live-dictation-text-now-appears-above-the-desktop-recording-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-21-meeting-notes-stop-when-calls-end.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +66 -75
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +2 -0
- package/corpus/templates/clips/desktop/src/hooks/useWhisperSettings.ts +172 -0
- package/corpus/templates/clips/desktop/src/lib/meeting-call-app.ts +41 -0
- package/corpus/templates/clips/desktop/src/lib/silence-events.ts +1 -0
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +14 -3
- package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +25 -0
- package/corpus/templates/clips/desktop/src/shared/config.ts +1 -0
- package/corpus/templates/clips/desktop/src/styles.css +100 -7
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +3 -4
- package/corpus/templates/clips/desktop/src-tauri/src/config.rs +32 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +3 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +11 -4
- package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +268 -20
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_model.rs +285 -164
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +18 -7
- package/corpus/templates/clips/learnings.defaults.md +1 -1
- package/corpus/templates/clips/server/plugins/db.ts +11 -0
- package/corpus/templates/clips/shared/builder-credits.ts +1 -1
- package/corpus/templates/design/AGENTS.md +7 -3
- package/corpus/templates/design/actions/begin-node-rewrite-request.ts +78 -0
- package/corpus/templates/design/actions/cancel-node-rewrite-request.ts +17 -7
- package/corpus/templates/design/actions/propose-node-rewrite.ts +26 -18
- package/corpus/templates/design/actions/resolve-node-rewrite.ts +67 -19
- package/corpus/templates/design/app/components/design/FusionAppBanner.tsx +9 -1
- package/corpus/templates/design/app/components/design/edit-panel/component-section.tsx +5 -1
- package/corpus/templates/design/app/components/visual-editor/NodeRewriteProposal.tsx +1 -4
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +2 -6
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +9 -1
- package/corpus/templates/design/shared/node-rewrite.ts +32 -0
- package/corpus/templates/forms/app/components/layout/Sidebar.tsx +6 -3
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +11 -18
- package/corpus/templates/forms/app/pages/FormsListPage.tsx +9 -5
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +9 -1
- package/corpus/templates/slides/app/context/DeckContext.tsx +18 -0
- package/corpus/templates/slides/changelog/2026-07-21-decks-list-now-refreshes-when-you-switch-organizations.md +6 -0
- package/corpus/templates/tasks/app/components/custom-fields/FieldCreateBar.tsx +13 -19
- package/corpus/templates/tasks/app/components/custom-fields/FieldListRow.tsx +27 -22
- package/corpus/templates/tasks/app/components/custom-fields/FieldsList.tsx +26 -22
- package/corpus/templates/tasks/app/components/custom-fields/FieldsListSkeleton.tsx +4 -1
- package/corpus/templates/tasks/app/components/custom-fields/FieldsPage.tsx +4 -2
- package/corpus/templates/tasks/app/components/custom-fields/editor/FieldEditorSidebar.tsx +12 -3
- package/corpus/templates/tasks/app/components/custom-fields/editor/FieldTitleSection.tsx +6 -3
- package/corpus/templates/tasks/app/components/custom-fields/editor/config/CurrencyConfigControl.tsx +5 -2
- package/corpus/templates/tasks/app/components/custom-fields/editor/config/NumberConfigControl.tsx +5 -2
- package/corpus/templates/tasks/app/components/custom-fields/editor/config/PercentConfigControl.tsx +4 -1
- package/corpus/templates/tasks/app/components/custom-fields/editor/config/SelectConfigControl.tsx +17 -7
- package/corpus/templates/tasks/app/components/custom-fields/editor/config/select-colors.ts +37 -9
- package/corpus/templates/tasks/app/components/custom-fields/field-types.ts +23 -0
- package/corpus/templates/tasks/app/components/inbox/InboxList.tsx +24 -16
- package/corpus/templates/tasks/app/components/inbox/InboxListPage.tsx +5 -3
- package/corpus/templates/tasks/app/components/inbox/InboxListRow.tsx +7 -5
- package/corpus/templates/tasks/app/components/layout/Header.tsx +10 -10
- package/corpus/templates/tasks/app/components/layout/Layout.tsx +11 -7
- package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +17 -10
- package/corpus/templates/tasks/app/components/shared/AddListItemInput.tsx +14 -8
- package/corpus/templates/tasks/app/components/shared/AlertDialog.tsx +6 -2
- package/corpus/templates/tasks/app/components/shared/BulkDeleteDialog.tsx +13 -6
- package/corpus/templates/tasks/app/components/shared/ChipSelect.tsx +12 -5
- package/corpus/templates/tasks/app/components/shared/DeleteItemDialog.tsx +6 -6
- package/corpus/templates/tasks/app/components/shared/ListViewHeader.tsx +3 -1
- package/corpus/templates/tasks/app/components/shared/SidePanel.tsx +5 -2
- package/corpus/templates/tasks/app/components/shared/list/ListRowDragHandle.tsx +3 -1
- package/corpus/templates/tasks/app/components/shared/selection/ListSelectionBar.tsx +46 -20
- package/corpus/templates/tasks/app/components/shared/selection/ListSelectionHeaderToggle.tsx +3 -1
- package/corpus/templates/tasks/app/components/shared/selection/ListSelectionToolbar.tsx +10 -6
- package/corpus/templates/tasks/app/components/tasks/TaskList.tsx +24 -13
- package/corpus/templates/tasks/app/components/tasks/TaskListHeaderRow.tsx +4 -1
- package/corpus/templates/tasks/app/components/tasks/TaskListPage.tsx +5 -3
- package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +15 -6
- package/corpus/templates/tasks/app/components/tasks/fields/TaskFieldsSidebar.tsx +11 -6
- package/corpus/templates/tasks/app/components/tasks/fields/TaskTitleSection.tsx +8 -3
- package/corpus/templates/tasks/app/components/tasks/fields/controls/RichTextValueControl.tsx +12 -6
- package/corpus/templates/tasks/app/components/tasks/fields/controls/SingleSelectValueControl.tsx +6 -1
- package/corpus/templates/tasks/app/i18n/en-US.ts +209 -0
- package/corpus/templates/tasks/app/i18n/index.ts +34 -0
- package/corpus/templates/tasks/app/root.tsx +34 -2
- package/corpus/templates/tasks/app/routes/extensions.$id.tsx +2 -1
- package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -1
- package/corpus/templates/tasks/app/routes/fields.tsx +2 -1
- package/corpus/templates/tasks/app/routes/inbox.tsx +2 -1
- package/corpus/templates/tasks/app/routes/tasks.tsx +2 -1
- package/corpus/templates/tasks/app/routes/team.tsx +6 -3
- package/corpus/templates/tasks/changelog/2026-07-20-tasks-is-now-available-in-10-additional-languages.md +6 -0
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +4 -0
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/application-state/index.d.ts +2 -2
- package/dist/application-state/index.d.ts.map +1 -1
- package/dist/application-state/index.js +2 -2
- package/dist/application-state/index.js.map +1 -1
- package/dist/application-state/script-helpers.d.ts +3 -1
- package/dist/application-state/script-helpers.d.ts.map +1 -1
- package/dist/application-state/script-helpers.js +7 -1
- package/dist/application-state/script-helpers.js.map +1 -1
- package/dist/application-state/store.d.ts +7 -1
- package/dist/application-state/store.d.ts.map +1 -1
- package/dist/application-state/store.js +143 -17
- package/dist/application-state/store.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +10 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +5 -1
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
- package/dist/client/chat/markdown-renderer.js +3 -2
- package/dist/client/chat/markdown-renderer.js.map +1 -1
- package/dist/client/error-format.d.ts +1 -1
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +1 -1
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
- package/dist/client/integrations/IntegrationsPanel.js +3 -4
- package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +21 -7
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
- package/dist/client/resources/mcp-integration-catalog.js +2 -2
- package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
- package/dist/client/settings/BackgroundAgentSection.d.ts.map +1 -1
- package/dist/client/settings/BackgroundAgentSection.js +5 -1
- package/dist/client/settings/BackgroundAgentSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.js +1 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +2 -0
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/db/client.d.ts +7 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +39 -1
- package/dist/db/client.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/localization/default-messages.d.ts +0 -6
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +1 -7
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +4 -0
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/builder-design-systems.d.ts.map +1 -1
- package/dist/server/builder-design-systems.js +6 -1
- package/dist/server/builder-design-systems.js.map +1 -1
- package/dist/server/fusion-app.d.ts.map +1 -1
- package/dist/server/fusion-app.js +2 -1
- package/dist/server/fusion-app.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/builder-link-tracking.d.ts +14 -0
- package/dist/shared/builder-link-tracking.d.ts.map +1 -0
- package/dist/shared/builder-link-tracking.js +26 -0
- package/dist/shared/builder-link-tracking.js.map +1 -0
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +1 -0
- package/dist/shared/index.js.map +1 -1
- package/docs/content/deployment.mdx +1 -1
- package/docs/content/frames.mdx +1 -1
- package/docs/content/integrations.mdx +27 -27
- package/docs/content/pr-visual-recap.mdx +17 -13
- package/docs/content/workspace-management.mdx +1 -1
- package/package.json +3 -2
- package/src/agent/engine/builder-engine.ts +4 -0
- package/src/application-state/index.ts +3 -0
- package/src/application-state/script-helpers.ts +12 -1
- package/src/application-state/store.ts +216 -15
- package/src/client/AgentPanel.tsx +10 -4
- package/src/client/ConnectBuilderCard.tsx +5 -1
- package/src/client/chat/markdown-renderer.tsx +3 -5
- package/src/client/error-format.ts +2 -1
- package/src/client/index.ts +1 -0
- package/src/client/integrations/IntegrationsPanel.tsx +2 -18
- package/src/client/resources/McpIntegrationDialog.tsx +43 -32
- package/src/client/resources/mcp-integration-catalog.ts +4 -2
- package/src/client/settings/BackgroundAgentSection.tsx +5 -1
- package/src/client/settings/SettingsPanel.tsx +4 -4
- package/src/client/settings/useBuilderStatus.ts +3 -0
- package/src/db/client.ts +44 -1
- package/src/localization/default-messages.ts +1 -7
- package/src/server/builder-browser.ts +4 -0
- package/src/server/builder-design-systems.ts +6 -1
- package/src/server/fusion-app.ts +5 -1
- package/src/shared/builder-link-tracking.ts +41 -0
- package/src/shared/index.ts +1 -0
|
@@ -322,33 +322,33 @@ endpoint is reachable or cataloged, but a live provider authorization or tool
|
|
|
322
322
|
session still needs to be completed. **Restricted** entries remain discoverable
|
|
323
323
|
but need provider or administrator setup before a generic connection can work.
|
|
324
324
|
|
|
325
|
-
| Integration
|
|
326
|
-
|
|
|
327
|
-
| [Context7](https://context7.com/)
|
|
328
|
-
| [Sentry](https://docs.sentry.io/product/sentry-mcp/)
|
|
329
|
-
| [Notion](https://developers.notion.com/guides/mcp/get-started-with-mcp)
|
|
330
|
-
| [Semgrep](https://github.com/semgrep/mcp#readme)
|
|
331
|
-
| [Linear](https://linear.app/docs/mcp)
|
|
332
|
-
| [Atlassian Rovo MCP](https://developer.atlassian.com/cloud/rovo-mcp/guides/getting-started/)
|
|
333
|
-
| [Supabase](https://www.builder.io/c/docs/fusion-connect-to-supabase)
|
|
334
|
-
| [Neon](https://www.builder.io/c/docs/fusion-connect-to-neon)
|
|
335
|
-
| [Stripe](https://docs.stripe.com/mcp)
|
|
336
|
-
| [Cloudflare](https://developers.cloudflare.com/agents/model-context-protocol/cloudflare/servers-for-cloudflare/)
|
|
337
|
-
| [GitLab](https://docs.gitlab.com/user/model_context_protocol/mcp_server/)
|
|
338
|
-
| [Figma](https://developers.figma.com/docs/figma-mcp-server/)
|
|
339
|
-
| [Canva](https://www.canva.dev/docs/mcp/)
|
|
340
|
-
| [Vercel](https://vercel.com/docs/agent-resources/vercel-mcp)
|
|
341
|
-
| [GitHub](https://github.com/github/github-mcp-server)
|
|
342
|
-
| [Slack](https://docs.slack.dev/ai/slack-mcp-server/)
|
|
343
|
-
| [Asana](https://developers.asana.com/docs/integrating-with-asanas-mcp-server)
|
|
344
|
-
| [HubSpot](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/integrate-with-the-remote-hubspot-mcp-server)
|
|
345
|
-
| [Intercom](https://developers.intercom.com/docs/guides/mcp)
|
|
346
|
-
| [monday.com](https://developer.monday.com/api-reference/docs/build-on-monday-with-ai)
|
|
347
|
-
| [Webflow](https://developers.webflow.com/mcp/reference/getting-started)
|
|
348
|
-
| [PayPal](https://developer.paypal.com/ai-tools/mcp-server/)
|
|
349
|
-
| [Box](https://developer.box.com/guides/box-mcp)
|
|
350
|
-
| [Netlify](https://docs.netlify.com/build/build-with-ai/agent-setup-guides/agent-setup-overview/)
|
|
351
|
-
| [Zapier MCP](https://help.zapier.com/hc/en-us/articles/36265392843917-Use-Zapier-MCP-with-your-client)
|
|
325
|
+
| Integration | Connection mode | Status | Endpoint | Setup and notes |
|
|
326
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ---------------------------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
327
|
+
| [Context7](https://context7.com/) | No auth | Ready · verified | `https://mcp.context7.com/mcp` | Fetch current library and framework docs. |
|
|
328
|
+
| [Sentry](https://docs.sentry.io/product/sentry-mcp/) | Header / token | Ready · preflight only | `https://mcp.sentry.dev/mcp` | Add a Sentry bearer token as the `Authorization` header. |
|
|
329
|
+
| [Notion](https://developers.notion.com/guides/mcp/get-started-with-mcp) | OAuth | Ready · preflight only | `https://mcp.notion.com/mcp` | User OAuth; enterprise admins can audit or restrict MCP clients. |
|
|
330
|
+
| [Semgrep](https://github.com/semgrep/mcp#readme) | No auth | Ready · preflight only | `https://mcp.semgrep.ai/mcp` | Scan code for security findings. |
|
|
331
|
+
| [Linear](https://linear.app/docs/mcp) | OAuth | Ready · preflight only | `https://mcp.linear.app/mcp` | Authorize the workspace and issue permissions required by the workflow. |
|
|
332
|
+
| [Atlassian Rovo MCP](https://developer.atlassian.com/cloud/rovo-mcp/guides/getting-started/) | OAuth + provider setup | Restricted | `https://mcp.atlassian.com/v1/mcp/authv2` | Admins may need to trust the app origin and enable Rovo Read, Write, and Search permissions. |
|
|
333
|
+
| [Supabase](https://www.builder.io/c/docs/fusion-connect-to-supabase?utm_source=agent-native&utm_medium=docs&utm_campaign=framework&utm_content=fusion_connect_supabase) | OAuth | Ready · preflight only | `https://mcp.supabase.com/mcp` | Authorize the Supabase project and requested backend scopes. |
|
|
334
|
+
| [Neon](https://www.builder.io/c/docs/fusion-connect-to-neon?utm_source=agent-native&utm_medium=docs&utm_campaign=framework&utm_content=fusion_connect_neon) | OAuth | Ready · preflight only | `https://mcp.neon.tech/sse` | Authorize access to the serverless Postgres projects needed by the app. |
|
|
335
|
+
| [Stripe](https://docs.stripe.com/mcp) | OAuth | Ready · preflight only | `https://mcp.stripe.com` | Review payment, customer, and subscription permissions before authorizing. |
|
|
336
|
+
| [Cloudflare](https://developers.cloudflare.com/agents/model-context-protocol/cloudflare/servers-for-cloudflare/) | OAuth | Ready · preflight only | `https://mcp.cloudflare.com/mcp` | Choose the narrowest Cloudflare server and review scopes. |
|
|
337
|
+
| [GitLab](https://docs.gitlab.com/user/model_context_protocol/mcp_server/) | OAuth + provider setup | Beta · restricted | `https://gitlab.com/api/v4/mcp` | A top-level group admin, or the equivalent self-managed instance admin, must allow MCP. |
|
|
338
|
+
| [Figma](https://developers.figma.com/docs/figma-mcp-server/) | Manual setup | Approved clients only · restricted | `https://mcp.figma.com/mcp` | Figma only allows clients in its MCP Catalog. Use the [Figma REST API personal access token fallback](https://developers.figma.com/docs/rest-api/personal-access-tokens/) for file and node reads until Agent Native is approved. |
|
|
339
|
+
| [Canva](https://www.canva.dev/docs/mcp/) | Manual setup | Approved domains · restricted | `https://mcp.canva.com/mcp` | Per-user OAuth; clients must allow Canva's canva.com and canva.ai domains and follow the current redirect setup. |
|
|
340
|
+
| [Vercel](https://vercel.com/docs/agent-resources/vercel-mcp) | Manual setup | Approved clients only · restricted | `https://mcp.vercel.com` | Vercel must list the client as approved before a generic connection will work. |
|
|
341
|
+
| [GitHub](https://github.com/github/github-mcp-server) | Manual setup | Provider setup · restricted | `https://api.githubcopilot.com/mcp/` | Use a registered OAuth app; organizations may also enforce OAuth App Access Policies. |
|
|
342
|
+
| [Slack](https://docs.slack.dev/ai/slack-mcp-server/) | Manual setup | Approved clients only · restricted | `https://mcp.slack.com/mcp` | Requires a fixed registered Slack app; dynamic client registration is not supported. |
|
|
343
|
+
| [Asana](https://developers.asana.com/docs/integrating-with-asanas-mcp-server) | Manual setup | Provider setup · restricted | `https://mcp.asana.com/v2/mcp` | Pre-register an Asana MCP OAuth app before connecting. |
|
|
344
|
+
| [HubSpot](https://developers.hubspot.com/docs/apps/developer-platform/build-apps/integrate-with-the-remote-hubspot-mcp-server) | Manual setup | Provider setup · restricted | `https://mcp.hubspot.com` | Create a HubSpot MCP Auth App with PKCE; the existing HubSpot OAuth connector remains available to app actions. |
|
|
345
|
+
| [Intercom](https://developers.intercom.com/docs/guides/mcp) | OAuth | Ready · preflight only | `https://mcp.intercom.com/mcp` | Available for US-hosted workspaces; confirm region and scopes during authorization. |
|
|
346
|
+
| [monday.com](https://developer.monday.com/api-reference/docs/build-on-monday-with-ai) | OAuth | Ready · preflight only | `https://mcp.monday.com/mcp` | Choose the workspace and permissions to share during authorization. |
|
|
347
|
+
| [Webflow](https://developers.webflow.com/mcp/reference/getting-started) | OAuth | Ready · preflight only | `https://mcp.webflow.com/mcp` | Designer capabilities may install Webflow's Bridge App; Data API access is separate. |
|
|
348
|
+
| [PayPal](https://developer.paypal.com/ai-tools/mcp-server/) | OAuth | Ready · preflight only | `https://mcp.paypal.com/sse` | Review merchant permissions before authorizing the remote server. |
|
|
349
|
+
| [Box](https://developer.box.com/guides/box-mcp) | Manual setup | Beta · restricted | `https://mcp.box.com` | An administrator must enable Box MCP; custom clients also need credentials, a redirect URI, and approved scopes. |
|
|
350
|
+
| [Netlify](https://docs.netlify.com/build/build-with-ai/agent-setup-guides/agent-setup-overview/) | OAuth | Ready · preflight only | `https://netlify-mcp.netlify.app/mcp` | Review site and team permissions before completing OAuth. |
|
|
351
|
+
| [Zapier MCP](https://help.zapier.com/hc/en-us/articles/36265392843917-Use-Zapier-MCP-with-your-client) | Header / token | User-created server · restricted | `https://mcp.zapier.com/api/v1/connect` | Create the MCP server in Zapier, then paste its generated bearer token into the header field. |
|
|
352
352
|
|
|
353
353
|
### Repeatable preflight verification
|
|
354
354
|
|
|
@@ -209,7 +209,8 @@ To reduce cost:
|
|
|
209
209
|
- **Pin a cheaper model.** For Claude, set `VISUAL_RECAP_MODEL=claude-haiku-4-5` for simple or small repos — it's the cheapest Claude tier; `claude-sonnet-5` (the default) is the balanced tier.
|
|
210
210
|
- **Switch to Codex with a cheaper model.** Set `VISUAL_RECAP_AGENT=codex` with `VISUAL_RECAP_MODEL=gpt-5.6-luna` (the cheapest GPT-5.6 tier) or `gpt-5.6-terra` (mid-tier), and set `VISUAL_RECAP_REASONING=low` to cut reasoning-token spend further. Reasoning depth only applies to Codex.
|
|
211
211
|
- **Point at a cheaper OpenAI-compatible provider.** Set `VISUAL_RECAP_AGENT=openai-compatible` with `VISUAL_RECAP_BASE_URL`/`VISUAL_RECAP_MODEL`/`VISUAL_RECAP_API_KEY` targeting DeepSeek, Kimi, or any other OpenAI-compatible endpoint.
|
|
212
|
-
- **
|
|
212
|
+
- **Require an explicit PR label.** Set `VISUAL_RECAP_REQUIRED_LABELS=visual recap` to keep the workflow installed but skip recaps until a PR has that label. Use a comma-separated list, such as `visual recap,recap`, when several labels should opt in. Applying a listed label wakes the workflow immediately.
|
|
213
|
+
- **Recap less often.** Edit the installed workflow's `on.pull_request.types` from `[opened, synchronize, reopened, ready_for_review, labeled, closed]` to `[opened, ready_for_review, closed]` so it recaps only when a PR opens and when it merges, instead of on every push.
|
|
213
214
|
|
|
214
215
|
Every run records what it actually spent: the workflow's `recap usage` step attaches token and cost usage to the published plan, so you can look at real spend per run instead of guessing.
|
|
215
216
|
|
|
@@ -256,16 +257,17 @@ real token.
|
|
|
256
257
|
|
|
257
258
|
### Optional (only if you change defaults)
|
|
258
259
|
|
|
259
|
-
| Secret / variable
|
|
260
|
-
|
|
|
261
|
-
| `OPENAI_API_KEY`
|
|
262
|
-
| `VISUAL_RECAP_API_KEY`
|
|
263
|
-
| `VISUAL_RECAP_AGENT`
|
|
264
|
-
| `VISUAL_RECAP_BASE_URL`
|
|
265
|
-
| `VISUAL_RECAP_MODEL`
|
|
266
|
-
| `VISUAL_RECAP_REASONING`
|
|
267
|
-
| `
|
|
268
|
-
| `
|
|
260
|
+
| Secret / variable | Default | When you need it |
|
|
261
|
+
| ------------------------------ | ------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
262
|
+
| `OPENAI_API_KEY` | — | Secret. Set together with `VISUAL_RECAP_AGENT=codex` to run the recap with Codex instead. |
|
|
263
|
+
| `VISUAL_RECAP_API_KEY` | — | Secret. Set with `VISUAL_RECAP_AGENT=openai-compatible` for DeepSeek, Kimi, or another OpenAI-compatible provider. |
|
|
264
|
+
| `VISUAL_RECAP_AGENT` | `claude` | Variable. Selects the coding-agent backend (`claude`, `codex`, or `openai-compatible`). |
|
|
265
|
+
| `VISUAL_RECAP_BASE_URL` | required for compatible backend | Variable. HTTP(S) base URL for the OpenAI-compatible backend; credentials are rejected. |
|
|
266
|
+
| `VISUAL_RECAP_MODEL` | `claude-sonnet-5` for Claude; required for compatible backend | Variable. Provider model id for OpenAI-compatible backends (required there). For Claude, unset now defaults to `claude-sonnet-5` — set it to override, e.g. `claude-haiku-4-5` for the cheapest tier. For Codex, unset uses the Codex CLI's own default. |
|
|
267
|
+
| `VISUAL_RECAP_REASONING` | each model's default | Variable. Reasoning depth: `none`, `minimal`, `low`, `medium`, `high`, or `xhigh`. Applies to the Codex backend. |
|
|
268
|
+
| `VISUAL_RECAP_REQUIRED_LABELS` | — | Variable. Comma-separated PR labels. When set, the gate skips until the PR has at least one listed label, for example `visual recap`. |
|
|
269
|
+
| `RECAP_CLI_VERSION` | `latest` | Variable. Pins the `@agent-native/recap-cli` version the workflow installs — e.g. `1.5.0`. See [Version pinning](#version-pinning-copy-variant). |
|
|
270
|
+
| `PLAN_RECAP_APP_URL` | `https://plan.agent-native.com` | Secret. Only when self-hosting the Plans app at a different origin. |
|
|
269
271
|
|
|
270
272
|
The workflow auto-detects how to invoke its helper CLI (local source inside this monorepo, the published `@agent-native/recap-cli` elsewhere), so there is no `RECAP_CLI` variable to set.
|
|
271
273
|
|
|
@@ -510,10 +512,11 @@ name: PR Visual Recap
|
|
|
510
512
|
|
|
511
513
|
on:
|
|
512
514
|
pull_request:
|
|
513
|
-
types: [opened, synchronize, reopened, ready_for_review, closed]
|
|
515
|
+
types: [opened, synchronize, reopened, ready_for_review, labeled, closed]
|
|
514
516
|
|
|
515
517
|
jobs:
|
|
516
518
|
visual-recap:
|
|
519
|
+
if: github.event.action != 'labeled' || vars.VISUAL_RECAP_REQUIRED_LABELS != ''
|
|
517
520
|
permissions:
|
|
518
521
|
actions: write
|
|
519
522
|
contents: read
|
|
@@ -535,10 +538,11 @@ jobs:
|
|
|
535
538
|
skill-source: ${{ vars.VISUAL_RECAP_SKILL_SOURCE || 'auto' }}
|
|
536
539
|
runs-on: ${{ vars.VISUAL_RECAP_RUNS_ON || '"ubuntu-latest"' }}
|
|
537
540
|
gate-runs-on: ${{ vars.VISUAL_RECAP_GATE_RUNS_ON || 'ubuntu-latest' }}
|
|
541
|
+
required-labels: ${{ vars.VISUAL_RECAP_REQUIRED_LABELS || '' }}
|
|
538
542
|
# cli-version: "latest" # pin to a specific @agent-native/recap-cli version
|
|
539
543
|
```
|
|
540
544
|
|
|
541
|
-
The same secrets and variables described in [Secrets and variables](#secrets-and-variables) apply — set them in your repo settings the same way as for the copy variant. The reusable caller forwards `VISUAL_RECAP_RUNS_ON` through its JSON `runs-on` input for the full recap job
|
|
545
|
+
The same secrets and variables described in [Secrets and variables](#secrets-and-variables) apply — set them in your repo settings the same way as for the copy variant. The reusable caller forwards `VISUAL_RECAP_RUNS_ON` through its JSON `runs-on` input for the full recap job, `VISUAL_RECAP_GATE_RUNS_ON` through its plain-label `gate-runs-on` input for the metadata-only gate, and `VISUAL_RECAP_REQUIRED_LABELS` through `required-labels` for optional PR-label gating. The gate runner input is used only for trusted same-repository authors; other PRs use GitHub-hosted `ubuntu-latest`. Omitting the runner inputs preserves their `ubuntu-latest` defaults; omitting required labels preserves automatic recap behavior.
|
|
542
546
|
|
|
543
547
|
### Installing via the CLI
|
|
544
548
|
|
|
@@ -91,7 +91,7 @@ Keep branches short-lived. Long-lived branches diverge from main and create pain
|
|
|
91
91
|
|
|
92
92
|
### Non-Developer Branching
|
|
93
93
|
|
|
94
|
-
Not everyone who needs to make changes is comfortable with git. [Builder.io](https://www.builder.io) supports a visual branching model that maps to git branches under the hood — useful for content and copy changes, layout adjustments, design iterations, and A/B testing without a dev environment.
|
|
94
|
+
Not everyone who needs to make changes is comfortable with git. [Builder.io](https://www.builder.io?utm_source=agent-native&utm_medium=docs&utm_campaign=framework&utm_content=workspace_management) supports a visual branching model that maps to git branches under the hood — useful for content and copy changes, layout adjustments, design iterations, and A/B testing without a dev environment.
|
|
95
95
|
|
|
96
96
|
## Code Ownership
|
|
97
97
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.114.
|
|
3
|
+
"version": "0.114.15",
|
|
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": {
|
|
@@ -150,6 +150,7 @@
|
|
|
150
150
|
"./embedding/react": "./dist/embedding/react.js",
|
|
151
151
|
"./onboarding": "./dist/onboarding/index.js",
|
|
152
152
|
"./shared": "./dist/shared/index.js",
|
|
153
|
+
"./shared/builder-link-tracking": "./dist/shared/builder-link-tracking.js",
|
|
153
154
|
"./voice": "./dist/voice/index.js",
|
|
154
155
|
"./scripts": "./dist/scripts/index.js",
|
|
155
156
|
"./guards": {
|
|
@@ -380,7 +381,7 @@
|
|
|
380
381
|
"y-protocols": "^1.0.7",
|
|
381
382
|
"yjs": "^13.6.31",
|
|
382
383
|
"zod": "^4.3.6",
|
|
383
|
-
"@agent-native/recap-cli": "0.
|
|
384
|
+
"@agent-native/recap-cli": "0.5.0",
|
|
384
385
|
"@agent-native/toolkit": "^0.8.2"
|
|
385
386
|
},
|
|
386
387
|
"devDependencies": {
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
getBuilderGatewayBaseUrl,
|
|
21
21
|
recordBuilderCredentialAuthFailure,
|
|
22
22
|
} from "../../server/credential-provider.js";
|
|
23
|
+
import { applyBuilderUtmTrackingParams } from "../../shared/builder-link-tracking.js";
|
|
23
24
|
import {
|
|
24
25
|
normalizeReasoningEffortForModel,
|
|
25
26
|
type ReasoningEffort,
|
|
@@ -112,6 +113,9 @@ async function buildUpgradeUrl(): Promise<string> {
|
|
|
112
113
|
url.searchParams.set("agentNativeConnectSource", "gateway_quota_upgrade");
|
|
113
114
|
url.searchParams.set("agentNativeFlow", "connect_llm");
|
|
114
115
|
url.searchParams.set("framework", "agent-native");
|
|
116
|
+
applyBuilderUtmTrackingParams(url.searchParams, {
|
|
117
|
+
content: "gateway_quota_upgrade",
|
|
118
|
+
});
|
|
115
119
|
return url.toString();
|
|
116
120
|
}
|
|
117
121
|
|
|
@@ -5,8 +5,10 @@ export {
|
|
|
5
5
|
appStatePut,
|
|
6
6
|
appStateDelete,
|
|
7
7
|
appStateCompareAndSet,
|
|
8
|
+
appStateCompareAndSetMany,
|
|
8
9
|
appStateList,
|
|
9
10
|
appStateDeleteByPrefix,
|
|
11
|
+
type AppStateCompareAndSetOperation,
|
|
10
12
|
} from "./store.js";
|
|
11
13
|
|
|
12
14
|
// Emitter (for SSE wiring)
|
|
@@ -35,6 +37,7 @@ export {
|
|
|
35
37
|
writeAppState,
|
|
36
38
|
deleteAppState,
|
|
37
39
|
compareAndSetAppState,
|
|
40
|
+
compareAndSetManyAppState,
|
|
38
41
|
listAppState,
|
|
39
42
|
deleteAppStateByPrefix,
|
|
40
43
|
readAppStateForCurrentTab,
|
|
@@ -18,8 +18,10 @@ import {
|
|
|
18
18
|
appStatePut,
|
|
19
19
|
appStateDelete,
|
|
20
20
|
appStateCompareAndSet,
|
|
21
|
+
appStateCompareAndSetMany,
|
|
21
22
|
appStateList,
|
|
22
23
|
appStateDeleteByPrefix,
|
|
24
|
+
type AppStateCompareAndSetOperation,
|
|
23
25
|
} from "./store.js";
|
|
24
26
|
|
|
25
27
|
/**
|
|
@@ -74,7 +76,7 @@ export async function deleteAppState(key: string): Promise<boolean> {
|
|
|
74
76
|
|
|
75
77
|
export async function compareAndSetAppState(
|
|
76
78
|
key: string,
|
|
77
|
-
expectedValue: Record<string, unknown
|
|
79
|
+
expectedValue: Record<string, unknown> | null,
|
|
78
80
|
nextValue: Record<string, unknown> | null,
|
|
79
81
|
): Promise<boolean> {
|
|
80
82
|
const sessionId = await resolveSessionId();
|
|
@@ -83,6 +85,15 @@ export async function compareAndSetAppState(
|
|
|
83
85
|
});
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
export async function compareAndSetManyAppState(
|
|
89
|
+
operations: readonly AppStateCompareAndSetOperation[],
|
|
90
|
+
): Promise<boolean> {
|
|
91
|
+
const sessionId = await resolveSessionId();
|
|
92
|
+
return appStateCompareAndSetMany(sessionId, operations, {
|
|
93
|
+
requestSource: "agent",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
export async function listAppState(
|
|
87
98
|
prefix: string,
|
|
88
99
|
): Promise<Array<{ key: string; value: Record<string, unknown> }>> {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getDbExec,
|
|
3
|
+
getDialect,
|
|
3
4
|
isLocalDatabase,
|
|
4
5
|
isConnectionError,
|
|
5
6
|
isPostgres,
|
|
6
7
|
intType,
|
|
8
|
+
type DbExec,
|
|
7
9
|
} from "../db/client.js";
|
|
8
10
|
import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
9
11
|
import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
|
|
@@ -194,39 +196,238 @@ export async function appStateDelete(
|
|
|
194
196
|
export async function appStateCompareAndSet(
|
|
195
197
|
sessionId: string,
|
|
196
198
|
key: string,
|
|
197
|
-
expectedValue: Record<string, unknown
|
|
199
|
+
expectedValue: Record<string, unknown> | null,
|
|
198
200
|
nextValue: Record<string, unknown> | null,
|
|
199
201
|
options?: StoreWriteOptions,
|
|
200
202
|
): Promise<boolean> {
|
|
201
203
|
await ensureTable();
|
|
202
204
|
const client = getDbExec();
|
|
205
|
+
const changed = await executeAppStateCompareAndSet(
|
|
206
|
+
client,
|
|
207
|
+
sessionId,
|
|
208
|
+
key,
|
|
209
|
+
expectedValue,
|
|
210
|
+
nextValue,
|
|
211
|
+
);
|
|
212
|
+
if (changed) {
|
|
213
|
+
if (nextValue === null) {
|
|
214
|
+
emitAppStateDelete(key, options?.requestSource, sessionId);
|
|
215
|
+
} else {
|
|
216
|
+
emitAppStateChange(key, options?.requestSource, sessionId);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return changed;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export interface AppStateCompareAndSetOperation {
|
|
223
|
+
key: string;
|
|
224
|
+
expectedValue: Record<string, unknown> | null;
|
|
225
|
+
nextValue: Record<string, unknown> | null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const APP_STATE_CAS_MISMATCH = Symbol("app-state-cas-mismatch");
|
|
229
|
+
|
|
230
|
+
async function executeAppStateCompareAndSet(
|
|
231
|
+
client: DbExec,
|
|
232
|
+
sessionId: string,
|
|
233
|
+
key: string,
|
|
234
|
+
expectedValue: Record<string, unknown> | null,
|
|
235
|
+
nextValue: Record<string, unknown> | null,
|
|
236
|
+
): Promise<boolean> {
|
|
237
|
+
const statement = buildAppStateCompareAndSetStatement(
|
|
238
|
+
sessionId,
|
|
239
|
+
key,
|
|
240
|
+
expectedValue,
|
|
241
|
+
nextValue,
|
|
242
|
+
);
|
|
243
|
+
const result = await client.execute(statement);
|
|
244
|
+
return result.rowsAffected > 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function buildAppStateCompareAndSetStatement(
|
|
248
|
+
sessionId: string,
|
|
249
|
+
key: string,
|
|
250
|
+
expectedValue: Record<string, unknown> | null,
|
|
251
|
+
nextValue: Record<string, unknown> | null,
|
|
252
|
+
): { sql: string; args: unknown[] } {
|
|
253
|
+
if (expectedValue === null) {
|
|
254
|
+
if (nextValue === null) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
"Application state CAS cannot replace absence with absence.",
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
const next = serializeAppStateValue(key, nextValue);
|
|
260
|
+
return {
|
|
261
|
+
sql: isPostgres()
|
|
262
|
+
? `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?) ON CONFLICT (session_id, key) DO NOTHING`
|
|
263
|
+
: `INSERT OR IGNORE INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)`,
|
|
264
|
+
args: [sessionId, key, next, Date.now(), sessionId, key],
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
203
268
|
const expected = JSON.stringify(expectedValue);
|
|
204
269
|
if (nextValue === null) {
|
|
205
|
-
|
|
270
|
+
return {
|
|
206
271
|
sql: `DELETE FROM application_state WHERE session_id = ? AND key = ? AND value = ?`,
|
|
207
272
|
args: [sessionId, key, expected],
|
|
208
|
-
}
|
|
209
|
-
const changed = result.rowsAffected > 0;
|
|
210
|
-
if (changed) emitAppStateDelete(key, options?.requestSource, sessionId);
|
|
211
|
-
return changed;
|
|
273
|
+
};
|
|
212
274
|
}
|
|
213
275
|
|
|
214
|
-
const next =
|
|
276
|
+
const next = serializeAppStateValue(key, nextValue);
|
|
277
|
+
return {
|
|
278
|
+
sql: `UPDATE application_state SET value = ?, updated_at = ? WHERE session_id = ? AND key = ? AND value = ?`,
|
|
279
|
+
args: [next, Date.now(), sessionId, key, expected],
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function buildD1CompareAndSetGuard(
|
|
284
|
+
sessionId: string,
|
|
285
|
+
guardKey: string,
|
|
286
|
+
operation: AppStateCompareAndSetOperation,
|
|
287
|
+
): { sql: string; args: unknown[] } {
|
|
288
|
+
const expected = operation.expectedValue;
|
|
289
|
+
const condition =
|
|
290
|
+
expected === null
|
|
291
|
+
? "NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)"
|
|
292
|
+
: "EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ? AND value = ?)";
|
|
293
|
+
return {
|
|
294
|
+
sql: `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, '{}', ? WHERE NOT (${condition})`,
|
|
295
|
+
args:
|
|
296
|
+
expected === null
|
|
297
|
+
? [sessionId, guardKey, Date.now(), sessionId, operation.key]
|
|
298
|
+
: [
|
|
299
|
+
sessionId,
|
|
300
|
+
guardKey,
|
|
301
|
+
Date.now(),
|
|
302
|
+
sessionId,
|
|
303
|
+
operation.key,
|
|
304
|
+
JSON.stringify(expected),
|
|
305
|
+
],
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function isD1CompareAndSetMismatch(error: unknown): boolean {
|
|
310
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
311
|
+
return /unique constraint failed:\s*application_state\.session_id,\s*application_state\.key/i.test(
|
|
312
|
+
message,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function serializeAppStateValue(
|
|
317
|
+
key: string,
|
|
318
|
+
value: Record<string, unknown>,
|
|
319
|
+
): string {
|
|
320
|
+
const serialized = JSON.stringify(value);
|
|
215
321
|
if (
|
|
216
322
|
!isLocalDatabase() &&
|
|
217
|
-
Buffer.byteLength(
|
|
323
|
+
Buffer.byteLength(serialized, "utf8") > MAX_HOSTED_APP_STATE_VALUE_BYTES
|
|
218
324
|
) {
|
|
219
325
|
throw new Error(
|
|
220
326
|
`application_state value "${key}" is too large for hosted SQL storage. Store large files, base64, or blobs in file storage and write only a URL or handle.`,
|
|
221
327
|
);
|
|
222
328
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
329
|
+
return serialized;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export async function appStateCompareAndSetMany(
|
|
333
|
+
sessionId: string,
|
|
334
|
+
operations: readonly AppStateCompareAndSetOperation[],
|
|
335
|
+
options?: StoreWriteOptions,
|
|
336
|
+
): Promise<boolean> {
|
|
337
|
+
if (operations.length === 0) return true;
|
|
338
|
+
const keys = new Set(operations.map(({ key }) => key));
|
|
339
|
+
if (keys.size !== operations.length) {
|
|
340
|
+
throw new Error("Application state multi-key CAS requires unique keys.");
|
|
341
|
+
}
|
|
342
|
+
for (const { key, nextValue } of operations) {
|
|
343
|
+
if (nextValue) serializeAppStateValue(key, nextValue);
|
|
344
|
+
}
|
|
345
|
+
const orderedOperations = [...operations].sort((a, b) =>
|
|
346
|
+
a.key.localeCompare(b.key),
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
await ensureTable();
|
|
350
|
+
const client = getDbExec();
|
|
351
|
+
if (getDialect() === "d1") {
|
|
352
|
+
if (!client.atomicBatch) {
|
|
353
|
+
throw new Error(
|
|
354
|
+
"D1 application-state CAS requires atomic batch support.",
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
const guardKey = `__agent_native_cas_guard__:${Date.now()}:${Math.random().toString(36).slice(2)}`;
|
|
358
|
+
const guardInsert = {
|
|
359
|
+
sql: `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, '{}', ?)`,
|
|
360
|
+
args: [sessionId, guardKey, Date.now()],
|
|
361
|
+
};
|
|
362
|
+
const guards = orderedOperations.map((operation) =>
|
|
363
|
+
buildD1CompareAndSetGuard(sessionId, guardKey, operation),
|
|
364
|
+
);
|
|
365
|
+
const mutations = orderedOperations.map((operation) =>
|
|
366
|
+
buildAppStateCompareAndSetStatement(
|
|
367
|
+
sessionId,
|
|
368
|
+
operation.key,
|
|
369
|
+
operation.expectedValue,
|
|
370
|
+
operation.nextValue,
|
|
371
|
+
),
|
|
372
|
+
);
|
|
373
|
+
let results;
|
|
374
|
+
try {
|
|
375
|
+
results = await client.atomicBatch([
|
|
376
|
+
guardInsert,
|
|
377
|
+
...guards,
|
|
378
|
+
...mutations,
|
|
379
|
+
{
|
|
380
|
+
sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,
|
|
381
|
+
args: [sessionId, guardKey],
|
|
382
|
+
},
|
|
383
|
+
]);
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (isD1CompareAndSetMismatch(error)) return false;
|
|
386
|
+
throw error;
|
|
387
|
+
}
|
|
388
|
+
const mutationResults = results.slice(
|
|
389
|
+
1 + guards.length,
|
|
390
|
+
1 + guards.length + mutations.length,
|
|
391
|
+
);
|
|
392
|
+
if (
|
|
393
|
+
mutationResults.length !== mutations.length ||
|
|
394
|
+
mutationResults.some((result) => result.rowsAffected !== 1)
|
|
395
|
+
) {
|
|
396
|
+
throw new Error(
|
|
397
|
+
"D1 application-state CAS completed without applying every mutation.",
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
if (!client.transaction) {
|
|
402
|
+
throw new Error("Application state multi-key CAS requires transactions.");
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
await client.transaction(async (tx) => {
|
|
406
|
+
for (const operation of orderedOperations) {
|
|
407
|
+
const changed = await executeAppStateCompareAndSet(
|
|
408
|
+
tx,
|
|
409
|
+
sessionId,
|
|
410
|
+
operation.key,
|
|
411
|
+
operation.expectedValue,
|
|
412
|
+
operation.nextValue,
|
|
413
|
+
);
|
|
414
|
+
if (!changed) throw APP_STATE_CAS_MISMATCH;
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
} catch (error) {
|
|
418
|
+
if (error === APP_STATE_CAS_MISMATCH) return false;
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
for (const { key, nextValue } of operations) {
|
|
424
|
+
if (nextValue === null) {
|
|
425
|
+
emitAppStateDelete(key, options?.requestSource, sessionId);
|
|
426
|
+
} else {
|
|
427
|
+
emitAppStateChange(key, options?.requestSource, sessionId);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return true;
|
|
230
431
|
}
|
|
231
432
|
|
|
232
433
|
export async function appStateList(
|
|
@@ -81,6 +81,7 @@ const MultiTabAssistantChatLazy = lazy(() =>
|
|
|
81
81
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
82
82
|
import { Link, useLocation, useNavigate } from "react-router";
|
|
83
83
|
|
|
84
|
+
import { withBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js";
|
|
84
85
|
import type { AgentChatSurfaceKind } from "./agent-chat-adapter.js";
|
|
85
86
|
import {
|
|
86
87
|
consumeAgentSidebarUrlOpenOverride,
|
|
@@ -670,14 +671,19 @@ function CodeAccessUnavailablePanel({
|
|
|
670
671
|
compact?: boolean;
|
|
671
672
|
}) {
|
|
672
673
|
const { connectUrl: builderConnectUrl } = useBuilderConnectUrl();
|
|
673
|
-
const builderHref =
|
|
674
|
-
secondaryCtaHref
|
|
675
|
-
|
|
674
|
+
const builderHref = secondaryCtaHref
|
|
675
|
+
? withBuilderUtmTrackingParams(secondaryCtaHref, {
|
|
676
|
+
campaign: "product",
|
|
677
|
+
content: "code_access_unavailable_panel",
|
|
678
|
+
})
|
|
679
|
+
: builderConnectUrl
|
|
676
680
|
? withBuilderConnectTrackingParams(builderConnectUrl, {
|
|
677
681
|
source: "code_access_unavailable_panel",
|
|
678
682
|
flow: "background_agent",
|
|
679
683
|
})
|
|
680
|
-
: "https://builder.io"
|
|
684
|
+
: withBuilderUtmTrackingParams("https://builder.io", {
|
|
685
|
+
content: "code_access_unavailable_panel",
|
|
686
|
+
});
|
|
681
687
|
|
|
682
688
|
return (
|
|
683
689
|
<div
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { IconExternalLink, IconLoader2 } from "@tabler/icons-react";
|
|
2
2
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
|
|
4
|
+
import { withBuilderUtmTrackingParams } from "../shared/builder-link-tracking.js";
|
|
4
5
|
import { agentNativePath } from "./api-path.js";
|
|
5
6
|
import { BuilderBMark } from "./builder-mark.js";
|
|
6
7
|
import { getCallbackOrigin } from "./frame.js";
|
|
@@ -307,7 +308,10 @@ export function ConnectBuilderCard({
|
|
|
307
308
|
<div className="mt-3">
|
|
308
309
|
{runResult ? (
|
|
309
310
|
<a
|
|
310
|
-
href={runResult.url
|
|
311
|
+
href={withBuilderUtmTrackingParams(runResult.url, {
|
|
312
|
+
campaign: "product",
|
|
313
|
+
content: "connect_builder_card_branch",
|
|
314
|
+
})}
|
|
311
315
|
target="_blank"
|
|
312
316
|
rel="noopener noreferrer"
|
|
313
317
|
className={cn(
|
|
@@ -28,10 +28,7 @@ import {
|
|
|
28
28
|
smoothStreamingRevealCount,
|
|
29
29
|
splitStreamingTextGraphemes,
|
|
30
30
|
} from "../../shared/streaming-text-smoothing.js";
|
|
31
|
-
import {
|
|
32
|
-
NEW_CHAT_ACTION_HREF,
|
|
33
|
-
BUILDER_SPACE_SETTINGS_URL,
|
|
34
|
-
} from "../error-format.js";
|
|
31
|
+
import { NEW_CHAT_ACTION_HREF } from "../error-format.js";
|
|
35
32
|
import { HighlightedCodeBlock as SharedHighlightedCodeBlock } from "../HighlightedCodeBlock.js";
|
|
36
33
|
import { IframeEmbed, parseEmbedBody } from "../IframeEmbed.js";
|
|
37
34
|
import { cn } from "../utils.js";
|
|
@@ -187,7 +184,8 @@ function isBuilderErrorCtaHref(href: string | undefined): boolean {
|
|
|
187
184
|
return false;
|
|
188
185
|
}
|
|
189
186
|
return (
|
|
190
|
-
url.
|
|
187
|
+
(url.origin === "https://builder.io" &&
|
|
188
|
+
url.pathname === "/account/space") ||
|
|
191
189
|
url.pathname === "/account/billing" ||
|
|
192
190
|
url.pathname === "/account/subscription" ||
|
|
193
191
|
/^\/app\/organizations\/[^/]+\/billing$/.test(url.pathname)
|