@agent-native/core 0.127.1 → 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 +92 -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/cli/template-sync.ts +96 -0
- 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/builder-design-systems.ts +1 -1
- 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/chat/_gitignore +2 -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/design-generation/SKILL.md +23 -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/actions/create-file.ts +15 -0
- package/corpus/templates/design/actions/generate-design.ts +40 -0
- package/corpus/templates/design/actions/present-design-variants.ts +16 -0
- 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/pages/design-editor/save-failure.ts +7 -1
- package/corpus/templates/design/app/root.tsx +18 -1
- package/corpus/templates/design/changelog/2026-07-28-generated-designs-with-malformed-html-are-now-rejected-at-sa.md +6 -0
- 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/design/server/source-workspace.ts +1 -0
- package/corpus/templates/design/shared/html-integrity.ts +608 -18
- 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/slides/app/components/deck/DeckCard.tsx +2 -6
- 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/cli/template-sync.d.ts.map +1 -1
- package/dist/cli/template-sync.js +88 -0
- package/dist/cli/template-sync.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 +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- 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/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/observability/routes.d.ts +3 -3
- 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/provider-api/actions/custom-provider-registration.d.ts +4 -4
- package/dist/provider-api/actions/provider-api.d.ts +12 -12
- package/dist/resources/handlers.d.ts +1 -1
- 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/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/builder-design-systems.js +1 -1
- package/dist/server/builder-design-systems.js.map +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/transcribe-voice.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/chat/_gitignore +2 -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/cli/template-sync.ts +96 -0
- 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/builder-design-systems.ts +1 -1
- 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/chat/_gitignore +2 -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 -44
- 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
|
@@ -333,8 +333,16 @@ function activeRunStuckThresholdMs(runInfo: ActiveRunLookup): number {
|
|
|
333
333
|
}
|
|
334
334
|
|
|
335
335
|
function activeRunLooksStale(runInfo: ActiveRunLookup): boolean {
|
|
336
|
+
// A run killed before it emitted anything has no `lastProgressAt`. Falling
|
|
337
|
+
// back to the heartbeat keeps that case from reading as perpetually fresh —
|
|
338
|
+
// "never reported progress" is the worst case, not an exemption from the
|
|
339
|
+
// staleness check.
|
|
336
340
|
const lastProgressAt =
|
|
337
|
-
typeof runInfo.lastProgressAt === "number"
|
|
341
|
+
typeof runInfo.lastProgressAt === "number"
|
|
342
|
+
? runInfo.lastProgressAt
|
|
343
|
+
: typeof runInfo.heartbeatAt === "number"
|
|
344
|
+
? runInfo.heartbeatAt
|
|
345
|
+
: null;
|
|
338
346
|
const nowMs =
|
|
339
347
|
typeof runInfo.serverNow === "number" ? runInfo.serverNow : Date.now();
|
|
340
348
|
const thresholdMs = activeRunStuckThresholdMs(runInfo);
|
|
@@ -345,6 +353,16 @@ function activeRunLooksStale(runInfo: ActiveRunLookup): boolean {
|
|
|
345
353
|
);
|
|
346
354
|
}
|
|
347
355
|
|
|
356
|
+
/**
|
|
357
|
+
* The stored active run is what keeps the "Thinking…" spinner up across a
|
|
358
|
+
* reload, and the `/runs/active` probe is the only path that clears it. A 5xx
|
|
359
|
+
* or a network drop there means "couldn't determine", NOT "still running" —
|
|
360
|
+
* collapsing the two is what strands the spinner forever when the server is
|
|
361
|
+
* briefly unreachable, because no caller reschedules the probe. Retry inside
|
|
362
|
+
* the call, then give up loudly.
|
|
363
|
+
*/
|
|
364
|
+
const ACTIVE_RUN_PROBE_RETRY_DELAYS_MS = [400, 1200];
|
|
365
|
+
|
|
348
366
|
/**
|
|
349
367
|
* Decide whether a reconnect should give up with "no progress". The signal is
|
|
350
368
|
* *time since the last streamed event*, NOT total reconnect duration: a healthy
|
|
@@ -3440,12 +3458,50 @@ const AssistantChatInner = forwardRef<
|
|
|
3440
3458
|
if (isRuntimeRunningRef.current || isAutoResumingRef.current) {
|
|
3441
3459
|
return false;
|
|
3442
3460
|
}
|
|
3461
|
+
const storedActiveRun = getActiveRun();
|
|
3462
|
+
let runRes: Response | null = null;
|
|
3463
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
3464
|
+
try {
|
|
3465
|
+
const res = await fetch(
|
|
3466
|
+
`${apiUrl}/runs/active?threadId=${encodeURIComponent(threadId)}`,
|
|
3467
|
+
);
|
|
3468
|
+
if (res.ok) {
|
|
3469
|
+
runRes = res;
|
|
3470
|
+
break;
|
|
3471
|
+
}
|
|
3472
|
+
} catch {
|
|
3473
|
+
// Fall through to the retry/give-up path below.
|
|
3474
|
+
}
|
|
3475
|
+
const retryDelayMs = ACTIVE_RUN_PROBE_RETRY_DELAYS_MS[attempt];
|
|
3476
|
+
if (retryDelayMs === undefined) break;
|
|
3477
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
if (!runRes) {
|
|
3481
|
+
// Still unreachable. Only a stored run for this thread is holding the
|
|
3482
|
+
// spinner up, so that is also the only case worth interrupting the user
|
|
3483
|
+
// for — drop it and say why, rather than leaving "Thinking…" forever.
|
|
3484
|
+
if (storedActiveRun?.threadId === threadId) {
|
|
3485
|
+
clearActiveRunIfMatches(threadId, storedActiveRun.runId);
|
|
3486
|
+
window.dispatchEvent(
|
|
3487
|
+
new CustomEvent("agent-chat:run-error", {
|
|
3488
|
+
detail: {
|
|
3489
|
+
message:
|
|
3490
|
+
"Couldn't reach the server to check whether the agent is still working. Send your message again to retry.",
|
|
3491
|
+
errorCode: "run_status_unavailable",
|
|
3492
|
+
recoverable: true,
|
|
3493
|
+
...(storedActiveRun.runId
|
|
3494
|
+
? { runId: storedActiveRun.runId }
|
|
3495
|
+
: {}),
|
|
3496
|
+
...(tabId ? { tabId } : {}),
|
|
3497
|
+
},
|
|
3498
|
+
}),
|
|
3499
|
+
);
|
|
3500
|
+
}
|
|
3501
|
+
return false;
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3443
3504
|
try {
|
|
3444
|
-
const storedActiveRun = getActiveRun();
|
|
3445
|
-
const runRes = await fetch(
|
|
3446
|
-
`${apiUrl}/runs/active?threadId=${encodeURIComponent(threadId)}`,
|
|
3447
|
-
);
|
|
3448
|
-
if (!runRes.ok) return false;
|
|
3449
3505
|
const runInfo = (await runRes.json()) as ActiveRunLookup;
|
|
3450
3506
|
if (
|
|
3451
3507
|
!runInfo.active ||
|
|
@@ -3464,7 +3520,7 @@ const AssistantChatInner = forwardRef<
|
|
|
3464
3520
|
} catch {
|
|
3465
3521
|
return false;
|
|
3466
3522
|
}
|
|
3467
|
-
}, [apiUrl, refreshThreadFromServer, startReconnectToRun, threadId]);
|
|
3523
|
+
}, [apiUrl, refreshThreadFromServer, startReconnectToRun, tabId, threadId]);
|
|
3468
3524
|
|
|
3469
3525
|
useEffect(() => {
|
|
3470
3526
|
if (!threadId || !isNewThread) return;
|
|
@@ -2,14 +2,23 @@
|
|
|
2
2
|
* Full-screen loading spinner rendered during SSR and initial hydration.
|
|
3
3
|
* Uses inline SVG + styles because Tailwind may not be loaded yet on the server.
|
|
4
4
|
* Respects the user's OS color scheme so dark-mode users don't get a white flash.
|
|
5
|
+
*
|
|
6
|
+
* The stall hint is revealed by a pure-CSS `animation-delay`, never a timer:
|
|
7
|
+
* the states that strand a user here (hydration never runs, the route module
|
|
8
|
+
* 404s, a cold dev-server compile) are exactly the states where no JS of ours
|
|
9
|
+
* executes, so a `setTimeout` fallback would never fire. A featureless spinner
|
|
10
|
+
* is indistinguishable from a blank screen, and reads as "the app is broken"
|
|
11
|
+
* rather than "look at the terminal" — that mis-read is what this text buys.
|
|
5
12
|
*/
|
|
6
13
|
export function DefaultSpinner() {
|
|
7
14
|
return (
|
|
8
15
|
<div
|
|
9
16
|
style={{
|
|
10
17
|
display: "flex",
|
|
18
|
+
flexDirection: "column",
|
|
11
19
|
alignItems: "center",
|
|
12
20
|
justifyContent: "center",
|
|
21
|
+
gap: 16,
|
|
13
22
|
height: "100vh",
|
|
14
23
|
width: "100%",
|
|
15
24
|
}}
|
|
@@ -29,8 +38,28 @@ export function DefaultSpinner() {
|
|
|
29
38
|
>
|
|
30
39
|
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
|
|
31
40
|
</svg>
|
|
41
|
+
<p className="an-stall-hint">
|
|
42
|
+
Still loading. A first run compiles dependencies and can take a minute —
|
|
43
|
+
if it does not finish, check the terminal running the dev server for
|
|
44
|
+
errors.
|
|
45
|
+
</p>
|
|
32
46
|
<style>{`
|
|
33
47
|
@keyframes an-spin { to { transform: rotate(360deg) } }
|
|
48
|
+
@keyframes an-stall-in { to { opacity: 0.6 } }
|
|
49
|
+
.an-stall-hint {
|
|
50
|
+
opacity: 0;
|
|
51
|
+
margin: 0;
|
|
52
|
+
max-width: 32rem;
|
|
53
|
+
padding: 0 1.5rem;
|
|
54
|
+
text-align: center;
|
|
55
|
+
font-size: 0.875rem;
|
|
56
|
+
line-height: 1.5;
|
|
57
|
+
font-family: ui-sans-serif, system-ui, sans-serif;
|
|
58
|
+
animation: an-stall-in 0.4s ease-out 10s forwards;
|
|
59
|
+
}
|
|
60
|
+
@media (prefers-reduced-motion: reduce) {
|
|
61
|
+
.an-stall-hint { animation-duration: 0s }
|
|
62
|
+
}
|
|
34
63
|
html {
|
|
35
64
|
background: hsl(var(--background, 0 0% 100%));
|
|
36
65
|
color: hsl(var(--foreground, 240 10% 3.9%));
|
|
@@ -56,6 +56,9 @@ import {
|
|
|
56
56
|
type ReactNode,
|
|
57
57
|
} from "react";
|
|
58
58
|
|
|
59
|
+
// Type-only: erased at build time, so declaring app roles pulls no server or
|
|
60
|
+
// database code into the browser bundle.
|
|
61
|
+
import type { AppRolesDescriptor } from "../../org/app-roles.js";
|
|
59
62
|
import type { DomainMatchOrg } from "../../org/types.js";
|
|
60
63
|
import {
|
|
61
64
|
Tooltip,
|
|
@@ -82,6 +85,8 @@ import {
|
|
|
82
85
|
useSetA2ASecret,
|
|
83
86
|
useSyncA2ASecret,
|
|
84
87
|
useJoinByDomain,
|
|
88
|
+
useAppRoles,
|
|
89
|
+
useSetAppMemberRole,
|
|
85
90
|
type InviteRole,
|
|
86
91
|
type SyncA2ASecretResult,
|
|
87
92
|
} from "./hooks.js";
|
|
@@ -127,6 +132,17 @@ export interface TeamPageProps {
|
|
|
127
132
|
* tweak page width.
|
|
128
133
|
*/
|
|
129
134
|
className?: string;
|
|
135
|
+
/**
|
|
136
|
+
* Opt in to an app-role column on the members table, using the same
|
|
137
|
+
* descriptor the app passes to `defineAppRoles`. Pass it explicitly rather
|
|
138
|
+
* than letting the page discover registered apps: a workspace can host
|
|
139
|
+
* several, and a members table that silently grows a column when some
|
|
140
|
+
* unrelated module registers itself is a surprise, not a feature.
|
|
141
|
+
*
|
|
142
|
+
* Only org owners/admins can change assignments; everyone else sees the
|
|
143
|
+
* column read-only.
|
|
144
|
+
*/
|
|
145
|
+
appRoles?: AppRolesDescriptor;
|
|
130
146
|
}
|
|
131
147
|
|
|
132
148
|
function RoleIcon({ role }: { role: string }) {
|
|
@@ -383,7 +399,7 @@ interface PendingInviteListItem {
|
|
|
383
399
|
role: string;
|
|
384
400
|
}
|
|
385
401
|
|
|
386
|
-
function MembersCard() {
|
|
402
|
+
function MembersCard({ appRoles }: { appRoles?: AppRolesDescriptor }) {
|
|
387
403
|
const t = useT();
|
|
388
404
|
const { data: org } = useOrg();
|
|
389
405
|
const { data: membersData, isLoading: isLoadingMembers } = useOrgMembers();
|
|
@@ -457,6 +473,7 @@ function MembersCard() {
|
|
|
457
473
|
isLoadingMembers={isLoadingMembers}
|
|
458
474
|
currentUserEmail={org.email}
|
|
459
475
|
currentUserRole={org.role ?? null}
|
|
476
|
+
appRoles={appRoles}
|
|
460
477
|
/>
|
|
461
478
|
|
|
462
479
|
{isOwner && <DangerZoneCard orgName={org.orgName ?? ""} />}
|
|
@@ -470,16 +487,26 @@ function MembersTableCard({
|
|
|
470
487
|
isLoadingMembers,
|
|
471
488
|
currentUserEmail,
|
|
472
489
|
currentUserRole,
|
|
490
|
+
appRoles,
|
|
473
491
|
}: {
|
|
474
492
|
members: MemberListItem[];
|
|
475
493
|
pendingInvites: PendingInviteListItem[];
|
|
476
494
|
isLoadingMembers: boolean;
|
|
477
495
|
currentUserEmail: string;
|
|
478
496
|
currentUserRole: string | null;
|
|
497
|
+
appRoles?: AppRolesDescriptor;
|
|
479
498
|
}) {
|
|
480
499
|
const t = useT();
|
|
481
500
|
const [showInviteForm, setShowInviteForm] = useState(false);
|
|
482
501
|
const canInvite = currentUserRole === "owner" || currentUserRole === "admin";
|
|
502
|
+
const { data: appRoleData } = useAppRoles(appRoles?.appId);
|
|
503
|
+
const appRoleByEmail = new Map(
|
|
504
|
+
(appRoleData?.assignments ?? []).map((a) => [
|
|
505
|
+
a.email.toLowerCase(),
|
|
506
|
+
a.role,
|
|
507
|
+
]),
|
|
508
|
+
);
|
|
509
|
+
const columnCount = appRoles ? 5 : 4;
|
|
483
510
|
|
|
484
511
|
return (
|
|
485
512
|
<section className="rounded-lg border border-border bg-card">
|
|
@@ -516,6 +543,9 @@ function MembersTableCard({
|
|
|
516
543
|
<TableRow>
|
|
517
544
|
<TableHead>{t("org.member")}</TableHead>
|
|
518
545
|
<TableHead>{t("org.role")}</TableHead>
|
|
546
|
+
{appRoles && (
|
|
547
|
+
<TableHead>{appRoles.label ?? appRoles.appId}</TableHead>
|
|
548
|
+
)}
|
|
519
549
|
<TableHead>{t("org.status")}</TableHead>
|
|
520
550
|
<TableHead className="text-end">{t("org.actions")}</TableHead>
|
|
521
551
|
</TableRow>
|
|
@@ -524,7 +554,7 @@ function MembersTableCard({
|
|
|
524
554
|
{isLoadingMembers && members.length === 0 ? (
|
|
525
555
|
[0, 1, 2].map((i) => (
|
|
526
556
|
<TableRow key={i}>
|
|
527
|
-
<TableCell colSpan={
|
|
557
|
+
<TableCell colSpan={columnCount}>
|
|
528
558
|
<div
|
|
529
559
|
className="h-3.5 rounded bg-muted animate-pulse"
|
|
530
560
|
style={{ width: `${180 + i * 48}px` }}
|
|
@@ -535,7 +565,7 @@ function MembersTableCard({
|
|
|
535
565
|
) : members.length === 0 && pendingInvites.length === 0 ? (
|
|
536
566
|
<TableRow>
|
|
537
567
|
<TableCell
|
|
538
|
-
colSpan={
|
|
568
|
+
colSpan={columnCount}
|
|
539
569
|
className="py-8 text-center text-sm text-muted-foreground"
|
|
540
570
|
>
|
|
541
571
|
{t("org.noMembers")}
|
|
@@ -550,10 +580,17 @@ function MembersTableCard({
|
|
|
550
580
|
role={m.role}
|
|
551
581
|
isCurrentUser={m.email === currentUserEmail}
|
|
552
582
|
currentUserRole={currentUserRole}
|
|
583
|
+
appRoles={appRoles}
|
|
584
|
+
appRole={appRoleByEmail.get(m.email.toLowerCase()) ?? null}
|
|
585
|
+
canManageAppRoles={Boolean(appRoleData?.canManage)}
|
|
553
586
|
/>
|
|
554
587
|
))}
|
|
555
588
|
{pendingInvites.map((inv) => (
|
|
556
|
-
<PendingInviteRow
|
|
589
|
+
<PendingInviteRow
|
|
590
|
+
key={inv.id}
|
|
591
|
+
invite={inv}
|
|
592
|
+
hasAppRoleColumn={Boolean(appRoles)}
|
|
593
|
+
/>
|
|
557
594
|
))}
|
|
558
595
|
</>
|
|
559
596
|
)}
|
|
@@ -665,7 +702,13 @@ function RoleBadge({ role }: { role: string }) {
|
|
|
665
702
|
);
|
|
666
703
|
}
|
|
667
704
|
|
|
668
|
-
function PendingInviteRow({
|
|
705
|
+
function PendingInviteRow({
|
|
706
|
+
invite,
|
|
707
|
+
hasAppRoleColumn,
|
|
708
|
+
}: {
|
|
709
|
+
invite: PendingInviteListItem;
|
|
710
|
+
hasAppRoleColumn?: boolean;
|
|
711
|
+
}) {
|
|
669
712
|
const t = useT();
|
|
670
713
|
return (
|
|
671
714
|
<TableRow className="opacity-70">
|
|
@@ -675,6 +718,13 @@ function PendingInviteRow({ invite }: { invite: PendingInviteListItem }) {
|
|
|
675
718
|
<TableCell>
|
|
676
719
|
<RoleBadge role={invite.role} />
|
|
677
720
|
</TableCell>
|
|
721
|
+
{/* App roles hang off membership, so there is nothing to assign until the
|
|
722
|
+
invitation is accepted. */}
|
|
723
|
+
{hasAppRoleColumn && (
|
|
724
|
+
<TableCell>
|
|
725
|
+
<span className="text-muted-foreground">-</span>
|
|
726
|
+
</TableCell>
|
|
727
|
+
)}
|
|
678
728
|
<TableCell>
|
|
679
729
|
<span className="rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground">
|
|
680
730
|
{t("org.invited")}
|
|
@@ -685,16 +735,112 @@ function PendingInviteRow({ invite }: { invite: PendingInviteListItem }) {
|
|
|
685
735
|
);
|
|
686
736
|
}
|
|
687
737
|
|
|
738
|
+
/** Sentinel for "clear the assignment" — Select cannot carry an empty value. */
|
|
739
|
+
const UNASSIGNED = "__unassigned__";
|
|
740
|
+
|
|
741
|
+
function AppRoleCell({
|
|
742
|
+
email,
|
|
743
|
+
appRoles,
|
|
744
|
+
appRole,
|
|
745
|
+
canManage,
|
|
746
|
+
}: {
|
|
747
|
+
email: string;
|
|
748
|
+
appRoles: AppRolesDescriptor;
|
|
749
|
+
appRole: string | null;
|
|
750
|
+
canManage: boolean;
|
|
751
|
+
}) {
|
|
752
|
+
const [editing, setEditing] = useState(false);
|
|
753
|
+
const setAppRole = useSetAppMemberRole(appRoles.appId);
|
|
754
|
+
const labelFor = (r: string) => appRoles.roleLabels?.[r] ?? r;
|
|
755
|
+
|
|
756
|
+
if (editing) {
|
|
757
|
+
return (
|
|
758
|
+
<TableCell>
|
|
759
|
+
<Select
|
|
760
|
+
defaultOpen
|
|
761
|
+
value={appRole ?? UNASSIGNED}
|
|
762
|
+
onOpenChange={(open) => {
|
|
763
|
+
if (!open) setEditing(false);
|
|
764
|
+
}}
|
|
765
|
+
onValueChange={(value) => {
|
|
766
|
+
const next = value === UNASSIGNED ? null : value;
|
|
767
|
+
if (next === appRole) {
|
|
768
|
+
setEditing(false);
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
setAppRole.mutate(
|
|
772
|
+
{ email, role: next },
|
|
773
|
+
{ onSuccess: () => setEditing(false) },
|
|
774
|
+
);
|
|
775
|
+
}}
|
|
776
|
+
disabled={setAppRole.isPending}
|
|
777
|
+
>
|
|
778
|
+
<SelectTrigger
|
|
779
|
+
autoFocus
|
|
780
|
+
className="h-auto w-auto rounded-md border border-border bg-background px-1.5 py-0.5 text-[11px]"
|
|
781
|
+
>
|
|
782
|
+
<SelectValue />
|
|
783
|
+
</SelectTrigger>
|
|
784
|
+
<SelectContent>
|
|
785
|
+
<SelectItem value={UNASSIGNED}>
|
|
786
|
+
{appRoles.defaultRole ? labelFor(appRoles.defaultRole) : "—"}
|
|
787
|
+
</SelectItem>
|
|
788
|
+
{appRoles.roles.map((r) => (
|
|
789
|
+
<SelectItem key={r} value={r}>
|
|
790
|
+
{labelFor(r)}
|
|
791
|
+
</SelectItem>
|
|
792
|
+
))}
|
|
793
|
+
</SelectContent>
|
|
794
|
+
</Select>
|
|
795
|
+
</TableCell>
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
// An unassigned member shows the app's default only as a hint. The default
|
|
800
|
+
// never satisfies a server guard, so it must not read as a granted role.
|
|
801
|
+
const display = appRole ? (
|
|
802
|
+
<span className="inline-flex items-center rounded border border-border px-2 py-1 text-xs text-muted-foreground">
|
|
803
|
+
{labelFor(appRole)}
|
|
804
|
+
</span>
|
|
805
|
+
) : (
|
|
806
|
+
<span className="text-xs text-muted-foreground/70">
|
|
807
|
+
{appRoles.defaultRole ? labelFor(appRoles.defaultRole) : "—"}
|
|
808
|
+
</span>
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
return (
|
|
812
|
+
<TableCell>
|
|
813
|
+
{canManage ? (
|
|
814
|
+
<Button
|
|
815
|
+
type="button"
|
|
816
|
+
onClick={() => setEditing(true)}
|
|
817
|
+
className="cursor-pointer rounded hover:opacity-80"
|
|
818
|
+
>
|
|
819
|
+
{display}
|
|
820
|
+
</Button>
|
|
821
|
+
) : (
|
|
822
|
+
display
|
|
823
|
+
)}
|
|
824
|
+
</TableCell>
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
|
|
688
828
|
function MemberRow({
|
|
689
829
|
email,
|
|
690
830
|
role,
|
|
691
831
|
isCurrentUser,
|
|
692
832
|
currentUserRole,
|
|
833
|
+
appRoles,
|
|
834
|
+
appRole,
|
|
835
|
+
canManageAppRoles,
|
|
693
836
|
}: {
|
|
694
837
|
email: string;
|
|
695
838
|
role: string;
|
|
696
839
|
isCurrentUser: boolean;
|
|
697
840
|
currentUserRole: string | null;
|
|
841
|
+
appRoles?: AppRolesDescriptor;
|
|
842
|
+
appRole?: string | null;
|
|
843
|
+
canManageAppRoles?: boolean;
|
|
698
844
|
}) {
|
|
699
845
|
const t = useT();
|
|
700
846
|
const removeMember = useRemoveMember();
|
|
@@ -719,6 +865,14 @@ function MemberRow({
|
|
|
719
865
|
<TableCell>
|
|
720
866
|
<RoleBadge role={role} />
|
|
721
867
|
</TableCell>
|
|
868
|
+
{appRoles && (
|
|
869
|
+
<AppRoleCell
|
|
870
|
+
email={email}
|
|
871
|
+
appRoles={appRoles}
|
|
872
|
+
appRole={appRole ?? null}
|
|
873
|
+
canManage={Boolean(canManageAppRoles)}
|
|
874
|
+
/>
|
|
875
|
+
)}
|
|
722
876
|
<TableCell>
|
|
723
877
|
{isCurrentUser ? (
|
|
724
878
|
<span className="rounded border border-border px-1.5 py-0.5 text-[10px] text-muted-foreground">
|
|
@@ -1620,6 +1774,7 @@ export function TeamPage({
|
|
|
1620
1774
|
showTitle = true,
|
|
1621
1775
|
createOrgDescription,
|
|
1622
1776
|
className,
|
|
1777
|
+
appRoles,
|
|
1623
1778
|
}: TeamPageProps) {
|
|
1624
1779
|
const t = useT();
|
|
1625
1780
|
const { data: org, isLoading } = useOrg();
|
|
@@ -1652,7 +1807,7 @@ export function TeamPage({
|
|
|
1652
1807
|
{!org?.orgId ? (
|
|
1653
1808
|
<CreateOrgCard description={createOrgDescription} />
|
|
1654
1809
|
) : (
|
|
1655
|
-
<MembersCard />
|
|
1810
|
+
<MembersCard appRoles={appRoles} />
|
|
1656
1811
|
)}
|
|
1657
1812
|
</>
|
|
1658
1813
|
)}
|
package/src/client/org/hooks.ts
CHANGED
|
@@ -318,6 +318,73 @@ export function useSetOrgDomain() {
|
|
|
318
318
|
});
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
export interface AppRoleAssignment {
|
|
322
|
+
email: string;
|
|
323
|
+
role: string;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export interface AppRolesInfo {
|
|
327
|
+
appId: string;
|
|
328
|
+
roles: string[];
|
|
329
|
+
/** Shown for members with no assignment. Never satisfies a server guard. */
|
|
330
|
+
defaultRole: string | null;
|
|
331
|
+
roleLabels: Record<string, string>;
|
|
332
|
+
/** Whether the current user may change assignments (org owner/admin). */
|
|
333
|
+
canManage: boolean;
|
|
334
|
+
assignments: AppRoleAssignment[];
|
|
335
|
+
myRole: string | null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* App-role vocabulary and assignments for the active org.
|
|
340
|
+
*
|
|
341
|
+
* `myRole` and `canManage` are progressive disclosure only — every guarded
|
|
342
|
+
* operation re-resolves both server-side, so a client that shows the wrong
|
|
343
|
+
* affordance still cannot perform the operation.
|
|
344
|
+
*/
|
|
345
|
+
export function useAppRoles(appId: string | undefined) {
|
|
346
|
+
const { data: org } = useOrg();
|
|
347
|
+
return useQuery<AppRolesInfo>({
|
|
348
|
+
queryKey: ["org-app-roles", appId ?? null, org?.orgId ?? null],
|
|
349
|
+
queryFn: () =>
|
|
350
|
+
apiFetch(`${ORG_BASE}/app-roles?appId=${encodeURIComponent(appId!)}`),
|
|
351
|
+
enabled: Boolean(appId),
|
|
352
|
+
staleTime: 30_000,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** The current user's role in one app, or `null` when unassigned. */
|
|
357
|
+
export function useAppRole(appId: string | undefined): {
|
|
358
|
+
role: string | null;
|
|
359
|
+
isLoading: boolean;
|
|
360
|
+
error: Error | null;
|
|
361
|
+
} {
|
|
362
|
+
const query = useAppRoles(appId);
|
|
363
|
+
return {
|
|
364
|
+
role: query.data?.myRole ?? null,
|
|
365
|
+
isLoading: query.isLoading,
|
|
366
|
+
error: query.error,
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export function useSetAppMemberRole(appId: string) {
|
|
371
|
+
const qc = useQueryClient();
|
|
372
|
+
return useMutation<
|
|
373
|
+
{ appId: string; email: string; role: string | null },
|
|
374
|
+
Error,
|
|
375
|
+
{ email: string; role: string | null }
|
|
376
|
+
>({
|
|
377
|
+
mutationFn: ({ email, role }) =>
|
|
378
|
+
apiFetch(`${ORG_BASE}/app-roles/${encodeURIComponent(email)}`, {
|
|
379
|
+
method: "PUT",
|
|
380
|
+
body: JSON.stringify({ appId, role }),
|
|
381
|
+
}),
|
|
382
|
+
onSuccess: async () => {
|
|
383
|
+
await qc.invalidateQueries({ queryKey: ["org-app-roles"] });
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
321
388
|
/**
|
|
322
389
|
* Fetch the org's A2A secret on demand (owner/admin). Deliberately a separate
|
|
323
390
|
* request from `useOrg()` so the secret only reaches the browser when the
|
package/src/client/org/index.ts
CHANGED
|
@@ -19,6 +19,9 @@ export {
|
|
|
19
19
|
useSetA2ASecret,
|
|
20
20
|
useSyncA2ASecret,
|
|
21
21
|
useOrgRole,
|
|
22
|
+
useAppRoles,
|
|
23
|
+
useAppRole,
|
|
24
|
+
useSetAppMemberRole,
|
|
22
25
|
} from "./hooks.js";
|
|
23
26
|
|
|
24
27
|
export type {
|
|
@@ -27,8 +30,14 @@ export type {
|
|
|
27
30
|
BulkInviteResult,
|
|
28
31
|
SyncA2ASecretResult,
|
|
29
32
|
UseOrgRoleResult,
|
|
33
|
+
AppRoleAssignment,
|
|
34
|
+
AppRolesInfo,
|
|
30
35
|
} from "./hooks.js";
|
|
31
36
|
|
|
37
|
+
// Type-only re-export so templates can annotate the `appRoles` prop without
|
|
38
|
+
// importing the server module.
|
|
39
|
+
export type { AppRolesDescriptor } from "../../org/app-roles.js";
|
|
40
|
+
|
|
32
41
|
export { OrgSwitcher, type OrgSwitcherProps } from "./OrgSwitcher.js";
|
|
33
42
|
export {
|
|
34
43
|
InvitationBanner,
|
|
@@ -15,6 +15,8 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
15
15
|
|
|
16
16
|
const NETWORK_RESTART_BASE_MS = 1_000;
|
|
17
17
|
const NETWORK_RESTART_MAX_MS = 30_000;
|
|
18
|
+
const RESTART_RETRY_BASE_MS = 400;
|
|
19
|
+
const MAX_RESTART_ATTEMPTS = 8;
|
|
18
20
|
|
|
19
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
22
|
function getSpeechRecognitionCtor(): any {
|
|
@@ -42,6 +44,14 @@ export interface LiveTranscriptionApi {
|
|
|
42
44
|
stopAndWait: (timeoutMs?: number) => Promise<string>;
|
|
43
45
|
pause: () => void;
|
|
44
46
|
resume: () => void;
|
|
47
|
+
/**
|
|
48
|
+
* Non-null when recognition died before the caller asked it to stop, so the
|
|
49
|
+
* returned transcript covers only part of the session. Callers must pass it
|
|
50
|
+
* along rather than storing a partial transcript as a finished one. Read
|
|
51
|
+
* through a getter (not state) so a stop handler can never observe a stale
|
|
52
|
+
* "everything was fine" value from an earlier render.
|
|
53
|
+
*/
|
|
54
|
+
getIncompleteReason: () => string | null;
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
export function useLiveTranscription(
|
|
@@ -64,6 +74,20 @@ export function useLiveTranscription(
|
|
|
64
74
|
const restartTimerRef = useRef<number | null>(null);
|
|
65
75
|
const networkErrorCountRef = useRef(0);
|
|
66
76
|
const restartDelayRef = useRef(0);
|
|
77
|
+
const restartFailureCountRef = useRef(0);
|
|
78
|
+
const incompleteReasonRef = useRef<string | null>(null);
|
|
79
|
+
|
|
80
|
+
// First reason wins: it names what actually broke the capture, and later
|
|
81
|
+
// teardown noise must not overwrite it.
|
|
82
|
+
const markIncomplete = useCallback((reason: string) => {
|
|
83
|
+
if (incompleteReasonRef.current) return;
|
|
84
|
+
incompleteReasonRef.current = reason;
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
const getIncompleteReason = useCallback(
|
|
88
|
+
() => incompleteReasonRef.current,
|
|
89
|
+
[],
|
|
90
|
+
);
|
|
67
91
|
|
|
68
92
|
const currentTranscript = useCallback(
|
|
69
93
|
() =>
|
|
@@ -102,6 +126,8 @@ export function useLiveTranscription(
|
|
|
102
126
|
stoppedManuallyRef.current = false;
|
|
103
127
|
networkErrorCountRef.current = 0;
|
|
104
128
|
restartDelayRef.current = 0;
|
|
129
|
+
restartFailureCountRef.current = 0;
|
|
130
|
+
incompleteReasonRef.current = null;
|
|
105
131
|
setTranscript("");
|
|
106
132
|
setInterimText("");
|
|
107
133
|
|
|
@@ -122,6 +148,7 @@ export function useLiveTranscription(
|
|
|
122
148
|
if (event.results.length > 0) {
|
|
123
149
|
networkErrorCountRef.current = 0;
|
|
124
150
|
restartDelayRef.current = 0;
|
|
151
|
+
restartFailureCountRef.current = 0;
|
|
125
152
|
}
|
|
126
153
|
};
|
|
127
154
|
|
|
@@ -133,6 +160,9 @@ export function useLiveTranscription(
|
|
|
133
160
|
event.error === "audio-capture"
|
|
134
161
|
) {
|
|
135
162
|
stoppedManuallyRef.current = true;
|
|
163
|
+
markIncomplete(
|
|
164
|
+
`Browser speech recognition stopped mid-recording (${event.error}).`,
|
|
165
|
+
);
|
|
136
166
|
setIsActive(false);
|
|
137
167
|
return;
|
|
138
168
|
}
|
|
@@ -163,17 +193,34 @@ export function useLiveTranscription(
|
|
|
163
193
|
}
|
|
164
194
|
try {
|
|
165
195
|
recognition.start();
|
|
196
|
+
restartFailureCountRef.current = 0;
|
|
166
197
|
} catch {
|
|
167
|
-
|
|
198
|
+
// Chrome throws InvalidStateError while the previous session is
|
|
199
|
+
// still releasing. `onend` has already fired and will not fire
|
|
200
|
+
// again, so this timer is the only thing left that can revive
|
|
201
|
+
// recognition — giving up here freezes the transcript mid-recording
|
|
202
|
+
// and the partial text then looks like the whole recording.
|
|
203
|
+
const attempt = ++restartFailureCountRef.current;
|
|
204
|
+
if (attempt >= MAX_RESTART_ATTEMPTS) {
|
|
205
|
+
markIncomplete(
|
|
206
|
+
"Browser speech recognition stopped mid-recording and could not be restarted.",
|
|
207
|
+
);
|
|
208
|
+
setIsActive(false);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
restartTimerRef.current = window.setTimeout(
|
|
212
|
+
restart,
|
|
213
|
+
RESTART_RETRY_BASE_MS * attempt,
|
|
214
|
+
);
|
|
168
215
|
}
|
|
169
216
|
};
|
|
170
|
-
|
|
217
|
+
// Never call start() synchronously from inside onend — Chrome still
|
|
218
|
+
// has the session marked as running during dispatch and throws.
|
|
219
|
+
restartTimerRef.current = window.setTimeout(
|
|
220
|
+
restart,
|
|
221
|
+
restartDelayRef.current,
|
|
222
|
+
);
|
|
171
223
|
restartDelayRef.current = 0;
|
|
172
|
-
if (delayMs > 0) {
|
|
173
|
-
restartTimerRef.current = window.setTimeout(restart, delayMs);
|
|
174
|
-
} else {
|
|
175
|
-
restart();
|
|
176
|
-
}
|
|
177
224
|
return;
|
|
178
225
|
}
|
|
179
226
|
setIsActive(false);
|
|
@@ -185,10 +232,14 @@ export function useLiveTranscription(
|
|
|
185
232
|
try {
|
|
186
233
|
recognition.start();
|
|
187
234
|
setIsActive(true);
|
|
188
|
-
} catch {
|
|
189
|
-
|
|
235
|
+
} catch (err) {
|
|
236
|
+
// Browser may block without user gesture. Nothing was captured, and the
|
|
237
|
+
// caller must not treat the resulting empty text as "no speech".
|
|
238
|
+
markIncomplete(
|
|
239
|
+
`Browser speech recognition could not start (${(err as Error)?.message || "unknown error"}).`,
|
|
240
|
+
);
|
|
190
241
|
}
|
|
191
|
-
}, [currentTranscript, lang]);
|
|
242
|
+
}, [currentTranscript, lang, markIncomplete]);
|
|
192
243
|
|
|
193
244
|
const stop = useCallback((): string => {
|
|
194
245
|
const text = currentTranscript();
|
|
@@ -307,5 +358,6 @@ export function useLiveTranscription(
|
|
|
307
358
|
stopAndWait,
|
|
308
359
|
pause,
|
|
309
360
|
resume,
|
|
361
|
+
getIncompleteReason,
|
|
310
362
|
};
|
|
311
363
|
}
|