@agent-native/core 0.14.8 → 0.15.0
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/README.md +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +30 -9
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +14 -4
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +71 -4
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/types.d.ts +9 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/appearance/actions/change-appearance.d.ts +3 -0
- package/dist/appearance/actions/change-appearance.d.ts.map +1 -0
- package/dist/appearance/actions/change-appearance.js +29 -0
- package/dist/appearance/actions/change-appearance.js.map +1 -0
- package/dist/chat-threads/store.d.ts +53 -2
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +172 -12
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +114 -37
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +30 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/workspace-dev.d.ts +25 -1
- package/dist/cli/workspace-dev.d.ts.map +1 -1
- package/dist/cli/workspace-dev.js +275 -49
- package/dist/cli/workspace-dev.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +23 -4
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +276 -53
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AppearancePicker.d.ts +11 -0
- package/dist/client/AppearancePicker.d.ts.map +1 -0
- package/dist/client/AppearancePicker.js +16 -0
- package/dist/client/AppearancePicker.js.map +1 -0
- package/dist/client/AssistantChat.d.ts +35 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +315 -32
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/ConnectBuilderCard.d.ts.map +1 -1
- package/dist/client/ConnectBuilderCard.js +5 -2
- package/dist/client/ConnectBuilderCard.js.map +1 -1
- package/dist/client/ErrorBoundary.d.ts.map +1 -1
- package/dist/client/ErrorBoundary.js +8 -10
- package/dist/client/ErrorBoundary.js.map +1 -1
- package/dist/client/FeedbackButton.d.ts.map +1 -1
- package/dist/client/FeedbackButton.js +1 -1
- package/dist/client/FeedbackButton.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts +13 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +217 -38
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
- package/dist/client/NewWorkspaceAppFlow.js +37 -14
- package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +5 -0
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +4 -0
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/agent-sidebar-state.d.ts +12 -0
- package/dist/client/agent-sidebar-state.d.ts.map +1 -1
- package/dist/client/agent-sidebar-state.js +8 -0
- package/dist/client/agent-sidebar-state.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +175 -3
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/appearance.d.ts +40 -0
- package/dist/client/appearance.d.ts.map +1 -0
- package/dist/client/appearance.js +114 -0
- package/dist/client/appearance.js.map +1 -0
- package/dist/client/builder-frame.d.ts +1 -0
- package/dist/client/builder-frame.d.ts.map +1 -1
- package/dist/client/builder-frame.js +19 -9
- package/dist/client/builder-frame.js.map +1 -1
- package/dist/client/components/CodeRequiredDialog.d.ts.map +1 -1
- package/dist/client/components/CodeRequiredDialog.js +10 -2
- package/dist/client/components/CodeRequiredDialog.js.map +1 -1
- package/dist/client/components/ui/dropdown-menu.js +2 -2
- package/dist/client/components/ui/dropdown-menu.js.map +1 -1
- package/dist/client/components/ui/hover-card.js +1 -1
- package/dist/client/components/ui/hover-card.js.map +1 -1
- package/dist/client/components/ui/popover.js +1 -1
- package/dist/client/components/ui/popover.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts +7 -0
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +63 -32
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +5 -0
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +36 -6
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
- package/dist/client/composer/useVoiceDictation.js +13 -1
- package/dist/client/composer/useVoiceDictation.js.map +1 -1
- package/dist/client/error-format.d.ts +3 -2
- package/dist/client/error-format.d.ts.map +1 -1
- package/dist/client/error-format.js +9 -2
- package/dist/client/error-format.js.map +1 -1
- package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/client/extensions/ExtensionViewer.js +24 -2
- package/dist/client/extensions/ExtensionViewer.js.map +1 -1
- package/dist/client/index.d.ts +8 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +7 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/onboarding/OnboardingPanel.js +1 -0
- package/dist/client/onboarding/OnboardingPanel.js.map +1 -1
- package/dist/client/org/InvitationBanner.d.ts.map +1 -1
- package/dist/client/org/InvitationBanner.js +23 -2
- package/dist/client/org/InvitationBanner.js.map +1 -1
- package/dist/client/org/OrgSwitcher.d.ts +5 -4
- package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/client/org/OrgSwitcher.js +57 -9
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/org/hooks.d.ts.map +1 -1
- package/dist/client/org/hooks.js +10 -6
- package/dist/client/org/hooks.js.map +1 -1
- package/dist/client/org/workspace-app-links.d.ts +31 -0
- package/dist/client/org/workspace-app-links.d.ts.map +1 -0
- package/dist/client/org/workspace-app-links.js +268 -0
- package/dist/client/org/workspace-app-links.js.map +1 -0
- package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +18 -5
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/client/resources/use-resources.d.ts +18 -13
- package/dist/client/resources/use-resources.d.ts.map +1 -1
- package/dist/client/resources/use-resources.js +24 -6
- package/dist/client/resources/use-resources.js.map +1 -1
- package/dist/client/settings/BackgroundAgentSection.d.ts.map +1 -1
- package/dist/client/settings/BackgroundAgentSection.js +9 -1
- package/dist/client/settings/BackgroundAgentSection.js.map +1 -1
- package/dist/client/settings/BrowserSection.d.ts.map +1 -1
- package/dist/client/settings/BrowserSection.js +16 -1
- package/dist/client/settings/BrowserSection.js.map +1 -1
- package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
- package/dist/client/settings/SettingsPanel.js +4 -1
- package/dist/client/settings/SettingsPanel.js.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.d.ts.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +5 -5
- package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +8 -0
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +50 -13
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.spec.d.ts +2 -0
- package/dist/client/settings/useBuilderStatus.spec.d.ts.map +1 -0
- package/dist/client/settings/useBuilderStatus.spec.js +64 -0
- package/dist/client/settings/useBuilderStatus.spec.js.map +1 -0
- package/dist/client/sharing/ShareButton.d.ts +5 -0
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +60 -6
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/client/theme.js +1 -1
- package/dist/client/theme.js.map +1 -1
- package/dist/client/transcription/BuilderTranscriptionCta.d.ts.map +1 -1
- package/dist/client/transcription/BuilderTranscriptionCta.js +2 -3
- package/dist/client/transcription/BuilderTranscriptionCta.js.map +1 -1
- package/dist/client/use-change-version.d.ts +46 -0
- package/dist/client/use-change-version.d.ts.map +1 -0
- package/dist/client/use-change-version.js +135 -0
- package/dist/client/use-change-version.js.map +1 -0
- package/dist/client/use-chat-threads.d.ts +16 -2
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +87 -12
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-chat-threads.spec.d.ts +2 -0
- package/dist/client/use-chat-threads.spec.d.ts.map +1 -0
- package/dist/client/use-chat-threads.spec.js +85 -0
- package/dist/client/use-chat-threads.spec.js.map +1 -0
- package/dist/client/use-db-sync.d.ts +5 -2
- package/dist/client/use-db-sync.d.ts.map +1 -1
- package/dist/client/use-db-sync.js +41 -16
- package/dist/client/use-db-sync.js.map +1 -1
- package/dist/client/use-pinch-zoom.d.ts +35 -0
- package/dist/client/use-pinch-zoom.d.ts.map +1 -0
- package/dist/client/use-pinch-zoom.js +105 -0
- package/dist/client/use-pinch-zoom.js.map +1 -0
- package/dist/deploy/workspace-deploy.d.ts.map +1 -1
- package/dist/deploy/workspace-deploy.js +99 -5
- package/dist/deploy/workspace-deploy.js.map +1 -1
- package/dist/extensions/actions.d.ts.map +1 -1
- package/dist/extensions/actions.js +3 -0
- package/dist/extensions/actions.js.map +1 -1
- package/dist/extensions/store.d.ts +5 -0
- package/dist/extensions/store.d.ts.map +1 -1
- package/dist/extensions/store.js +16 -1
- package/dist/extensions/store.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +3 -0
- package/dist/file-upload/actions/upload-image.d.ts.map +1 -0
- package/dist/file-upload/actions/upload-image.js +145 -0
- package/dist/file-upload/actions/upload-image.js.map +1 -0
- package/dist/file-upload/builder.d.ts.map +1 -1
- package/dist/file-upload/builder.js +31 -11
- package/dist/file-upload/builder.js.map +1 -1
- package/dist/file-upload/index.d.ts +1 -0
- package/dist/file-upload/index.d.ts.map +1 -1
- package/dist/file-upload/index.js +1 -0
- package/dist/file-upload/index.js.map +1 -1
- package/dist/file-upload/pre-upload-attachments.d.ts +39 -0
- package/dist/file-upload/pre-upload-attachments.d.ts.map +1 -0
- package/dist/file-upload/pre-upload-attachments.js +110 -0
- package/dist/file-upload/pre-upload-attachments.js.map +1 -0
- package/dist/file-upload/registry.d.ts.map +1 -1
- package/dist/file-upload/registry.js +8 -7
- package/dist/file-upload/registry.js.map +1 -1
- package/dist/onboarding/default-steps.js +1 -1
- package/dist/onboarding/default-steps.js.map +1 -1
- package/dist/org/context.d.ts +15 -1
- package/dist/org/context.d.ts.map +1 -1
- package/dist/org/context.js +25 -0
- package/dist/org/context.js.map +1 -1
- package/dist/org/handlers.d.ts +2 -2
- package/dist/org/handlers.d.ts.map +1 -1
- package/dist/org/handlers.js +3 -17
- package/dist/org/handlers.js.map +1 -1
- package/dist/org/index.d.ts +1 -1
- package/dist/org/index.d.ts.map +1 -1
- package/dist/org/index.js +1 -1
- package/dist/org/index.js.map +1 -1
- package/dist/resources/handlers.d.ts +6 -0
- package/dist/resources/handlers.d.ts.map +1 -1
- package/dist/resources/handlers.js +30 -6
- package/dist/resources/handlers.js.map +1 -1
- package/dist/resources/script-helpers.d.ts +11 -2
- package/dist/resources/script-helpers.d.ts.map +1 -1
- package/dist/resources/script-helpers.js +20 -3
- package/dist/resources/script-helpers.js.map +1 -1
- package/dist/resources/store.d.ts +28 -3
- package/dist/resources/store.d.ts.map +1 -1
- package/dist/resources/store.js +170 -20
- package/dist/resources/store.js.map +1 -1
- package/dist/scripts/resources/list.d.ts +1 -1
- package/dist/scripts/resources/list.d.ts.map +1 -1
- package/dist/scripts/resources/list.js +16 -4
- package/dist/scripts/resources/list.js.map +1 -1
- package/dist/scripts/resources/write.d.ts +1 -1
- package/dist/scripts/resources/write.d.ts.map +1 -1
- package/dist/scripts/resources/write.js +47 -3
- package/dist/scripts/resources/write.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +8 -3
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +214 -25
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-discovery.d.ts +35 -0
- package/dist/server/agent-discovery.d.ts.map +1 -1
- package/dist/server/agent-discovery.js +139 -8
- package/dist/server/agent-discovery.js.map +1 -1
- package/dist/server/app-url.d.ts +12 -6
- package/dist/server/app-url.d.ts.map +1 -1
- package/dist/server/app-url.js +58 -11
- package/dist/server/app-url.js.map +1 -1
- package/dist/server/auth.d.ts +22 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +272 -59
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts +0 -4
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +0 -3
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +23 -0
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +29 -14
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/credential-provider.d.ts +14 -0
- package/dist/server/credential-provider.d.ts.map +1 -1
- package/dist/server/credential-provider.js +88 -11
- package/dist/server/credential-provider.js.map +1 -1
- package/dist/server/google-auth-plugin.d.ts.map +1 -1
- package/dist/server/google-auth-plugin.js +53 -13
- package/dist/server/google-auth-plugin.js.map +1 -1
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +47 -17
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/oauth-public-origin.d.ts.map +1 -1
- package/dist/server/oauth-public-origin.js +19 -1
- package/dist/server/oauth-public-origin.js.map +1 -1
- package/dist/server/onboarding-html.d.ts.map +1 -1
- package/dist/server/onboarding-html.js +62 -15
- package/dist/server/onboarding-html.js.map +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +20 -5
- package/dist/server/poll.js.map +1 -1
- package/dist/server/request-context.d.ts +8 -0
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/request-context.js.map +1 -1
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.d.ts.map +1 -1
- package/dist/shared/index.js +2 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/llm-connection.d.ts +10 -0
- package/dist/shared/llm-connection.d.ts.map +1 -0
- package/dist/shared/llm-connection.js +29 -0
- package/dist/shared/llm-connection.js.map +1 -0
- package/dist/shared/workspace-app-audience.d.ts +25 -0
- package/dist/shared/workspace-app-audience.d.ts.map +1 -0
- package/dist/shared/workspace-app-audience.js +126 -0
- package/dist/shared/workspace-app-audience.js.map +1 -0
- package/dist/shared/workspace-app-id.d.ts +1 -1
- package/dist/shared/workspace-app-id.d.ts.map +1 -1
- package/dist/shared/workspace-app-id.js +1 -0
- package/dist/shared/workspace-app-id.js.map +1 -1
- package/dist/sharing/access.d.ts.map +1 -1
- package/dist/sharing/access.js +46 -5
- package/dist/sharing/access.js.map +1 -1
- package/dist/sharing/actions/list-resource-shares.d.ts.map +1 -1
- package/dist/sharing/actions/list-resource-shares.js +8 -1
- package/dist/sharing/actions/list-resource-shares.js.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.d.ts.map +1 -1
- package/dist/sharing/actions/set-resource-visibility.js +12 -3
- package/dist/sharing/actions/set-resource-visibility.js.map +1 -1
- package/dist/sharing/actions/share-resource.d.ts.map +1 -1
- package/dist/sharing/actions/share-resource.js +50 -1
- package/dist/sharing/actions/share-resource.js.map +1 -1
- package/dist/sharing/registry.d.ts +26 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js.map +1 -1
- package/dist/styles/agent-native.css +91 -0
- package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +72 -0
- package/dist/templates/default/.agents/skills/frontend-design/SKILL.md +60 -37
- package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +28 -17
- package/dist/templates/default/.agents/skills/shadcn-ui/SKILL.md +79 -0
- package/dist/templates/default/AGENTS.md +22 -19
- package/dist/templates/default/actions/navigate.ts +3 -0
- package/dist/templates/default/app/hooks/use-navigation-state.ts +29 -5
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +251 -0
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +264 -0
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +130 -0
- package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +112 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +88 -0
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +191 -0
- package/dist/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +74 -0
- package/dist/templates/workspace-core/.agents/skills/client-side-routing/SKILL.md +75 -0
- package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +190 -0
- package/dist/templates/workspace-core/.agents/skills/create-skill/SKILL.md +168 -0
- package/dist/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +163 -0
- package/dist/templates/workspace-core/.agents/skills/extension-points/SKILL.md +205 -0
- package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +720 -0
- package/dist/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +92 -0
- package/dist/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +285 -0
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +192 -0
- package/dist/templates/workspace-core/.agents/skills/onboarding/SKILL.md +43 -0
- package/dist/templates/workspace-core/.agents/skills/portability/SKILL.md +84 -0
- package/dist/templates/workspace-core/.agents/skills/qa/SKILL.md +313 -0
- package/dist/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +112 -0
- package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +165 -0
- package/dist/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +41 -0
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +239 -0
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +191 -0
- package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +79 -0
- package/dist/templates/workspace-core/.agents/skills/server-plugins/SKILL.md +73 -0
- package/dist/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +79 -0
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +217 -0
- package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +132 -0
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +150 -0
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +124 -0
- package/dist/templates/workspace-core/AGENTS.md +16 -1
- package/dist/templates/workspace-root/AGENTS.md +35 -0
- package/dist/templates/workspace-root/README.md +7 -0
- package/dist/vite/action-types-plugin.d.ts.map +1 -1
- package/dist/vite/action-types-plugin.js +4 -0
- package/dist/vite/action-types-plugin.js.map +1 -1
- package/docs/content/authentication.md +36 -0
- package/docs/content/creating-templates.md +15 -0
- package/docs/content/dispatch.md +3 -3
- package/docs/content/multi-app-workspace.md +5 -0
- package/docs/content/tracking.md +12 -0
- package/docs/content/workspace-management.md +39 -4
- package/package.json +15 -12
- package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +72 -0
- package/src/templates/default/.agents/skills/frontend-design/SKILL.md +60 -37
- package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +28 -17
- package/src/templates/default/.agents/skills/shadcn-ui/SKILL.md +79 -0
- package/src/templates/default/AGENTS.md +22 -19
- package/src/templates/default/actions/navigate.ts +3 -0
- package/src/templates/default/app/hooks/use-navigation-state.ts +29 -5
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +251 -0
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +264 -0
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +130 -0
- package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +112 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +88 -0
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +191 -0
- package/src/templates/workspace-core/.agents/skills/capture-learnings/SKILL.md +74 -0
- package/src/templates/workspace-core/.agents/skills/client-side-routing/SKILL.md +75 -0
- package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +190 -0
- package/src/templates/workspace-core/.agents/skills/create-skill/SKILL.md +168 -0
- package/src/templates/workspace-core/.agents/skills/delegate-to-agent/SKILL.md +163 -0
- package/src/templates/workspace-core/.agents/skills/extension-points/SKILL.md +205 -0
- package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +720 -0
- package/src/templates/workspace-core/.agents/skills/frontend-design/SKILL.md +92 -0
- package/src/templates/workspace-core/.agents/skills/integration-webhooks/SKILL.md +285 -0
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +192 -0
- package/src/templates/workspace-core/.agents/skills/onboarding/SKILL.md +43 -0
- package/src/templates/workspace-core/.agents/skills/portability/SKILL.md +84 -0
- package/src/templates/workspace-core/.agents/skills/qa/SKILL.md +313 -0
- package/src/templates/workspace-core/.agents/skills/real-time-collab/SKILL.md +112 -0
- package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +165 -0
- package/src/templates/workspace-core/.agents/skills/recurring-jobs/SKILL.md +41 -0
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +239 -0
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +191 -0
- package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +79 -0
- package/src/templates/workspace-core/.agents/skills/server-plugins/SKILL.md +73 -0
- package/src/templates/workspace-core/.agents/skills/shadcn-ui/SKILL.md +79 -0
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +217 -0
- package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +132 -0
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +150 -0
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +124 -0
- package/src/templates/workspace-core/AGENTS.md +16 -1
- package/src/templates/workspace-root/AGENTS.md +35 -0
- package/src/templates/workspace-root/README.md +7 -0
|
@@ -0,0 +1,720 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: extensions
|
|
3
|
+
description: >-
|
|
4
|
+
Creating, editing, and managing extensions — sandboxed Alpine.js mini-apps
|
|
5
|
+
that run inside iframes. Use when a user asks for a dashboard, widget,
|
|
6
|
+
calculator, or any interactive mini-app that calls external APIs. Distinct
|
|
7
|
+
from LLM "tools" (function calls) — see note below.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Extensions
|
|
11
|
+
|
|
12
|
+
> **Terminology note.** This skill is about **extensions** — the framework's
|
|
13
|
+
> user-authored mini-app primitive (sandboxed Alpine.js HTML rendered in an
|
|
14
|
+
> iframe). It is NOT the same thing as **LLM "tools"**, which are the
|
|
15
|
+
> function-calling primitives the AI agent uses (actions, MCP tools, etc.).
|
|
16
|
+
> Other skills still talk about "the agent calls actions as tools" — that's
|
|
17
|
+
> the LLM concept and stays as-is. When this doc says "tool" without
|
|
18
|
+
> qualification, it means LLM tool. When it says "extension", it means the
|
|
19
|
+
> sandboxed mini-app.
|
|
20
|
+
>
|
|
21
|
+
> Historical naming: extensions were previously called "tools". The physical
|
|
22
|
+
> SQL table names (`tools`, `tool_data`, `tool_shares`) and a few legacy
|
|
23
|
+
> in-iframe globals (`toolFetch`, `toolData`) are kept for back-compat — see
|
|
24
|
+
> the relevant sections below.
|
|
25
|
+
|
|
26
|
+
## CRITICAL: What Extensions Are (and Are Not)
|
|
27
|
+
|
|
28
|
+
An Extension is a **self-contained Alpine.js HTML snippet** stored in the
|
|
29
|
+
SQL `tools` table (table name kept for back-compat; the Drizzle export is
|
|
30
|
+
`extensions`). It runs inside a sandboxed iframe with its own Tailwind CSS
|
|
31
|
+
and Alpine.js runtime.
|
|
32
|
+
|
|
33
|
+
**Extensions are NOT:**
|
|
34
|
+
|
|
35
|
+
- React components
|
|
36
|
+
- New source code files
|
|
37
|
+
- Database schema changes
|
|
38
|
+
- Action files in `actions/`
|
|
39
|
+
- Routes
|
|
40
|
+
|
|
41
|
+
**When a user asks to "make an extension", "create an extension", or "build
|
|
42
|
+
a ... extension" (or the older phrasings "make a tool" / "create a tool"):**
|
|
43
|
+
|
|
44
|
+
1. Write the Alpine.js HTML
|
|
45
|
+
2. Call `create-extension` with the HTML as `content`
|
|
46
|
+
3. That's it — no files to create, no schema changes, no actions
|
|
47
|
+
|
|
48
|
+
Extensions have full access to app data via helpers injected into the iframe:
|
|
49
|
+
|
|
50
|
+
- `appAction(name, params)` — call any app action
|
|
51
|
+
- `appFetch(path, options)` — call allowed framework endpoints under
|
|
52
|
+
`/_agent-native/*`
|
|
53
|
+
- `dbQuery(sql, args)` — read from SQL
|
|
54
|
+
- `dbExec(sql, args)` — write to SQL
|
|
55
|
+
- `extensionFetch(url, options)` — call external APIs via proxy. Legacy
|
|
56
|
+
alias: `toolFetch` — kept for back-compat with extension bodies authored
|
|
57
|
+
before the rename; both names refer to the same helper.
|
|
58
|
+
- `extensionData.set/list/get/remove(collection, ...)` — persist custom data
|
|
59
|
+
per-extension (supports `{ scope: 'user' | 'org' | 'all' }` option). Legacy
|
|
60
|
+
alias: `toolData` — kept for back-compat; both names refer to the same
|
|
61
|
+
store.
|
|
62
|
+
|
|
63
|
+
## Data Persistence is Built In
|
|
64
|
+
|
|
65
|
+
**Every extension has `extensionData` — a per-extension key-value store. NO
|
|
66
|
+
source code changes, NO Builder, NO new tables needed.**
|
|
67
|
+
|
|
68
|
+
When a user asks to "add persistence", "save data", "remember state", or
|
|
69
|
+
"store settings" in an extension, use `extensionData`. It handles table
|
|
70
|
+
creation, scoping, and upserts automatically. Data is organized into
|
|
71
|
+
collections per-extension:
|
|
72
|
+
|
|
73
|
+
```javascript
|
|
74
|
+
// Save a private item (default — only the current user can see it)
|
|
75
|
+
await extensionData.set('notes', 'note-1', { title: 'My Note', body: 'Hello' });
|
|
76
|
+
|
|
77
|
+
// Save an org-shared item (visible to everyone in the org)
|
|
78
|
+
await extensionData.set('notes', 'note-1', { title: 'Team Note', body: 'Hello' }, { scope: 'org' });
|
|
79
|
+
|
|
80
|
+
// List items by scope
|
|
81
|
+
const myNotes = await extensionData.list('notes'); // user-scoped (default)
|
|
82
|
+
const orgNotes = await extensionData.list('notes', { scope: 'org' }); // org-scoped only
|
|
83
|
+
const allNotes = await extensionData.list('notes', { scope: 'all' }); // both user + org
|
|
84
|
+
|
|
85
|
+
// Get one item
|
|
86
|
+
const note = await extensionData.get('notes', 'note-1'); // user-scoped
|
|
87
|
+
const orgNote = await extensionData.get('notes', 'note-1', { scope: 'org' }); // org-scoped
|
|
88
|
+
|
|
89
|
+
// Delete an item
|
|
90
|
+
await extensionData.remove('notes', 'note-1'); // user-scoped
|
|
91
|
+
await extensionData.remove('notes', 'note-1', { scope: 'org' }); // org-scoped
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> The legacy global `toolData` is still injected and points at the same
|
|
95
|
+
> store — older extension bodies that reference `toolData.set(...)` continue
|
|
96
|
+
> to work without changes. Prefer `extensionData` in new code.
|
|
97
|
+
|
|
98
|
+
**Prefer `extensionData` over raw `dbExec` for extension-specific
|
|
99
|
+
persistence** — it handles everything automatically. Only use
|
|
100
|
+
`dbQuery`/`dbExec` when querying the app's existing tables.
|
|
101
|
+
|
|
102
|
+
## What extensions are
|
|
103
|
+
|
|
104
|
+
Extensions are mini Alpine.js apps that run inside sandboxed iframes. They
|
|
105
|
+
can call external APIs via `extensionFetch()`, which routes through a
|
|
106
|
+
server-side proxy that injects secret values. Extensions share the main
|
|
107
|
+
app's Tailwind v4 theme automatically.
|
|
108
|
+
|
|
109
|
+
## Creating an extension
|
|
110
|
+
|
|
111
|
+
Call the `create-extension` action:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pnpm action create-extension \
|
|
115
|
+
--name "GitHub PR Dashboard" \
|
|
116
|
+
--description "Shows open PRs for the repo" \
|
|
117
|
+
--content '<div x-data="{ prs: [], loading: true }" x-init="extensionFetch('"'"'https://api.github.com/repos/OWNER/REPO/pulls'"'"', { headers: { '"'"'Authorization'"'"': '"'"'Bearer ${keys.GITHUB_TOKEN}'"'"' }}).then(r => r.json()).then(d => { prs = d; loading = false })"><template x-if="loading"><p>Loading...</p></template><div class="space-y-2"><template x-for="pr in prs" :key="pr.id"><a :href="pr.html_url" target="_blank" class="block rounded-lg border p-3 hover:bg-accent"><p class="font-medium" x-text="pr.title"></p><p class="text-sm text-muted-foreground" x-text="'"'"'#'"'"' + pr.number + '"'"' by '"'"' + pr.user.login"></p></a></template></div></div>'
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Or via the HTTP API:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
POST /_agent-native/extensions
|
|
124
|
+
{ "name": "GitHub PR Dashboard", "description": "Shows open PRs", "content": "<div ...>...</div>" }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The action accepts:
|
|
128
|
+
|
|
129
|
+
| Field | Type | Required | Purpose |
|
|
130
|
+
| ------------- | -------- | -------- | ----------------------------- |
|
|
131
|
+
| `name` | `string` | yes | Display name of the extension |
|
|
132
|
+
| `description` | `string` | no | Short summary |
|
|
133
|
+
| `content` | `string` | yes | Alpine.js HTML body |
|
|
134
|
+
|
|
135
|
+
## Editing an extension
|
|
136
|
+
|
|
137
|
+
Use the `update-extension` action. Prefer `patches` for surgical edits
|
|
138
|
+
instead of regenerating the full HTML:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
PUT /_agent-native/extensions/:id
|
|
142
|
+
{
|
|
143
|
+
"patches": [
|
|
144
|
+
{ "find": "old HTML fragment", "replace": "new HTML fragment" }
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Each patch does a string find-and-replace on the current content. Use this
|
|
150
|
+
to change a single element, fix a URL, or update a class without rewriting
|
|
151
|
+
everything.
|
|
152
|
+
|
|
153
|
+
To replace the full content instead:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
PUT /_agent-native/extensions/:id
|
|
157
|
+
{ "content": "full new HTML" }
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Alpine.js patterns
|
|
161
|
+
|
|
162
|
+
Extension HTML uses Alpine.js directives for reactivity. No build step, no
|
|
163
|
+
imports.
|
|
164
|
+
|
|
165
|
+
| Directive | Purpose | Example |
|
|
166
|
+
| --------------- | ----------------------------- | ------------------------------------------ |
|
|
167
|
+
| `x-data` | Reactive state object | `x-data="{ count: 0, items: [] }"` |
|
|
168
|
+
| `x-init` | Run on mount (fetch data) | `x-init="fetchData()"` |
|
|
169
|
+
| `x-show` | Toggle visibility | `x-show="isOpen"` |
|
|
170
|
+
| `x-if` | Conditional render (template) | `<template x-if="loaded">...</template>` |
|
|
171
|
+
| `x-for` | Loop | `<template x-for="item in items">...</template>` |
|
|
172
|
+
| `x-text` | Set text content | `x-text="item.name"` |
|
|
173
|
+
| `x-html` | Set inner HTML | `x-html="item.richContent"` |
|
|
174
|
+
| `x-on:click` | Event handler | `x-on:click="count++"` |
|
|
175
|
+
| `x-model` | Two-way binding | `x-model="searchQuery"` |
|
|
176
|
+
| `x-bind:class` | Dynamic classes | `x-bind:class="{ 'font-bold': active }"` |
|
|
177
|
+
|
|
178
|
+
Always wrap `x-if` and `x-for` in a `<template>` tag.
|
|
179
|
+
|
|
180
|
+
## Component shape: inline `x-data` vs. `Alpine.data()`
|
|
181
|
+
|
|
182
|
+
For trivial components (a couple of state fields, no methods, no string
|
|
183
|
+
templating) inline `x-data="{ count: 0, items: [] }"` is fine. **For anything
|
|
184
|
+
beyond that — multiple methods, string formatting, classification logic,
|
|
185
|
+
async fetches with branching — put the component in a `<script>` block and
|
|
186
|
+
register it with `Alpine.data()`.** The inline form is a string inside an
|
|
187
|
+
HTML attribute; the longer it gets the more fragile it becomes (one stray
|
|
188
|
+
quote, one closing-tag-shaped substring, one template literal and the
|
|
189
|
+
attribute terminates early — Alpine then evaluates a half-parsed expression
|
|
190
|
+
and throws `ReferenceError: <var> is not defined`).
|
|
191
|
+
|
|
192
|
+
**Use this pattern for any non-trivial extension:**
|
|
193
|
+
|
|
194
|
+
```html
|
|
195
|
+
<div x-data="customerAnalyzer" class="p-4">
|
|
196
|
+
<button @click="analyze()" class="rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground cursor-pointer">
|
|
197
|
+
Analyze
|
|
198
|
+
</button>
|
|
199
|
+
<template x-if="error"><p class="text-red-500" x-text="error"></p></template>
|
|
200
|
+
<template x-if="results">
|
|
201
|
+
<div class="space-y-2">
|
|
202
|
+
<div class="rounded-lg border p-3">
|
|
203
|
+
<p class="font-medium">Action — Builder Side</p>
|
|
204
|
+
<p class="text-sm text-muted-foreground" x-text="results.builderActions.length + ' items'"></p>
|
|
205
|
+
</div>
|
|
206
|
+
<!-- ...other buckets... -->
|
|
207
|
+
</div>
|
|
208
|
+
</template>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<script>
|
|
212
|
+
document.addEventListener('alpine:init', () => {
|
|
213
|
+
Alpine.data('customerAnalyzer', () => ({
|
|
214
|
+
loading: false,
|
|
215
|
+
error: '',
|
|
216
|
+
results: null,
|
|
217
|
+
async analyze() {
|
|
218
|
+
this.loading = true;
|
|
219
|
+
this.error = '';
|
|
220
|
+
try {
|
|
221
|
+
const { emails } = await appAction('list-emails', { view: 'inbox', limit: 50 });
|
|
222
|
+
// ...categorize into 3 buckets...
|
|
223
|
+
this.results = {
|
|
224
|
+
builderActions: emails.filter((e) => /* ... */),
|
|
225
|
+
waitingOnCustomer: emails.filter((e) => /* ... */),
|
|
226
|
+
fyi: emails.filter((e) => /* ... */),
|
|
227
|
+
};
|
|
228
|
+
} catch (e) {
|
|
229
|
+
this.error = e?.message || 'Analysis failed';
|
|
230
|
+
} finally {
|
|
231
|
+
this.loading = false;
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
}));
|
|
235
|
+
});
|
|
236
|
+
</script>
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Hard rules for `x-data` / `x-*` attributes:**
|
|
240
|
+
|
|
241
|
+
- Never put template literals (backticks) inside an HTML attribute. Use
|
|
242
|
+
string concatenation or pre-format in the script block. Backticks can
|
|
243
|
+
trip the HTML parser and the resulting string isn't a JS template literal
|
|
244
|
+
anyway — the attribute is read as plain text.
|
|
245
|
+
- Never put a multi-method object literal inline. Move methods into
|
|
246
|
+
`Alpine.data()`.
|
|
247
|
+
- In the `<script>` block, write normal JS — template literals, async/await,
|
|
248
|
+
optional chaining all work.
|
|
249
|
+
- One source of truth for state: define every variable referenced from any
|
|
250
|
+
`x-text`, `x-show`, `x-if`, `x-for`, `:class`, etc. on the `Alpine.data()`
|
|
251
|
+
object's initial state. If `x-text="results.foo"` references `results`,
|
|
252
|
+
`results` must be a property of the data object — null is a fine initial
|
|
253
|
+
value as long as you guard with `<template x-if="results">`.
|
|
254
|
+
- When showing an error, render `error.message`-style text, never a raw
|
|
255
|
+
boolean. `x-text="error"` is correct only when `error` is a string;
|
|
256
|
+
if it's `true` the user sees the literal word "true".
|
|
257
|
+
|
|
258
|
+
## AI / LLM features in extensions
|
|
259
|
+
|
|
260
|
+
Extensions can do AI work two ways. Pick deliberately — silent fallbacks
|
|
261
|
+
end up rendering nonsense like the literal text `true`.
|
|
262
|
+
|
|
263
|
+
1. **Delegate to the agent chat.** If the user says "analyze my emails",
|
|
264
|
+
"summarize this", "categorize these tickets" and there is no API key
|
|
265
|
+
already configured for the relevant provider, prefer doing the work in
|
|
266
|
+
the agent chat instead of inside the extension. The extension can have
|
|
267
|
+
a button that calls `parent.postMessage({ type: 'agent-native-send-to-chat', message: '...' })`,
|
|
268
|
+
or you can just answer in chat and skip the extension. Don't ship an
|
|
269
|
+
extension with a stubbed AI step that returns a placeholder — that's
|
|
270
|
+
how you end up rendering `true` in red.
|
|
271
|
+
2. **Call an LLM directly via `extensionFetch`.** Requires a real key the
|
|
272
|
+
user has set up. Reference it via `${keys.OPENAI_API_KEY}` /
|
|
273
|
+
`${keys.ANTHROPIC_API_KEY}` and surface a clear error if the proxy
|
|
274
|
+
reports the key isn't configured. Tell the user where to add the key
|
|
275
|
+
(Settings → Secrets) before the extension can work.
|
|
276
|
+
|
|
277
|
+
If you're not sure a key is configured, ask the user before generating an
|
|
278
|
+
extension whose primary value is the AI step.
|
|
279
|
+
|
|
280
|
+
## Accessing app data
|
|
281
|
+
|
|
282
|
+
Extensions can call the host app's actions and API endpoints directly. The
|
|
283
|
+
iframe shares the session cookie, so authentication is automatic.
|
|
284
|
+
|
|
285
|
+
### `appAction(name, params)` — Call app actions
|
|
286
|
+
|
|
287
|
+
Call any action defined in the app's `actions/` directory. Actions are
|
|
288
|
+
auto-mounted at `/_agent-native/actions/:name`.
|
|
289
|
+
|
|
290
|
+
```html
|
|
291
|
+
<div x-data="{ emails: [], loading: true }" x-init="
|
|
292
|
+
appAction('list-emails', { view: 'inbox', limit: 10 })
|
|
293
|
+
.then(d => { emails = d.emails || d; loading = false })
|
|
294
|
+
.catch(e => { console.error(e); loading = false })
|
|
295
|
+
">
|
|
296
|
+
<h2 class='text-lg font-semibold mb-4'>My Inbox</h2>
|
|
297
|
+
<template x-for='email in emails' :key='email.id'>
|
|
298
|
+
<div class='rounded-lg border p-3 mb-2'>
|
|
299
|
+
<p class='font-medium text-sm' x-text='email.subject'></p>
|
|
300
|
+
<p class='text-xs text-muted-foreground' x-text='email.from?.name || email.from?.email'></p>
|
|
301
|
+
</div>
|
|
302
|
+
</template>
|
|
303
|
+
</div>
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### `appFetch(path, options)` — Call allowed framework endpoints
|
|
307
|
+
|
|
308
|
+
General-purpose fetch to allowed framework endpoints (for example,
|
|
309
|
+
`/_agent-native/application-state/navigation`). Automatically adds credentials
|
|
310
|
+
and JSON content type. Template `/api/*` routes are intentionally blocked by
|
|
311
|
+
the extension bridge; use `appAction(name, params)` for app data instead.
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
// Read application state
|
|
315
|
+
const nav = await appFetch('/_agent-native/application-state/navigation');
|
|
316
|
+
|
|
317
|
+
// Call a framework route
|
|
318
|
+
const nav = await appFetch('/_agent-native/application-state/navigation');
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### `dbQuery(sql)` — Read from the app's database
|
|
322
|
+
|
|
323
|
+
Run a read-only SELECT query against the app's SQL database. Results are
|
|
324
|
+
auto-scoped to the current user/org.
|
|
325
|
+
|
|
326
|
+
```html
|
|
327
|
+
<div x-data="{ rows: [] }" x-init="
|
|
328
|
+
dbQuery('SELECT id, name FROM tools ORDER BY created_at DESC LIMIT 10')
|
|
329
|
+
.then(d => rows = d.rows || d)
|
|
330
|
+
">
|
|
331
|
+
<template x-for="row in rows" :key="row.id">
|
|
332
|
+
<div class="border-b p-2 text-sm" x-text="row.name"></div>
|
|
333
|
+
</template>
|
|
334
|
+
</div>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
> The physical SQL table is still named `tools` (and `tool_data`,
|
|
338
|
+
> `tool_shares`) for back-compat. The Drizzle exports are `extensions`,
|
|
339
|
+
> `extensionData`, and `extensionShares` — use those when you query via the
|
|
340
|
+
> ORM. When writing raw SQL inside an extension (as above), use the
|
|
341
|
+
> physical names.
|
|
342
|
+
|
|
343
|
+
### `dbExec(sql)` — Write to the app's database
|
|
344
|
+
|
|
345
|
+
Run an INSERT, UPDATE, or DELETE statement. Writes are auto-scoped to the
|
|
346
|
+
current user/org, and `owner_email` / `org_id` are auto-injected on INSERT.
|
|
347
|
+
|
|
348
|
+
```javascript
|
|
349
|
+
// Insert a new record
|
|
350
|
+
await dbExec("INSERT INTO notes (id, title, body) VALUES ('abc', 'My Note', 'Hello world')");
|
|
351
|
+
|
|
352
|
+
// Update an existing record
|
|
353
|
+
await dbExec("UPDATE notes SET title = 'Updated Title' WHERE id = 'abc'");
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### All helpers summary
|
|
357
|
+
|
|
358
|
+
| Helper | Use for | Example |
|
|
359
|
+
|--------|---------|---------|
|
|
360
|
+
| `appAction(name, params)` | Call app actions (CRUD, queries) | `appAction('list-emails', { view: 'inbox' })` |
|
|
361
|
+
| `appFetch(path, options)` | Call allowed framework endpoints | `appFetch('/_agent-native/application-state/navigation')` |
|
|
362
|
+
| `dbQuery(sql)` | Read from the app's SQL database | `dbQuery('SELECT * FROM notes LIMIT 10')` |
|
|
363
|
+
| `dbExec(sql)` | Write to the app's SQL database | `dbExec("INSERT INTO notes ...")` |
|
|
364
|
+
| `extensionFetch(url, options)` | Call external APIs via proxy (alias `toolFetch`) | `extensionFetch('https://api.github.com/user', { headers: { 'Authorization': 'Bearer ${keys.GITHUB_TOKEN}' } })` |
|
|
365
|
+
| `extensionData.set(collection, id, data, opts?)` | Save an item to extension storage (alias `toolData.set`) | `extensionData.set('todos', 'todo-1', { title: 'Buy milk' })` |
|
|
366
|
+
| `extensionData.list(collection, opts?)` | List items in a collection | `extensionData.list('todos', { scope: 'all' })` |
|
|
367
|
+
| `extensionData.get(collection, id, opts?)` | Get a single item by id | `extensionData.get('todos', 'todo-1')` |
|
|
368
|
+
| `extensionData.remove(collection, id, opts?)` | Delete an item | `extensionData.remove('todos', 'todo-1')` |
|
|
369
|
+
|
|
370
|
+
## Persisting Custom Data
|
|
371
|
+
|
|
372
|
+
Extensions have a built-in key-value store via `extensionData` (legacy alias:
|
|
373
|
+
`toolData`). Each extension gets its own isolated storage, organized into
|
|
374
|
+
collections. Every method accepts an optional `{ scope }` option:
|
|
375
|
+
|
|
376
|
+
- `'user'` (default) — private to the current user
|
|
377
|
+
- `'org'` — visible to everyone in the user's org
|
|
378
|
+
- `'all'` (list/get only) — returns both user and org items
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
// Save a private item (default scope: 'user')
|
|
382
|
+
await extensionData.set('todos', 'todo-1', { title: 'Buy milk', done: false });
|
|
383
|
+
|
|
384
|
+
// Save an org-shared item
|
|
385
|
+
await extensionData.set('todos', 'team-todo-1', { title: 'Ship v2', done: false }, { scope: 'org' });
|
|
386
|
+
|
|
387
|
+
// List user items (default)
|
|
388
|
+
const myTodos = await extensionData.list('todos');
|
|
389
|
+
|
|
390
|
+
// List org items
|
|
391
|
+
const orgTodos = await extensionData.list('todos', { scope: 'org' });
|
|
392
|
+
|
|
393
|
+
// List both user + org items
|
|
394
|
+
const allTodos = await extensionData.list('todos', { scope: 'all' });
|
|
395
|
+
// Returns: [{ id, toolId, collection, data (JSON string), ownerEmail, scope, orgId, createdAt, updatedAt }]
|
|
396
|
+
// (the row column is still named `toolId` for back-compat — it's the extension id)
|
|
397
|
+
|
|
398
|
+
// Parse the JSON data
|
|
399
|
+
const parsed = allTodos.map(t => ({ ...JSON.parse(t.data), id: t.id, scope: t.scope }));
|
|
400
|
+
|
|
401
|
+
// Get/delete with scope
|
|
402
|
+
const item = await extensionData.get('todos', 'team-todo-1', { scope: 'org' });
|
|
403
|
+
await extensionData.remove('todos', 'team-todo-1', { scope: 'org' });
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Data is scoped per-extension. User-scoped items are private per-user;
|
|
407
|
+
org-scoped items are shared across the org. Any org member can read,
|
|
408
|
+
update, or delete org-scoped items. **Prefer `extensionData` over raw
|
|
409
|
+
`dbExec` for extension-specific persistence** — it handles table creation,
|
|
410
|
+
scoping, and upserts automatically.
|
|
411
|
+
|
|
412
|
+
## Using `extensionFetch()` for API calls
|
|
413
|
+
|
|
414
|
+
`extensionFetch()` (legacy alias `toolFetch()`) is a drop-in replacement for
|
|
415
|
+
`fetch()` that proxies requests through the server. The server injects
|
|
416
|
+
secret values before the request leaves.
|
|
417
|
+
|
|
418
|
+
```javascript
|
|
419
|
+
// Basic GET
|
|
420
|
+
const res = await extensionFetch('https://api.example.com/data');
|
|
421
|
+
const data = await res.json();
|
|
422
|
+
|
|
423
|
+
// With secret injection
|
|
424
|
+
const res = await extensionFetch('https://api.openai.com/v1/models', {
|
|
425
|
+
headers: {
|
|
426
|
+
'Authorization': 'Bearer ${keys.OPENAI_API_KEY}'
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// POST with body
|
|
431
|
+
const res = await extensionFetch('https://api.example.com/items', {
|
|
432
|
+
method: 'POST',
|
|
433
|
+
headers: { 'Content-Type': 'application/json' },
|
|
434
|
+
body: JSON.stringify({ name: 'New Item' })
|
|
435
|
+
});
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**Important:** Use single quotes around strings containing `${keys.NAME}`
|
|
439
|
+
to prevent JavaScript template literal evaluation. The substitution
|
|
440
|
+
happens server-side, not in the browser.
|
|
441
|
+
|
|
442
|
+
## Tailwind classes
|
|
443
|
+
|
|
444
|
+
Extensions inherit the main app's Tailwind v4 theme. Use the same utility
|
|
445
|
+
classes:
|
|
446
|
+
|
|
447
|
+
- **Colors:** `bg-background`, `text-foreground`, `bg-primary`, `text-primary-foreground`, `text-muted-foreground`, `border-border`, `bg-accent`, `bg-destructive`
|
|
448
|
+
- **Layout:** `flex`, `grid`, `space-y-2`, `gap-4`, `p-4`, `m-2`
|
|
449
|
+
- **Typography:** `text-sm`, `text-lg`, `font-medium`, `font-bold`
|
|
450
|
+
- **Borders:** `border`, `rounded-lg`, `rounded-md`, `rounded-sm`
|
|
451
|
+
- **Dark mode:** automatic via `.dark` class on the html element
|
|
452
|
+
|
|
453
|
+
## Managing secrets
|
|
454
|
+
|
|
455
|
+
Extensions reference secrets via `${keys.NAME}` inside `extensionFetch()`
|
|
456
|
+
calls. Create secrets via:
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
POST /_agent-native/secrets/adhoc
|
|
460
|
+
{ "name": "GITHUB_TOKEN", "value": "ghp_xxxx", "description": "GitHub PAT", "urlAllowlist": ["https://api.github.com"] }
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
Or the user can add them in the settings UI. If an extension needs an API
|
|
464
|
+
key that isn't configured yet, tell the user what key is needed and where
|
|
465
|
+
to get it.
|
|
466
|
+
|
|
467
|
+
See the `secrets` skill for the full secrets API.
|
|
468
|
+
|
|
469
|
+
## Sharing
|
|
470
|
+
|
|
471
|
+
Use the framework sharing actions:
|
|
472
|
+
|
|
473
|
+
```bash
|
|
474
|
+
# Make an extension visible to the org
|
|
475
|
+
pnpm action set-resource-visibility --resourceType=tool --resourceId=EXTENSION_ID --visibility=org
|
|
476
|
+
|
|
477
|
+
# Share with a specific user
|
|
478
|
+
pnpm action share-resource --resourceType=tool --resourceId=EXTENSION_ID --principalType=user --principalId=user@example.com --role=editor
|
|
479
|
+
|
|
480
|
+
# List current shares
|
|
481
|
+
pnpm action list-resource-shares --resourceType=tool --resourceId=EXTENSION_ID
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
> The `resourceType` value is still `tool` for back-compat with the
|
|
485
|
+
> `tool_shares` table. The variable name `EXTENSION_ID` is the canonical
|
|
486
|
+
> name for the value going into the call.
|
|
487
|
+
|
|
488
|
+
See the `sharing` skill for visibility levels and roles.
|
|
489
|
+
|
|
490
|
+
## Navigation
|
|
491
|
+
|
|
492
|
+
```bash
|
|
493
|
+
# Navigate to the extensions list
|
|
494
|
+
pnpm action navigate --view=extensions
|
|
495
|
+
|
|
496
|
+
# Navigate to a specific extension
|
|
497
|
+
pnpm action navigate --view=extensions --extensionId=EXTENSION_ID
|
|
498
|
+
|
|
499
|
+
# Or directly:
|
|
500
|
+
set-url-path({ "pathname": "/extensions/EXTENSION_ID" })
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## Example extensions
|
|
504
|
+
|
|
505
|
+
### API Status Dashboard
|
|
506
|
+
|
|
507
|
+
Checks the health of multiple endpoints and shows green/red status:
|
|
508
|
+
|
|
509
|
+
```html
|
|
510
|
+
<div class="p-6" x-data="{
|
|
511
|
+
endpoints: [
|
|
512
|
+
{ name: 'API', url: 'https://api.example.com/health' },
|
|
513
|
+
{ name: 'Auth', url: 'https://auth.example.com/health' },
|
|
514
|
+
{ name: 'CDN', url: 'https://cdn.example.com/health' }
|
|
515
|
+
],
|
|
516
|
+
results: [],
|
|
517
|
+
loading: true
|
|
518
|
+
}" x-init="
|
|
519
|
+
Promise.all(endpoints.map(ep =>
|
|
520
|
+
extensionFetch(ep.url).then(r => ({ ...ep, ok: r.ok })).catch(() => ({ ...ep, ok: false }))
|
|
521
|
+
)).then(r => { results = r; loading = false })
|
|
522
|
+
">
|
|
523
|
+
<h2 class="text-lg font-bold mb-4">Service Status</h2>
|
|
524
|
+
<template x-if="loading"><p class="text-muted-foreground">Checking...</p></template>
|
|
525
|
+
<div class="space-y-2">
|
|
526
|
+
<template x-for="r in results" :key="r.name">
|
|
527
|
+
<div class="flex items-center justify-between rounded-lg border p-3">
|
|
528
|
+
<span class="font-medium" x-text="r.name"></span>
|
|
529
|
+
<span x-bind:class="r.ok ? 'text-green-600' : 'text-red-600'" x-text="r.ok ? 'Healthy' : 'Down'"></span>
|
|
530
|
+
</div>
|
|
531
|
+
</template>
|
|
532
|
+
</div>
|
|
533
|
+
</div>
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Weather Widget
|
|
537
|
+
|
|
538
|
+
Fetches current weather for a city:
|
|
539
|
+
|
|
540
|
+
```html
|
|
541
|
+
<div class="p-6" x-data="{ city: 'San Francisco', weather: null, loading: false }" x-init="
|
|
542
|
+
loading = true;
|
|
543
|
+
extensionFetch('https://api.weatherapi.com/v1/current.json?q=' + encodeURIComponent(city) + '&key=${keys.WEATHER_API_KEY}')
|
|
544
|
+
.then(r => r.json()).then(d => { weather = d; loading = false })
|
|
545
|
+
">
|
|
546
|
+
<div class="space-y-4">
|
|
547
|
+
<div class="flex gap-2">
|
|
548
|
+
<input type="text" x-model="city" class="flex-1 rounded-md border bg-background px-3 py-2 text-sm" placeholder="City name" />
|
|
549
|
+
<button x-on:click="loading = true; extensionFetch('https://api.weatherapi.com/v1/current.json?q=' + encodeURIComponent(city) + '&key=${keys.WEATHER_API_KEY}').then(r => r.json()).then(d => { weather = d; loading = false })" class="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground cursor-pointer">Search</button>
|
|
550
|
+
</div>
|
|
551
|
+
<template x-if="loading"><p class="text-muted-foreground">Loading...</p></template>
|
|
552
|
+
<template x-if="weather && !loading">
|
|
553
|
+
<div class="rounded-lg border p-4">
|
|
554
|
+
<p class="text-2xl font-bold" x-text="weather.current.temp_f + '°F'"></p>
|
|
555
|
+
<p class="text-muted-foreground" x-text="weather.current.condition.text"></p>
|
|
556
|
+
<p class="text-sm text-muted-foreground" x-text="weather.location.name + ', ' + weather.location.region"></p>
|
|
557
|
+
</div>
|
|
558
|
+
</template>
|
|
559
|
+
</div>
|
|
560
|
+
</div>
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Todo List (using extensionData)
|
|
564
|
+
|
|
565
|
+
Full CRUD app using the built-in `extensionData` store — no SQL, no schema
|
|
566
|
+
files, no actions. Data is automatically scoped per-extension and per-user:
|
|
567
|
+
|
|
568
|
+
```html
|
|
569
|
+
<div class="p-6" x-data="{
|
|
570
|
+
todos: [],
|
|
571
|
+
newTodo: '',
|
|
572
|
+
loading: true,
|
|
573
|
+
async init() {
|
|
574
|
+
const items = await extensionData.list('todos');
|
|
575
|
+
this.todos = items.map(i => ({ id: i.id, ...JSON.parse(i.data) }));
|
|
576
|
+
this.loading = false;
|
|
577
|
+
},
|
|
578
|
+
async addTodo() {
|
|
579
|
+
if (!this.newTodo.trim()) return;
|
|
580
|
+
const id = crypto.randomUUID();
|
|
581
|
+
const data = { title: this.newTodo.trim(), completed: false };
|
|
582
|
+
await extensionData.set('todos', id, data);
|
|
583
|
+
this.todos.unshift({ id, ...data });
|
|
584
|
+
this.newTodo = '';
|
|
585
|
+
},
|
|
586
|
+
async toggle(todo) {
|
|
587
|
+
todo.completed = !todo.completed;
|
|
588
|
+
await extensionData.set('todos', todo.id, { title: todo.title, completed: todo.completed });
|
|
589
|
+
},
|
|
590
|
+
async remove(id) {
|
|
591
|
+
await extensionData.remove('todos', id);
|
|
592
|
+
this.todos = this.todos.filter(t => t.id !== id);
|
|
593
|
+
}
|
|
594
|
+
}">
|
|
595
|
+
<h2 class="text-lg font-semibold mb-4">Todo List</h2>
|
|
596
|
+
<div class="flex gap-2 mb-4">
|
|
597
|
+
<input x-model="newTodo" type="text" placeholder="What needs to be done?"
|
|
598
|
+
class="flex-1 rounded-md border border-input bg-background px-3 py-2 text-sm"
|
|
599
|
+
@keydown.enter="addTodo()">
|
|
600
|
+
<button @click="addTodo()"
|
|
601
|
+
class="rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground cursor-pointer hover:bg-primary/90">
|
|
602
|
+
Add
|
|
603
|
+
</button>
|
|
604
|
+
</div>
|
|
605
|
+
<div x-show="loading" class="text-sm text-muted-foreground">Loading...</div>
|
|
606
|
+
<div class="space-y-2">
|
|
607
|
+
<template x-for="todo in todos" :key="todo.id">
|
|
608
|
+
<div class="flex items-center gap-3 rounded-md border p-3">
|
|
609
|
+
<button @click="toggle(todo)" class="cursor-pointer"
|
|
610
|
+
:class="todo.completed ? 'text-green-500' : 'text-muted-foreground'">
|
|
611
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
612
|
+
<template x-if="todo.completed"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14M22 4 12 14.01l-3-3"/></template>
|
|
613
|
+
<template x-if="!todo.completed"><circle cx="12" cy="12" r="10"/></template>
|
|
614
|
+
</svg>
|
|
615
|
+
</button>
|
|
616
|
+
<span class="flex-1 text-sm" :class="todo.completed && 'line-through text-muted-foreground'" x-text="todo.title"></span>
|
|
617
|
+
<button @click="remove(todo.id)" class="text-muted-foreground hover:text-destructive cursor-pointer text-xs">Remove</button>
|
|
618
|
+
</div>
|
|
619
|
+
</template>
|
|
620
|
+
</div>
|
|
621
|
+
<p x-show="!loading && todos.length === 0" class="text-sm text-muted-foreground text-center py-8">No todos yet. Add one above!</p>
|
|
622
|
+
</div>
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### Quick Notes
|
|
626
|
+
|
|
627
|
+
Persistent notes using localStorage -- no API key needed:
|
|
628
|
+
|
|
629
|
+
```html
|
|
630
|
+
<div class="p-6" x-data="{
|
|
631
|
+
notes: JSON.parse(localStorage.getItem('quick-notes') || '[]'),
|
|
632
|
+
draft: '',
|
|
633
|
+
save() {
|
|
634
|
+
if (!this.draft.trim()) return;
|
|
635
|
+
this.notes.unshift({ id: Date.now(), text: this.draft, date: new Date().toLocaleDateString() });
|
|
636
|
+
this.draft = '';
|
|
637
|
+
localStorage.setItem('quick-notes', JSON.stringify(this.notes));
|
|
638
|
+
},
|
|
639
|
+
remove(id) {
|
|
640
|
+
this.notes = this.notes.filter(n => n.id !== id);
|
|
641
|
+
localStorage.setItem('quick-notes', JSON.stringify(this.notes));
|
|
642
|
+
}
|
|
643
|
+
}">
|
|
644
|
+
<div class="space-y-4">
|
|
645
|
+
<div class="flex gap-2">
|
|
646
|
+
<input type="text" x-model="draft" x-on:keydown.enter="save()" class="flex-1 rounded-md border bg-background px-3 py-2 text-sm" placeholder="Add a note..." />
|
|
647
|
+
<button x-on:click="save()" class="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground cursor-pointer">Add</button>
|
|
648
|
+
</div>
|
|
649
|
+
<div class="space-y-2">
|
|
650
|
+
<template x-for="note in notes" :key="note.id">
|
|
651
|
+
<div class="flex items-start justify-between rounded-lg border p-3">
|
|
652
|
+
<div>
|
|
653
|
+
<p class="text-sm" x-text="note.text"></p>
|
|
654
|
+
<p class="text-xs text-muted-foreground" x-text="note.date"></p>
|
|
655
|
+
</div>
|
|
656
|
+
<button x-on:click="remove(note.id)" class="text-muted-foreground hover:text-destructive text-sm cursor-pointer">Remove</button>
|
|
657
|
+
</div>
|
|
658
|
+
</template>
|
|
659
|
+
<template x-if="notes.length === 0">
|
|
660
|
+
<p class="text-sm text-muted-foreground">No notes yet.</p>
|
|
661
|
+
</template>
|
|
662
|
+
</div>
|
|
663
|
+
</div>
|
|
664
|
+
</div>
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
## Guidelines
|
|
668
|
+
|
|
669
|
+
- **Rely on the default canvas padding.** The iframe shell adds modest body padding so simple extensions do not hug the edge. Do not add outer `p-4` / `p-6` unless the design needs extra breathing room. For full-bleed extensions such as maps, canvases, or custom editors, put `data-tool-layout="full-bleed"` or `data-tool-padding="none"` on the outermost element. (The `data-tool-*` attribute names are kept for back-compat with the iframe runtime.)
|
|
670
|
+
- **Use semantic Tailwind colors for native theming.** Always use `bg-background`, `text-foreground`, `bg-primary`, `text-primary-foreground`, `border-border`, `bg-muted`, `text-muted-foreground`, etc. The extension inherits the parent app's exact theme variables, so it will look fully native in both light and dark modes.
|
|
671
|
+
- **Keep extensions focused.** One extension, one job. A "GitHub PR Dashboard" should show PRs, not also manage issues.
|
|
672
|
+
- **Handle loading and error states.** Always show a loading indicator during fetch and handle failures gracefully.
|
|
673
|
+
- **All functions referenced in Alpine expressions must be defined in `x-data`.** If you use `@click="add()"`, there must be an `add()` method in the component's `x-data` object. Undefined references cause runtime errors.
|
|
674
|
+
- **For non-trivial components, use a `<script>` + `Alpine.data('name', () => ({...}))` block and reference it with `x-data="name"`.** Inline `x-data="{ ...big object... }"` is brittle: stuffing many methods, branching logic, or any backtick template literal into an HTML attribute leads to half-parsed expressions and `ReferenceError` failures. See the "Component shape" section above.
|
|
675
|
+
- **Don't ship a stubbed AI step.** If the extension's value is "AI analysis" and no LLM key is configured, either route the work to the agent chat or tell the user which key to add — never render a placeholder/boolean as the result.
|
|
676
|
+
- **Use the right fetch helper.** `appAction()` for app actions and app data, `appFetch()` for allowed framework `/_agent-native/*` endpoints, and `extensionFetch()` for external APIs. Never call template `/api/*` routes from an extension and never use raw `fetch()` -- secrets won't be injected and CORS will block external APIs.
|
|
677
|
+
- **Single quotes around `${keys.*}`** to prevent browser-side template literal evaluation.
|
|
678
|
+
- **Prefer patches over full rewrites** when editing existing extensions. Smaller diffs are less error-prone.
|
|
679
|
+
|
|
680
|
+
## Routes
|
|
681
|
+
|
|
682
|
+
| Method | Path | Purpose |
|
|
683
|
+
| ------ | -------------------------------------- | --------------------------------------------- |
|
|
684
|
+
| GET | `/_agent-native/extensions` | List extensions (filtered by ownership/share) |
|
|
685
|
+
| POST | `/_agent-native/extensions` | Create an extension |
|
|
686
|
+
| GET | `/_agent-native/extensions/:id` | Get an extension |
|
|
687
|
+
| PUT | `/_agent-native/extensions/:id` | Update (supports `patches` for diffing) |
|
|
688
|
+
| DELETE | `/_agent-native/extensions/:id` | Delete an extension |
|
|
689
|
+
| GET | `/_agent-native/extensions/:id/render` | Render HTML for iframe |
|
|
690
|
+
| POST | `/_agent-native/extensions/proxy` | Authenticated proxy with secret injection |
|
|
691
|
+
|
|
692
|
+
## Database & API names — back-compat reference
|
|
693
|
+
|
|
694
|
+
The rename from "tools" to "extensions" is mostly user-facing. Several
|
|
695
|
+
under-the-hood names are kept to avoid breaking existing data and code:
|
|
696
|
+
|
|
697
|
+
| Surface | Stays as | Rationale |
|
|
698
|
+
| ------------------------------------ | --------------------- | ------------------------------------------------------ |
|
|
699
|
+
| SQL table for extensions | `tools` | Renaming a table = drop+create; data must not move |
|
|
700
|
+
| SQL table for per-ext data | `tool_data` | Same |
|
|
701
|
+
| SQL table for ext shares | `tool_shares` | Same |
|
|
702
|
+
| Drizzle schema export | `extensions` | Code-side rename — no data migration needed |
|
|
703
|
+
| Drizzle schema export | `extensionData` | Same |
|
|
704
|
+
| Drizzle schema export | `extensionShares` | Same |
|
|
705
|
+
| Iframe global (legacy alias) | `toolFetch` | Kept so older extension bodies keep working |
|
|
706
|
+
| Iframe global (legacy alias) | `toolData` | Same |
|
|
707
|
+
| Iframe global (canonical) | `extensionFetch` | Use this in new extensions |
|
|
708
|
+
| Iframe global (canonical) | `extensionData` | Same |
|
|
709
|
+
| `data-tool-layout` HTML attribute | unchanged | Runtime contract; not worth churning |
|
|
710
|
+
| `resourceType` for sharing | `tool` | Matches `tool_shares` table |
|
|
711
|
+
| Slot-system table | `tool_slots` | Drizzle export is `extensionSlots` (see `extension-points`) |
|
|
712
|
+
| Slot-installs table | `tool_slot_installs` | Drizzle export is `extensionSlotInstalls` |
|
|
713
|
+
|
|
714
|
+
## Related skills
|
|
715
|
+
|
|
716
|
+
- `extension-points` -- how an extension renders as a widget inside another app via named UI slots.
|
|
717
|
+
- `secrets` -- creating and managing API keys for `${keys.NAME}` substitution.
|
|
718
|
+
- `sharing` -- visibility and access control for extensions.
|
|
719
|
+
- `actions` -- the `create-extension` and `update-extension` actions that back extension CRUD.
|
|
720
|
+
- `frontend-design` -- design guidance when styling extension HTML.
|