@agent-native/core 0.127.3 → 0.128.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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +80 -0
- package/corpus/core/README.md +5 -26
- package/corpus/core/docs/content/actions.mdx +47 -4
- package/corpus/core/docs/content/native-chat-ui.mdx +7 -0
- package/corpus/core/docs/content/organizations-teams-permissions.mdx +109 -3
- package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +15 -0
- package/corpus/core/docs/content/toolkit-org-team.mdx +72 -12
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/artifact-response.ts +36 -29
- package/corpus/core/src/action.ts +83 -2
- package/corpus/core/src/chat-threads/store.ts +48 -43
- package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +46 -3
- package/corpus/core/src/client/AgentPanel.tsx +26 -6
- package/corpus/core/src/client/AssistantChat.tsx +63 -7
- package/corpus/core/src/client/DefaultSpinner.tsx +29 -0
- package/corpus/core/src/client/org/TeamPage.tsx +161 -6
- package/corpus/core/src/client/org/hooks.ts +67 -0
- package/corpus/core/src/client/org/index.ts +9 -0
- package/corpus/core/src/client/transcription/use-live-transcription.ts +62 -10
- package/corpus/core/src/db/client.ts +42 -3
- package/corpus/core/src/db/ensure-additive-columns.ts +60 -33
- package/corpus/core/src/deploy/build.ts +31 -2
- package/corpus/core/src/mcp-client/routes.ts +10 -1
- package/corpus/core/src/org/app-roles-handlers.ts +152 -0
- package/corpus/core/src/org/app-roles.ts +355 -0
- package/corpus/core/src/org/index.ts +27 -1
- package/corpus/core/src/org/migrations.ts +22 -0
- package/corpus/core/src/org/plugin.ts +27 -0
- package/corpus/core/src/org/schema.ts +15 -0
- package/corpus/core/src/scripts/docs/search.ts +17 -0
- package/corpus/core/src/scripts/docs/source-search.ts +10 -0
- package/corpus/core/src/server/agent-chat/script-entries.ts +3 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +53 -52
- package/corpus/core/src/server/core-routes-plugin.ts +6 -1
- package/corpus/core/src/server/framework-request-handler.ts +44 -6
- package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/core/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +31 -0
- package/corpus/core/src/templates/headless/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/analytics/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/analytics/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/analytics/actions/data-source-status.ts +21 -12
- package/corpus/templates/analytics/app/global.css +7 -7
- package/corpus/templates/analytics/app/i18n-data.ts +30 -0
- package/corpus/templates/analytics/app/lib/data-source-status.ts +2 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +54 -10
- package/corpus/templates/analytics/changelog/2026-07-28-analytics-pages-load-reliably-with-large-dashboard-workspaces.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-28-dashboard-loading-shimmer-now-flows-smoothly-in-one-directi.md +6 -0
- package/corpus/templates/analytics/server/lib/first-party-dashboard-repair.ts +5 -4
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +54 -42
- package/corpus/templates/analytics/server/plugins/db.ts +1 -18
- package/corpus/templates/assets/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/assets/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/assets/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/brain/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/templates/brain/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/brain/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/calendar/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/calendar/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/chat/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/templates/chat/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/clips/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/clips/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/clips/actions/request-transcript.ts +77 -11
- package/corpus/templates/clips/actions/save-browser-transcript.ts +28 -7
- package/corpus/templates/clips/app/routes/record.tsx +11 -3
- package/corpus/templates/clips/changelog/2026-07-28-browser-recordings-no-longer-save-a-truncated-transcript-as-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-browser-recordings-no-longer-stop-transcribing-partway-throu.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-desktop-full-screen-clips-now-get-an-instant-local-transcrip.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-long-transcriptions-no-longer-show-a-false-transcription-sto.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-restarting-for-an-update-now-installs-the-newest-release-ins.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-the-meeting-recording-pill-now-anchors-to-the-right-edge-of-.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/audio-meter.ts +31 -2
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +114 -13
- package/corpus/templates/clips/desktop/src/lib/updater.ts +12 -4
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +40 -15
- package/corpus/templates/clips/desktop/src/styles.css +10 -7
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +21 -14
- package/corpus/templates/clips/shared/transcript-status.ts +10 -0
- package/corpus/templates/content/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/content/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/content/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/crm/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/design/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/design/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/design/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +46 -3
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +7 -5
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +7 -4
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +10 -4
- package/corpus/templates/design/app/lib/design-ui-events.ts +5 -0
- package/corpus/templates/design/app/root.tsx +18 -1
- package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/dispatch/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/forms/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/forms/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/forms/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/macros/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/macros/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/mail/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/mail/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/mail/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/plan/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/templates/plan/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/plan/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/slides/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/slides/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/slides/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/templates/tasks/.agents/skills/actions/SKILL.md +32 -0
- package/corpus/templates/tasks/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/corpus/templates/tasks/.agents/skills/security/SKILL.md +31 -0
- package/corpus/templates/tasks/.agents/skills/sharing/SKILL.md +4 -0
- package/dist/a2a/artifact-response.d.ts.map +1 -1
- package/dist/a2a/artifact-response.js +30 -29
- package/dist/a2a/artifact-response.js.map +1 -1
- package/dist/action.d.ts +36 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +37 -2
- package/dist/action.js.map +1 -1
- package/dist/chat-threads/store.d.ts +12 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +48 -42
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/design-visual-edit-skill.js +46 -3
- package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +3 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +11 -9
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +57 -6
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/DefaultSpinner.d.ts +7 -0
- package/dist/client/DefaultSpinner.d.ts.map +1 -1
- package/dist/client/DefaultSpinner.js +25 -1
- package/dist/client/DefaultSpinner.js.map +1 -1
- package/dist/client/org/TeamPage.d.ts +13 -1
- package/dist/client/org/TeamPage.d.ts.map +1 -1
- package/dist/client/org/TeamPage.js +41 -11
- package/dist/client/org/TeamPage.js.map +1 -1
- package/dist/client/org/hooks.d.ts +37 -0
- package/dist/client/org/hooks.d.ts.map +1 -1
- package/dist/client/org/hooks.js +37 -0
- package/dist/client/org/hooks.js.map +1 -1
- package/dist/client/org/index.d.ts +3 -2
- package/dist/client/org/index.d.ts.map +1 -1
- package/dist/client/org/index.js +1 -1
- package/dist/client/org/index.js.map +1 -1
- package/dist/client/transcription/use-live-transcription.d.ts +8 -0
- package/dist/client/transcription/use-live-transcription.d.ts.map +1 -1
- package/dist/client/transcription/use-live-transcription.js +38 -11
- package/dist/client/transcription/use-live-transcription.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/db/client.d.ts +12 -0
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +40 -3
- package/dist/db/client.js.map +1 -1
- package/dist/db/ensure-additive-columns.d.ts.map +1 -1
- package/dist/db/ensure-additive-columns.js +54 -32
- package/dist/db/ensure-additive-columns.js.map +1 -1
- package/dist/deploy/build.d.ts +1 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +23 -2
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp-client/routes.d.ts +3 -1
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +8 -2
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/org/app-roles-handlers.d.ts +27 -0
- package/dist/org/app-roles-handlers.d.ts.map +1 -0
- package/dist/org/app-roles-handlers.js +125 -0
- package/dist/org/app-roles-handlers.js.map +1 -0
- package/dist/org/app-roles.d.ts +136 -0
- package/dist/org/app-roles.d.ts.map +1 -0
- package/dist/org/app-roles.js +216 -0
- package/dist/org/app-roles.js.map +1 -0
- package/dist/org/index.d.ts +4 -1
- package/dist/org/index.d.ts.map +1 -1
- package/dist/org/index.js +3 -1
- package/dist/org/index.js.map +1 -1
- package/dist/org/migrations.d.ts.map +1 -1
- package/dist/org/migrations.js +22 -0
- package/dist/org/migrations.js.map +1 -1
- package/dist/org/plugin.d.ts +2 -0
- package/dist/org/plugin.d.ts.map +1 -1
- package/dist/org/plugin.js +20 -0
- package/dist/org/plugin.js.map +1 -1
- package/dist/org/schema.d.ts +143 -0
- package/dist/org/schema.d.ts.map +1 -1
- package/dist/org/schema.js +14 -0
- package/dist/org/schema.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +12 -12
- package/dist/scripts/docs/search.d.ts.map +1 -1
- package/dist/scripts/docs/search.js +15 -0
- package/dist/scripts/docs/search.js.map +1 -1
- package/dist/scripts/docs/source-search.d.ts +7 -0
- package/dist/scripts/docs/source-search.d.ts.map +1 -1
- package/dist/scripts/docs/source-search.js +9 -0
- package/dist/scripts/docs/source-search.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-chat/script-entries.d.ts.map +1 -1
- package/dist/server/agent-chat/script-entries.js +3 -0
- package/dist/server/agent-chat/script-entries.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +46 -48
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +6 -1
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +42 -6
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/templates/chat/.agents/skills/actions/SKILL.md +32 -0
- package/dist/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/dist/templates/chat/.agents/skills/security/SKILL.md +31 -0
- package/dist/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
- package/dist/templates/default/.agents/skills/actions/SKILL.md +32 -0
- package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/dist/templates/default/.agents/skills/security/SKILL.md +31 -0
- package/dist/templates/headless/.agents/skills/actions/SKILL.md +32 -0
- package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
- package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
- package/dist/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
- package/docs/content/actions.mdx +47 -4
- package/docs/content/native-chat-ui.mdx +7 -0
- package/docs/content/organizations-teams-permissions.mdx +109 -3
- package/docs/content/template-design-collaboration-and-full-apps.mdx +15 -0
- package/docs/content/toolkit-org-team.mdx +72 -12
- package/package.json +1 -1
- package/src/a2a/artifact-response.ts +36 -29
- package/src/action.ts +83 -2
- package/src/chat-threads/store.ts +48 -43
- package/src/cli/skills-content/design-visual-edit-skill.ts +46 -3
- package/src/client/AgentPanel.tsx +26 -6
- package/src/client/AssistantChat.tsx +63 -7
- package/src/client/DefaultSpinner.tsx +29 -0
- package/src/client/org/TeamPage.tsx +161 -6
- package/src/client/org/hooks.ts +67 -0
- package/src/client/org/index.ts +9 -0
- package/src/client/transcription/use-live-transcription.ts +62 -10
- package/src/db/client.ts +42 -3
- package/src/db/ensure-additive-columns.ts +60 -33
- package/src/deploy/build.ts +31 -2
- package/src/mcp-client/routes.ts +10 -1
- package/src/org/app-roles-handlers.ts +152 -0
- package/src/org/app-roles.ts +355 -0
- package/src/org/index.ts +27 -1
- package/src/org/migrations.ts +22 -0
- package/src/org/plugin.ts +27 -0
- package/src/org/schema.ts +15 -0
- package/src/scripts/docs/search.ts +17 -0
- package/src/scripts/docs/source-search.ts +10 -0
- package/src/server/agent-chat/script-entries.ts +3 -0
- package/src/server/agent-chat-plugin.ts +53 -52
- package/src/server/core-routes-plugin.ts +6 -1
- package/src/server/framework-request-handler.ts +44 -6
- package/src/templates/chat/.agents/skills/actions/SKILL.md +32 -0
- package/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/src/templates/chat/.agents/skills/security/SKILL.md +31 -0
- package/src/templates/chat/.agents/skills/sharing/SKILL.md +4 -0
- package/src/templates/default/.agents/skills/actions/SKILL.md +32 -0
- package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/src/templates/default/.agents/skills/security/SKILL.md +31 -0
- package/src/templates/headless/.agents/skills/actions/SKILL.md +32 -0
- package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +32 -0
- package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +7 -0
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +40 -0
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +31 -0
- package/src/templates/workspace-core/.agents/skills/sharing/SKILL.md +4 -0
- package/corpus/core/src/templates/chat/.agents/skills/actions/SKILL.md +0 -530
- package/corpus/core/src/templates/chat/.agents/skills/adding-a-feature/SKILL.md +0 -191
- package/corpus/core/src/templates/chat/.agents/skills/agent-native-docs/SKILL.md +0 -115
- package/corpus/core/src/templates/chat/.agents/skills/agent-native-toolkit/SKILL.md +0 -177
- package/corpus/core/src/templates/chat/.agents/skills/capture-learnings/SKILL.md +0 -89
- package/corpus/core/src/templates/chat/.agents/skills/create-skill/SKILL.md +0 -221
- package/corpus/core/src/templates/chat/.agents/skills/customizing-agent-native/SKILL.md +0 -220
- package/corpus/core/src/templates/chat/.agents/skills/delegate-to-agent/SKILL.md +0 -263
- package/corpus/core/src/templates/chat/.agents/skills/feature-flags/SKILL.md +0 -169
- package/corpus/core/src/templates/chat/.agents/skills/frontend-design/SKILL.md +0 -174
- package/corpus/core/src/templates/chat/.agents/skills/real-time-sync/SKILL.md +0 -232
- package/corpus/core/src/templates/chat/.agents/skills/security/SKILL.md +0 -282
- package/corpus/core/src/templates/chat/.agents/skills/self-modifying-code/SKILL.md +0 -119
- package/corpus/core/src/templates/chat/.agents/skills/shadcn-ui/SKILL.md +0 -123
- package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +0 -122
- package/corpus/core/src/templates/chat/.env.example +0 -5
- package/corpus/core/src/templates/chat/.ignore +0 -0
- package/corpus/core/src/templates/chat/.oxfmtrc.json +0 -8
- package/corpus/core/src/templates/chat/AGENTS.md +0 -77
- package/corpus/core/src/templates/chat/CHANGELOG.md +0 -10
- package/corpus/core/src/templates/chat/DEVELOPING.md +0 -185
- package/corpus/core/src/templates/chat/README.md +0 -32
- package/corpus/core/src/templates/chat/_gitignore +0 -46
- package/corpus/core/src/templates/chat/actions/hello.ts +0 -13
- package/corpus/core/src/templates/chat/actions/navigate.ts +0 -41
- package/corpus/core/src/templates/chat/actions/run.ts +0 -2
- package/corpus/core/src/templates/chat/actions/view-screen.ts +0 -31
- package/corpus/core/src/templates/chat/app/components/layout/Header.tsx +0 -60
- package/corpus/core/src/templates/chat/app/components/layout/Layout.tsx +0 -188
- package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +0 -505
- package/corpus/core/src/templates/chat/app/components/ui/button.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/card.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/dropdown-menu.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/input.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/label.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/sheet.tsx +0 -1
- package/corpus/core/src/templates/chat/app/components/ui/toolkit-provider.tsx +0 -17
- package/corpus/core/src/templates/chat/app/components/ui/tooltip.tsx +0 -1
- package/corpus/core/src/templates/chat/app/design-system.ts +0 -3
- package/corpus/core/src/templates/chat/app/entry.client.tsx +0 -22
- package/corpus/core/src/templates/chat/app/entry.server.tsx +0 -10
- package/corpus/core/src/templates/chat/app/global.css +0 -93
- package/corpus/core/src/templates/chat/app/hooks/use-navigation-state.ts +0 -93
- package/corpus/core/src/templates/chat/app/i18n/ar-SA.ts +0 -68
- package/corpus/core/src/templates/chat/app/i18n/de-DE.ts +0 -71
- package/corpus/core/src/templates/chat/app/i18n/en-US.ts +0 -70
- package/corpus/core/src/templates/chat/app/i18n/es-ES.ts +0 -71
- package/corpus/core/src/templates/chat/app/i18n/fr-FR.ts +0 -71
- package/corpus/core/src/templates/chat/app/i18n/hi-IN.ts +0 -69
- package/corpus/core/src/templates/chat/app/i18n/index.ts +0 -34
- package/corpus/core/src/templates/chat/app/i18n/ja-JP.ts +0 -70
- package/corpus/core/src/templates/chat/app/i18n/ko-KR.ts +0 -70
- package/corpus/core/src/templates/chat/app/i18n/pt-BR.ts +0 -71
- package/corpus/core/src/templates/chat/app/i18n/zh-CN.ts +0 -66
- package/corpus/core/src/templates/chat/app/i18n/zh-TW.ts +0 -66
- package/corpus/core/src/templates/chat/app/i18n-data.ts +0 -556
- package/corpus/core/src/templates/chat/app/lib/agent-page.tsx +0 -49
- package/corpus/core/src/templates/chat/app/lib/app-config.ts +0 -11
- package/corpus/core/src/templates/chat/app/lib/tab-id.ts +0 -1
- package/corpus/core/src/templates/chat/app/lib/utils.ts +0 -1
- package/corpus/core/src/templates/chat/app/root.tsx +0 -173
- package/corpus/core/src/templates/chat/app/routes/_index.tsx +0 -94
- package/corpus/core/src/templates/chat/app/routes/agent.tsx +0 -24
- package/corpus/core/src/templates/chat/app/routes/chat.$threadId.tsx +0 -1
- package/corpus/core/src/templates/chat/app/routes/database.tsx +0 -17
- package/corpus/core/src/templates/chat/app/routes/extensions.$id.$slug.tsx +0 -2
- package/corpus/core/src/templates/chat/app/routes/extensions.$id.tsx +0 -11
- package/corpus/core/src/templates/chat/app/routes/extensions._index.tsx +0 -11
- package/corpus/core/src/templates/chat/app/routes/extensions.tsx +0 -5
- package/corpus/core/src/templates/chat/app/routes/observability.tsx +0 -19
- package/corpus/core/src/templates/chat/app/routes/settings.tsx +0 -89
- package/corpus/core/src/templates/chat/app/routes/team.tsx +0 -11
- package/corpus/core/src/templates/chat/app/routes.ts +0 -4
- package/corpus/core/src/templates/chat/app/vite-env.d.ts +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-24-a-new-settings-page-gives-quick-access-to-language-workspace.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-24-added-a-language-picker-and-localized-app-chrome-for-support.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-27-traditional-chinese-copy-uses-taiwan-terminology.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-06-29-chat-layouts-adapt-when-the-agent-sidebar-is-open.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +0 -5
- package/corpus/core/src/templates/chat/changelog/2026-07-10-chat-now-makes-ai-connection-setup-clear-without-shifting-th.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-13-a-full-agent-page-now-brings-context-files-connections-jobs-.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-14-chat-opens-reliably-on-hosted-deployments-instead-of-failing.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-15-chat-navigation-focuses-on-chat-and-agent.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-17-the-agent-chat-sidebar-stays-closed-until-you-open-it-or-sta.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-22-compact-sidebar-footer.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-22-full-page-chat-keeps-the-active-conversation-when-moving-to-.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-22-manage-agent-navigation-now-uses-the-connected-nodes-icon.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-22-recent-chats-are-easier-to-scan-and-expand.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-23-full-page-chat-is-better-centered-with-quieter-chat-history-.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-24-borderless-secondary-surfaces.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-24-sidebar-footer-controls-follow-a-consistent-order.md +0 -6
- package/corpus/core/src/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +0 -6
- package/corpus/core/src/templates/chat/components.json +0 -20
- package/corpus/core/src/templates/chat/learnings.defaults.md +0 -5
- package/corpus/core/src/templates/chat/netlify.toml +0 -11
- package/corpus/core/src/templates/chat/package.json +0 -92
- package/corpus/core/src/templates/chat/public/agent-native-icon-dark.svg +0 -10
- package/corpus/core/src/templates/chat/public/agent-native-icon-light.svg +0 -10
- package/corpus/core/src/templates/chat/public/agent-native-logo-dark.svg +0 -21
- package/corpus/core/src/templates/chat/public/agent-native-logo-light.svg +0 -21
- package/corpus/core/src/templates/chat/public/favicon.svg +0 -1
- package/corpus/core/src/templates/chat/public/icon-180.svg +0 -1
- package/corpus/core/src/templates/chat/public/icon-192.svg +0 -1
- package/corpus/core/src/templates/chat/public/icon-512.svg +0 -1
- package/corpus/core/src/templates/chat/public/manifest.json +0 -21
- package/corpus/core/src/templates/chat/react-router.config.ts +0 -7
- package/corpus/core/src/templates/chat/server/middleware/auth.ts +0 -15
- package/corpus/core/src/templates/chat/server/plugins/agent-chat.ts +0 -21
- package/corpus/core/src/templates/chat/server/plugins/auth.ts +0 -17
- package/corpus/core/src/templates/chat/server/routes/[...page].get.ts +0 -5
- package/corpus/core/src/templates/chat/ssr-entry.ts +0 -15
- package/corpus/core/src/templates/chat/tsconfig.json +0 -21
- package/corpus/core/src/templates/chat/vite.config.ts +0 -19
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-app member roles — an overlay on org membership, never a second roster.
|
|
3
|
+
*
|
|
4
|
+
* Three role systems now coexist and answer different questions:
|
|
5
|
+
* - org role (`org_members.role`) — what may this person do to the *team*?
|
|
6
|
+
* - app role (this module) — what may this person do inside *one app*?
|
|
7
|
+
* - share role (`sharing/`) — what may this person do to *one row*?
|
|
8
|
+
*
|
|
9
|
+
* They do not imply one another in either direction. In particular an app
|
|
10
|
+
* `admin` is not an org admin: nothing here is ever read by the org handlers.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ActionRunContext } from "../action.js";
|
|
14
|
+
import { getDbExec } from "../db/client.js";
|
|
15
|
+
import {
|
|
16
|
+
getRequestOrgId,
|
|
17
|
+
getRequestUserEmail,
|
|
18
|
+
} from "../server/request-context.js";
|
|
19
|
+
import { ForbiddenError } from "../sharing/access.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The serializable half of an app-role declaration. Kept free of database
|
|
23
|
+
* imports so the same object can be passed to `defineAppRoles` on the server
|
|
24
|
+
* and to `<TeamPage appRoles={...} />` in the browser bundle.
|
|
25
|
+
*/
|
|
26
|
+
export interface AppRolesDescriptor<R extends string = string> {
|
|
27
|
+
/**
|
|
28
|
+
* Stable identifier for the app these roles belong to. Comes from app source,
|
|
29
|
+
* never from a request: the REST layer resolves a descriptor out of the
|
|
30
|
+
* registry below and 404s on anything unregistered, so a client cannot name
|
|
31
|
+
* an app the server did not declare.
|
|
32
|
+
*/
|
|
33
|
+
appId: string;
|
|
34
|
+
/**
|
|
35
|
+
* The complete role vocabulary, as an unordered SET. Declaration order
|
|
36
|
+
* carries no meaning — `["ae", "se", "csm"]` are classifications, and ranking
|
|
37
|
+
* them would silently grant the last one everything the first one has.
|
|
38
|
+
* Authorization is always an explicit list of accepted roles.
|
|
39
|
+
*/
|
|
40
|
+
roles: readonly R[];
|
|
41
|
+
/**
|
|
42
|
+
* What to show for an org member who has no assignment yet.
|
|
43
|
+
*
|
|
44
|
+
* DISPLAY ONLY — `requireAny` never matches it. If an unassigned member could
|
|
45
|
+
* satisfy a guard, "nobody has assigned this person" and "this person was
|
|
46
|
+
* granted the role" would be the same answer, and later widening the default
|
|
47
|
+
* would silently widen every guard that names it.
|
|
48
|
+
*/
|
|
49
|
+
defaultRole?: R;
|
|
50
|
+
/** Optional human labels for the role picker. Falls back to the raw value. */
|
|
51
|
+
roleLabels?: Partial<Record<R, string>>;
|
|
52
|
+
/**
|
|
53
|
+
* Column header for the role in `<TeamPage appRoles={...} />`. Defaults to
|
|
54
|
+
* `appId`. Owned by the app rather than the framework's i18n catalogs — the
|
|
55
|
+
* vocabulary it labels is app-specific, so its translations are too.
|
|
56
|
+
*/
|
|
57
|
+
label?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Why a caller does or does not hold an app role. Each outcome is a distinct
|
|
62
|
+
* value on purpose: collapsing "not in this org" into "no role assigned" would
|
|
63
|
+
* report a membership problem as a permissions problem, and the operator would
|
|
64
|
+
* go looking in the wrong settings page.
|
|
65
|
+
*
|
|
66
|
+
* A failed lookup is NOT in this union — it throws. A database error is not an
|
|
67
|
+
* answer, and returning one of these statuses for it would deny loudly in
|
|
68
|
+
* exactly the same shape as a legitimate denial.
|
|
69
|
+
*/
|
|
70
|
+
export type AppRoleLookup<R extends string = string> =
|
|
71
|
+
| { status: "assigned"; role: R; orgId: string }
|
|
72
|
+
| { status: "unassigned"; orgId: string }
|
|
73
|
+
| { status: "not-a-member"; orgId: string }
|
|
74
|
+
| { status: "no-identity" }
|
|
75
|
+
| { status: "no-org" };
|
|
76
|
+
|
|
77
|
+
export interface AppRoleCaller {
|
|
78
|
+
userEmail?: string | null;
|
|
79
|
+
orgId?: string | null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface AppRoles<R extends string = string> {
|
|
83
|
+
descriptor: AppRolesDescriptor<R>;
|
|
84
|
+
appId: string;
|
|
85
|
+
roles: readonly R[];
|
|
86
|
+
/** Resolve without throwing on denial — for UI and for building richer guards. */
|
|
87
|
+
resolve: (caller?: AppRoleCaller) => Promise<AppRoleLookup<R>>;
|
|
88
|
+
/** Throw unless the caller holds one of `allowed`; returns the matched role. */
|
|
89
|
+
assertAny: (allowed: readonly R[], caller?: AppRoleCaller) => Promise<R>;
|
|
90
|
+
/** Guard for `defineAction({ authorize })`. Accepts an explicit assignment only. */
|
|
91
|
+
requireAny: (
|
|
92
|
+
...allowed: R[]
|
|
93
|
+
) => (args: unknown, ctx?: ActionRunContext) => Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const registry = new Map<string, AppRolesDescriptor<string>>();
|
|
97
|
+
|
|
98
|
+
/** Descriptor for a registered app id, or undefined. Used by the REST layer. */
|
|
99
|
+
export function getRegisteredAppRoles(
|
|
100
|
+
appId: string,
|
|
101
|
+
): AppRolesDescriptor<string> | undefined {
|
|
102
|
+
return registry.get(appId);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function listRegisteredAppRoles(): AppRolesDescriptor<string>[] {
|
|
106
|
+
return [...registry.values()];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const normalizeEmail = (email: string): string => email.trim().toLowerCase();
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* `undefined` means "the caller did not say" and falls back to the ambient
|
|
113
|
+
* request context; an explicit `null` means "this run has no user/org" and must
|
|
114
|
+
* NOT. Collapsing the two lets a system or cron caller that deliberately
|
|
115
|
+
* declared no identity inherit whichever request happens to be on the stack,
|
|
116
|
+
* and authorize as that person.
|
|
117
|
+
*/
|
|
118
|
+
function callerIdentity(caller?: AppRoleCaller): {
|
|
119
|
+
email: string | null;
|
|
120
|
+
orgId: string | null;
|
|
121
|
+
} {
|
|
122
|
+
const email =
|
|
123
|
+
caller?.userEmail !== undefined ? caller.userEmail : getRequestUserEmail();
|
|
124
|
+
const orgId = caller?.orgId !== undefined ? caller.orgId : getRequestOrgId();
|
|
125
|
+
return {
|
|
126
|
+
email: email && email.trim() ? email.trim() : null,
|
|
127
|
+
orgId: orgId && orgId.trim() ? orgId.trim() : null,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Declare an app's role vocabulary and get the server-side guards for it.
|
|
133
|
+
*
|
|
134
|
+
* ```ts
|
|
135
|
+
* export const coachAccess = defineAppRoles({
|
|
136
|
+
* appId: "coach",
|
|
137
|
+
* roles: ["member", "coach-admin"] as const,
|
|
138
|
+
* defaultRole: "member",
|
|
139
|
+
* });
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
export function defineAppRoles<const R extends string>(
|
|
143
|
+
descriptor: AppRolesDescriptor<R>,
|
|
144
|
+
): AppRoles<R> {
|
|
145
|
+
const appId = descriptor.appId.trim();
|
|
146
|
+
if (!appId) throw new Error("defineAppRoles: appId is required");
|
|
147
|
+
if (!descriptor.roles.length) {
|
|
148
|
+
throw new Error(`defineAppRoles(${appId}): at least one role is required`);
|
|
149
|
+
}
|
|
150
|
+
if (
|
|
151
|
+
descriptor.defaultRole &&
|
|
152
|
+
!descriptor.roles.includes(descriptor.defaultRole)
|
|
153
|
+
) {
|
|
154
|
+
throw new Error(
|
|
155
|
+
`defineAppRoles(${appId}): defaultRole "${descriptor.defaultRole}" is not in roles`,
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const existing = registry.get(appId);
|
|
160
|
+
if (existing && existing !== (descriptor as AppRolesDescriptor<string>)) {
|
|
161
|
+
// Two vocabularies behind one appId means the REST layer and the guards can
|
|
162
|
+
// disagree about which roles exist, and the settings UI would offer roles
|
|
163
|
+
// no guard accepts.
|
|
164
|
+
throw new Error(
|
|
165
|
+
`defineAppRoles: appId "${appId}" is already declared with a different descriptor`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
registry.set(appId, descriptor as AppRolesDescriptor<string>);
|
|
169
|
+
|
|
170
|
+
const resolve = (caller?: AppRoleCaller) =>
|
|
171
|
+
resolveAppRole(descriptor, caller);
|
|
172
|
+
|
|
173
|
+
const assertAny = async (
|
|
174
|
+
allowed: readonly R[],
|
|
175
|
+
caller?: AppRoleCaller,
|
|
176
|
+
): Promise<R> => {
|
|
177
|
+
const lookup = await resolve(caller);
|
|
178
|
+
if (lookup.status === "assigned" && allowed.includes(lookup.role)) {
|
|
179
|
+
return lookup.role;
|
|
180
|
+
}
|
|
181
|
+
throw new ForbiddenError(denialMessage(appId, allowed, lookup));
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
descriptor,
|
|
186
|
+
appId,
|
|
187
|
+
roles: descriptor.roles,
|
|
188
|
+
resolve,
|
|
189
|
+
assertAny,
|
|
190
|
+
requireAny: (...allowed: R[]) => {
|
|
191
|
+
// An empty accepted set can never match. Failing closed at request time
|
|
192
|
+
// would be correct but mute — `authorize: coachAccess.requireAny()` is a
|
|
193
|
+
// dropped argument, and it should fail where the mistake is, not as an
|
|
194
|
+
// unexplained 403 in production.
|
|
195
|
+
if (!allowed.length) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`defineAppRoles(${appId}): requireAny() needs at least one accepted role`,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
const unknown = allowed.filter((r) => !descriptor.roles.includes(r));
|
|
201
|
+
if (unknown.length) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`defineAppRoles(${appId}): requireAny() names undeclared role(s) ${unknown.join(", ")}`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
return async (_args: unknown, ctx?: ActionRunContext) => {
|
|
207
|
+
await assertAny(allowed, {
|
|
208
|
+
userEmail: ctx?.userEmail,
|
|
209
|
+
orgId: ctx?.orgId,
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function denialMessage<R extends string>(
|
|
217
|
+
appId: string,
|
|
218
|
+
allowed: readonly R[],
|
|
219
|
+
lookup: AppRoleLookup<R>,
|
|
220
|
+
): string {
|
|
221
|
+
const need = `Requires ${appId} role ${allowed.join(" or ")}`;
|
|
222
|
+
switch (lookup.status) {
|
|
223
|
+
case "assigned":
|
|
224
|
+
return `${need} (have ${lookup.role})`;
|
|
225
|
+
case "unassigned":
|
|
226
|
+
return `${need} (no ${appId} role assigned)`;
|
|
227
|
+
case "not-a-member":
|
|
228
|
+
return `${need} (not a member of this organization)`;
|
|
229
|
+
case "no-org":
|
|
230
|
+
return `${need} (no active organization)`;
|
|
231
|
+
case "no-identity":
|
|
232
|
+
return `${need} (no authenticated user)`;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Single read behind every app-role decision.
|
|
238
|
+
*
|
|
239
|
+
* Membership and assignment are resolved in ONE statement so an assignment left
|
|
240
|
+
* behind by a removed member can never authorize: the row only reaches the
|
|
241
|
+
* result set through `org_members`. Splitting this into two queries would also
|
|
242
|
+
* let a member removed between them still pass.
|
|
243
|
+
*/
|
|
244
|
+
export async function resolveAppRole<R extends string>(
|
|
245
|
+
descriptor: AppRolesDescriptor<R>,
|
|
246
|
+
caller?: AppRoleCaller,
|
|
247
|
+
): Promise<AppRoleLookup<R>> {
|
|
248
|
+
const { email, orgId } = callerIdentity(caller);
|
|
249
|
+
if (!email) return { status: "no-identity" };
|
|
250
|
+
if (!orgId) return { status: "no-org" };
|
|
251
|
+
|
|
252
|
+
const { rows } = await getDbExec().execute({
|
|
253
|
+
sql: `SELECT r.role AS "appRole"
|
|
254
|
+
FROM org_members m
|
|
255
|
+
LEFT JOIN app_member_roles r
|
|
256
|
+
ON r.org_id = m.org_id
|
|
257
|
+
AND r.app_id = ?
|
|
258
|
+
AND LOWER(r.email) = LOWER(m.email)
|
|
259
|
+
WHERE m.org_id = ? AND LOWER(m.email) = ?
|
|
260
|
+
LIMIT 1`,
|
|
261
|
+
args: [descriptor.appId, orgId, normalizeEmail(email)],
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const row = rows[0] as { appRole?: unknown; approle?: unknown } | undefined;
|
|
265
|
+
if (!row) return { status: "not-a-member", orgId };
|
|
266
|
+
|
|
267
|
+
const raw = row.appRole ?? row.approle;
|
|
268
|
+
if (raw === null || raw === undefined) return { status: "unassigned", orgId };
|
|
269
|
+
|
|
270
|
+
const role = String(raw) as R;
|
|
271
|
+
// A stored value outside the declared vocabulary is a role that was removed
|
|
272
|
+
// from the app's declaration while assignments still referenced it. Treating
|
|
273
|
+
// it as unassigned keeps a retired role from satisfying a guard that no
|
|
274
|
+
// longer knows what it meant.
|
|
275
|
+
if (!descriptor.roles.includes(role)) return { status: "unassigned", orgId };
|
|
276
|
+
|
|
277
|
+
return { status: "assigned", role, orgId };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export interface AppMemberRoleRow {
|
|
281
|
+
email: string;
|
|
282
|
+
role: string;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Every assignment for current members of one app in one org. */
|
|
286
|
+
export async function listAppMemberRoles(
|
|
287
|
+
appId: string,
|
|
288
|
+
orgId: string,
|
|
289
|
+
): Promise<AppMemberRoleRow[]> {
|
|
290
|
+
const { rows } = await getDbExec().execute({
|
|
291
|
+
sql: `SELECT r.email, r.role
|
|
292
|
+
FROM app_member_roles r
|
|
293
|
+
INNER JOIN org_members m
|
|
294
|
+
ON m.org_id = r.org_id
|
|
295
|
+
AND LOWER(m.email) = LOWER(r.email)
|
|
296
|
+
WHERE r.org_id = ? AND r.app_id = ?`,
|
|
297
|
+
args: [orgId, appId],
|
|
298
|
+
});
|
|
299
|
+
return rows.map((r: any) => ({
|
|
300
|
+
email: String(r.email),
|
|
301
|
+
role: String(r.role),
|
|
302
|
+
}));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const nanoid = (): string =>
|
|
306
|
+
globalThis.crypto?.randomUUID?.().replace(/-/g, "") ??
|
|
307
|
+
Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Assign or clear one member's app role. `role: null` deletes the assignment,
|
|
311
|
+
* which is distinct from assigning the descriptor's `defaultRole` — an unassigned
|
|
312
|
+
* member satisfies no guard, whereas a default is only ever a display value.
|
|
313
|
+
*
|
|
314
|
+
* Callers must have already established that the target is an org member and
|
|
315
|
+
* that the actor may manage the org; this function does not re-check either.
|
|
316
|
+
*/
|
|
317
|
+
export async function setAppMemberRole(opts: {
|
|
318
|
+
appId: string;
|
|
319
|
+
orgId: string;
|
|
320
|
+
email: string;
|
|
321
|
+
role: string | null;
|
|
322
|
+
updatedBy: string;
|
|
323
|
+
}): Promise<void> {
|
|
324
|
+
const exec = getDbExec();
|
|
325
|
+
const email = normalizeEmail(opts.email);
|
|
326
|
+
|
|
327
|
+
if (opts.role === null) {
|
|
328
|
+
await exec.execute({
|
|
329
|
+
sql: `DELETE FROM app_member_roles
|
|
330
|
+
WHERE org_id = ? AND app_id = ? AND LOWER(email) = ?`,
|
|
331
|
+
args: [opts.orgId, opts.appId, email],
|
|
332
|
+
});
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const now = Date.now();
|
|
337
|
+
await exec.execute({
|
|
338
|
+
sql: `INSERT INTO app_member_roles
|
|
339
|
+
(id, org_id, app_id, email, role, updated_by, updated_at)
|
|
340
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
341
|
+
ON CONFLICT (org_id, app_id, LOWER(email)) DO UPDATE SET
|
|
342
|
+
role = excluded.role,
|
|
343
|
+
updated_by = excluded.updated_by,
|
|
344
|
+
updated_at = excluded.updated_at`,
|
|
345
|
+
args: [
|
|
346
|
+
nanoid(),
|
|
347
|
+
opts.orgId,
|
|
348
|
+
opts.appId,
|
|
349
|
+
email,
|
|
350
|
+
opts.role,
|
|
351
|
+
opts.updatedBy,
|
|
352
|
+
now,
|
|
353
|
+
],
|
|
354
|
+
});
|
|
355
|
+
}
|
package/src/org/index.ts
CHANGED
|
@@ -39,13 +39,34 @@ export type { AcceptPendingResult } from "./accept-pending.js";
|
|
|
39
39
|
export { autoJoinDomainMatchingOrgs } from "./auto-join-domain.js";
|
|
40
40
|
export type { AutoJoinDomainResult } from "./auto-join-domain.js";
|
|
41
41
|
|
|
42
|
+
export {
|
|
43
|
+
defineAppRoles,
|
|
44
|
+
getRegisteredAppRoles,
|
|
45
|
+
listRegisteredAppRoles,
|
|
46
|
+
listAppMemberRoles,
|
|
47
|
+
resolveAppRole,
|
|
48
|
+
setAppMemberRole,
|
|
49
|
+
} from "./app-roles.js";
|
|
50
|
+
export type {
|
|
51
|
+
AppRoles,
|
|
52
|
+
AppRolesDescriptor,
|
|
53
|
+
AppRoleCaller,
|
|
54
|
+
AppRoleLookup,
|
|
55
|
+
AppMemberRoleRow,
|
|
56
|
+
} from "./app-roles.js";
|
|
57
|
+
|
|
42
58
|
export { ORG_MIGRATIONS } from "./migrations.js";
|
|
43
59
|
|
|
44
60
|
export { createOrgPlugin, defaultOrgPlugin } from "./plugin.js";
|
|
45
61
|
|
|
46
62
|
// Drizzle schema (re-exported so templates can write typed queries against
|
|
47
63
|
// org tables without redefining the schema themselves).
|
|
48
|
-
export {
|
|
64
|
+
export {
|
|
65
|
+
organizations,
|
|
66
|
+
orgMembers,
|
|
67
|
+
orgInvitations,
|
|
68
|
+
appMemberRoles,
|
|
69
|
+
} from "./schema.js";
|
|
49
70
|
|
|
50
71
|
// Individual handlers — exported so templates can compose a custom org plugin
|
|
51
72
|
// while still using the framework-provided handlers.
|
|
@@ -65,4 +86,9 @@ export {
|
|
|
65
86
|
receiveA2ASecretHandler,
|
|
66
87
|
} from "./handlers.js";
|
|
67
88
|
|
|
89
|
+
export {
|
|
90
|
+
listAppRolesHandler,
|
|
91
|
+
setAppRoleHandler,
|
|
92
|
+
} from "./app-roles-handlers.js";
|
|
93
|
+
|
|
68
94
|
export { isFreeEmailProvider } from "./free-email-providers.js";
|
package/src/org/migrations.ts
CHANGED
|
@@ -102,4 +102,26 @@ export const ORG_MIGRATIONS = [
|
|
|
102
102
|
name: "org-members-unique-lower-email-idx",
|
|
103
103
|
sql: `CREATE UNIQUE INDEX IF NOT EXISTS org_members_org_lower_email_uidx ON org_members (org_id, LOWER(email))`,
|
|
104
104
|
},
|
|
105
|
+
{
|
|
106
|
+
version: 1011,
|
|
107
|
+
sql: `CREATE TABLE IF NOT EXISTS app_member_roles (
|
|
108
|
+
id TEXT PRIMARY KEY,
|
|
109
|
+
org_id TEXT NOT NULL,
|
|
110
|
+
app_id TEXT NOT NULL,
|
|
111
|
+
email TEXT NOT NULL,
|
|
112
|
+
role TEXT NOT NULL,
|
|
113
|
+
updated_by TEXT NOT NULL,
|
|
114
|
+
updated_at INTEGER NOT NULL
|
|
115
|
+
)`,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
// Every guarded action resolves (org_id, app_id, LOWER(email)) on the
|
|
119
|
+
// request path, and the assignment write upserts on the same key. Unique
|
|
120
|
+
// rather than plain so a concurrent double-assign is rejected by the
|
|
121
|
+
// database instead of leaving two rows whose winner depends on read order.
|
|
122
|
+
version: 1012,
|
|
123
|
+
name: "app-member-roles-unique-org-app-lower-email-idx",
|
|
124
|
+
sql: `CREATE UNIQUE INDEX IF NOT EXISTS app_member_roles_org_app_lower_email_uidx
|
|
125
|
+
ON app_member_roles (org_id, app_id, LOWER(email))`,
|
|
126
|
+
},
|
|
105
127
|
];
|
package/src/org/plugin.ts
CHANGED
|
@@ -13,6 +13,10 @@ import {
|
|
|
13
13
|
FRAMEWORK_PREFIX,
|
|
14
14
|
markDefaultPluginProvided,
|
|
15
15
|
} from "../server/framework-request-handler.js";
|
|
16
|
+
import {
|
|
17
|
+
listAppRolesHandler,
|
|
18
|
+
setAppRoleHandler,
|
|
19
|
+
} from "./app-roles-handlers.js";
|
|
16
20
|
import {
|
|
17
21
|
getMyOrgHandler,
|
|
18
22
|
createOrgHandler,
|
|
@@ -50,6 +54,8 @@ const ORG_PREFIX = `${FRAMEWORK_PREFIX}/org`;
|
|
|
50
54
|
* PUT /_agent-native/org/switch — switch active org
|
|
51
55
|
* GET /_agent-native/org/members — list members of active org
|
|
52
56
|
* DELETE /_agent-native/org/members/:email — remove member (owner/admin only)
|
|
57
|
+
* GET /_agent-native/org/app-roles?appId=X — app role vocabulary + assignments
|
|
58
|
+
* PUT /_agent-native/org/app-roles/:email — assign/clear app role (owner/admin)
|
|
53
59
|
* GET /_agent-native/org/invitations — list pending invites
|
|
54
60
|
* POST /_agent-native/org/invitations — invite by email
|
|
55
61
|
* POST /_agent-native/org/invitations/:id/accept — accept an invitation
|
|
@@ -82,6 +88,27 @@ export function createOrgPlugin(): NitroPluginDef {
|
|
|
82
88
|
}),
|
|
83
89
|
);
|
|
84
90
|
|
|
91
|
+
// /app-roles and /app-roles/:email — per-app role overlay on the roster.
|
|
92
|
+
app.use(
|
|
93
|
+
`${ORG_PREFIX}/app-roles`,
|
|
94
|
+
defineEventHandler(async (event: H3Event) => {
|
|
95
|
+
const tail = getRequestURL(event).pathname || "/";
|
|
96
|
+
const method = getMethod(event);
|
|
97
|
+
if (tail === "" || tail === "/") {
|
|
98
|
+
if (method !== "GET") {
|
|
99
|
+
setResponseStatus(event, 405);
|
|
100
|
+
return { error: "Method not allowed" };
|
|
101
|
+
}
|
|
102
|
+
return listAppRolesHandler(event);
|
|
103
|
+
}
|
|
104
|
+
if (method !== "PUT") {
|
|
105
|
+
setResponseStatus(event, 405);
|
|
106
|
+
return { error: "Method not allowed" };
|
|
107
|
+
}
|
|
108
|
+
return setAppRoleHandler(event);
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
|
|
85
112
|
// /members, /members/:email, /members/:email/role — dispatch by path-
|
|
86
113
|
// tail + method in a single handler so H3's prefix-based `app.use`
|
|
87
114
|
// doesn't route a DELETE for /members/alice@example.com to the
|
package/src/org/schema.ts
CHANGED
|
@@ -17,6 +17,21 @@ export const orgMembers = table("org_members", {
|
|
|
17
17
|
joinedAt: integer("joined_at").notNull(),
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Per-app role assignments, keyed to an existing `org_members` row. An app role
|
|
22
|
+
* only narrows what a member may do inside one app; it never grants membership,
|
|
23
|
+
* and it never implies an org role.
|
|
24
|
+
*/
|
|
25
|
+
export const appMemberRoles = table("app_member_roles", {
|
|
26
|
+
id: text("id").primaryKey(),
|
|
27
|
+
orgId: text("org_id").notNull(),
|
|
28
|
+
appId: text("app_id").notNull(),
|
|
29
|
+
email: text("email").notNull(),
|
|
30
|
+
role: text("role").notNull(),
|
|
31
|
+
updatedBy: text("updated_by").notNull(),
|
|
32
|
+
updatedAt: integer("updated_at").notNull(),
|
|
33
|
+
});
|
|
34
|
+
|
|
20
35
|
export const orgInvitations = table("org_invitations", {
|
|
21
36
|
id: text("id").primaryKey(),
|
|
22
37
|
orgId: text("org_id").notNull(),
|
|
@@ -39,6 +39,21 @@ function getDocsDir(): string {
|
|
|
39
39
|
return path.join(getDocsRoot(), "content");
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Bundled serverless deploys carry the runtime agent bundle but not the
|
|
44
|
+
* framework doc pages, so a miss there means "not deployed", not "no such
|
|
45
|
+
* doc". Say which, or the agent concludes a documented API does not exist.
|
|
46
|
+
*/
|
|
47
|
+
function logMissingFrameworkDocsNote(): void {
|
|
48
|
+
if (fs.existsSync(getDocsDir())) return;
|
|
49
|
+
console.log(
|
|
50
|
+
"\nNote: bundled framework doc pages are not deployed here (only this " +
|
|
51
|
+
"app's AGENTS.md and skills are). A miss above does not mean the page " +
|
|
52
|
+
"does not exist — check the framework docs another way before concluding " +
|
|
53
|
+
"an API is missing.",
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
42
57
|
function parseFrontmatter(raw: string): {
|
|
43
58
|
data: Record<string, string>;
|
|
44
59
|
body: string;
|
|
@@ -256,6 +271,7 @@ Options:
|
|
|
256
271
|
if (!doc) {
|
|
257
272
|
console.log(`Doc not found: ${parsed.slug}`);
|
|
258
273
|
console.log(`Available: ${docs.map((d) => d.slug).join(", ")}`);
|
|
274
|
+
logMissingFrameworkDocsNote();
|
|
259
275
|
return;
|
|
260
276
|
}
|
|
261
277
|
console.log(`# ${doc.title}\n`);
|
|
@@ -268,6 +284,7 @@ Options:
|
|
|
268
284
|
const results = await searchDocs(parsed.query);
|
|
269
285
|
if (results.length === 0) {
|
|
270
286
|
console.log(`No docs found matching "${parsed.query}".`);
|
|
287
|
+
logMissingFrameworkDocsNote();
|
|
271
288
|
return;
|
|
272
289
|
}
|
|
273
290
|
console.log(`Found ${results.length} doc(s) matching "${parsed.query}":\n`);
|
|
@@ -70,6 +70,16 @@ function getCorpusRoot(): string {
|
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* The corpus is a sibling of the package root, so a bundled serverless deploy
|
|
75
|
+
* never carries it. Callers register this tool conditionally: advertising a
|
|
76
|
+
* search tool whose only possible answer is "not found" costs the agent a turn
|
|
77
|
+
* and reads to it like the code genuinely does not exist.
|
|
78
|
+
*/
|
|
79
|
+
export function hasSourceCorpus(): boolean {
|
|
80
|
+
return fs.existsSync(getCorpusRoot());
|
|
81
|
+
}
|
|
82
|
+
|
|
73
83
|
function isProbablyTextFile(filePath: string): boolean {
|
|
74
84
|
const ext = path.extname(filePath).toLowerCase();
|
|
75
85
|
if (TEXT_EXTENSIONS.has(ext)) return true;
|
|
@@ -270,6 +270,9 @@ export async function createDocsScriptEntries(): Promise<
|
|
|
270
270
|
|
|
271
271
|
try {
|
|
272
272
|
const mod = await import("../../scripts/docs/source-search.js");
|
|
273
|
+
if (!mod.hasSourceCorpus()) {
|
|
274
|
+
return entries;
|
|
275
|
+
}
|
|
273
276
|
entries["source-search"] = wrapCliScript(
|
|
274
277
|
{
|
|
275
278
|
description:
|