@agent-native/core 0.128.4 → 0.129.1
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 +90 -0
- package/corpus/core/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
- package/corpus/core/package.json +6 -3
- package/corpus/core/src/a2a/client.ts +18 -4
- package/corpus/core/src/a2a/server.ts +19 -3
- package/corpus/core/src/a2a/types.ts +7 -0
- package/corpus/core/src/action.ts +43 -16
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +14 -10
- package/corpus/core/src/agent/production-agent.ts +41 -3
- package/corpus/core/src/agent/thread-data-builder.ts +5 -0
- package/corpus/core/src/chat-threads/migrations.ts +53 -0
- package/corpus/core/src/cli/create.ts +4 -0
- package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
- package/corpus/core/src/cli/skills.ts +16 -0
- package/corpus/core/src/client/AgentPanel.tsx +9 -5
- package/corpus/core/src/client/AssistantChat.tsx +138 -46
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +16 -10
- package/corpus/core/src/client/agent-chat-adapter.ts +21 -3
- package/corpus/core/src/client/analytics.ts +159 -102
- package/corpus/core/src/client/chat/message-components.tsx +12 -5
- package/corpus/core/src/client/chat/runtime.ts +2 -1
- package/corpus/core/src/client/chat/tool-call-display.tsx +25 -2
- package/corpus/core/src/client/chat-model-groups.ts +3 -0
- package/corpus/core/src/client/client-status-requests.ts +161 -0
- package/corpus/core/src/client/org/OrgSwitcher.tsx +4 -4
- package/corpus/core/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
- package/corpus/core/src/client/sse-event-processor.ts +15 -3
- package/corpus/core/src/client/use-agent-engine-configured.ts +54 -50
- package/corpus/core/src/client/use-chat-models.ts +16 -11
- package/corpus/core/src/client/use-chat-threads.ts +32 -9
- package/corpus/core/src/client/use-session.ts +4 -5
- package/corpus/core/src/db/client.ts +83 -8
- package/corpus/core/src/db/ensure-additive-columns.ts +31 -20
- package/corpus/core/src/db/migrations.ts +22 -2
- package/corpus/core/src/mcp/approval-store.ts +99 -0
- package/corpus/core/src/mcp/build-server.ts +579 -303
- package/corpus/core/src/mcp/embed-app.ts +1 -1
- package/corpus/core/src/mcp/oauth-client-metadata.ts +380 -0
- package/corpus/core/src/mcp/oauth-route.ts +57 -52
- package/corpus/core/src/mcp/screen-memory-stdio.ts +400 -378
- package/corpus/core/src/mcp/server.ts +29 -145
- package/corpus/core/src/mcp/stdio.ts +72 -80
- package/corpus/core/src/mcp-client/manager.ts +6 -10
- package/corpus/core/src/mcp-client/oauth-client.ts +127 -36
- package/corpus/core/src/mcp-client/oauth-routes.ts +16 -6
- package/corpus/core/src/mcp-client/routes.ts +6 -5
- package/corpus/core/src/server/agent-capabilities.ts +20 -0
- package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +27 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +18 -0
- package/corpus/core/src/server/transcribe-voice.ts +14 -5
- package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +13 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
- package/corpus/core/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/analytics/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/analytics/app/components/dashboard/DashboardPanelSkeleton.tsx +1 -10
- package/corpus/templates/analytics/app/global.css +0 -31
- package/corpus/templates/analytics/app/lib/sql-query.ts +46 -20
- package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboard-loading-states-now-use-a-simpler-standar.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-28-analytics-dashboards-now-load-first-party-charts-reliably-in.md +6 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-cache.ts +42 -16
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +4 -2
- package/corpus/templates/analytics/shared/sql-query-limits.ts +1 -0
- package/corpus/templates/assets/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/brain/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/calendar/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/chat/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/chat/README.md +0 -2
- package/corpus/templates/clips/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/clips/app/components/player/player-controls.tsx +3 -6
- package/corpus/templates/clips/app/components/player/recording-views-badge.tsx +6 -2
- package/corpus/templates/clips/app/components/sharing/share-ui.tsx +25 -10
- package/corpus/templates/clips/app/components/sharing/viewed-by-popover.tsx +28 -6
- package/corpus/templates/clips/app/lib/use-visible-avatar-url.ts +34 -0
- package/corpus/templates/clips/app/routes/r.$recordingId.tsx +6 -2
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +3 -1
- package/corpus/templates/clips/changelog/2026-07-28-copied-share-links-no-longer-leave-an-unnecessary-confirmati.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-playback-skip-controls-no-longer-show-overlapping-numbers.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-profile-avatars-now-appear-in-viewer-and-share-access-lists.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-28-public-clips-share-buttons-now-use-a-clean-text-only-label.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +14 -17
- package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +3 -1
- package/corpus/templates/clips/desktop/src/styles.css +13 -10
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +2 -2
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +36 -0
- package/corpus/templates/content/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +155 -34
- package/corpus/templates/content/actions/_builder-docs-client.ts +151 -30
- package/corpus/templates/content/actions/stage-builder-source-bulk-update.ts +5 -2
- package/corpus/templates/content/app/root.tsx +35 -14
- package/corpus/templates/content/changelog/2026-07-28-builder-backed-files-media-fields-now-accept-direct-file-url.md +6 -0
- package/corpus/templates/content/changelog/2026-07-28-builder-content-reads-now-negotiate-the-newest-mcp-protocol-.md +6 -0
- package/corpus/templates/content/changelog/2026-07-28-public-content-pages-load-faster-before-the-optional-ask-sid.md +6 -0
- package/corpus/templates/design/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +36 -16
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +42 -3
- package/corpus/templates/design/actions/open-visual-edit.ts +2 -1
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +14 -15
- package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +314 -0
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +88 -3
- package/corpus/templates/design/app/components/design/keyboard-shortcuts.ts +1 -1
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +7 -6
- package/corpus/templates/design/app/i18n-data.ts +56 -11
- package/corpus/templates/design/app/i18n-responsive-interact.ts +183 -0
- package/corpus/templates/design/app/lib/agent-chat.ts +60 -0
- package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +89 -0
- package/corpus/templates/design/changelog/2026-07-28-design-edits-can-be-applied-through-the-host-coding-agent-fr.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +6 -0
- package/corpus/templates/design/changelog/2026-07-28-show-or-hide-design-editing-chrome-with-cmd-on-apple-devices.md +2 -2
- package/corpus/templates/design/changelog/2026-07-28-visual-edit-now-always-shows-the-live-running-app-in-the-can.md +6 -0
- package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/forms/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/macros/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/mail/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/plan/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/slides/.agents/skills/security/SKILL.md +13 -2
- package/corpus/templates/tasks/.agents/skills/security/SKILL.md +13 -2
- package/corpus/toolkit/CHANGELOG.md +13 -0
- package/corpus/toolkit/package.json +1 -1
- package/corpus/toolkit/src/chat-history.css +1 -2
- package/corpus/toolkit/src/composer/TiptapComposer.tsx +45 -0
- package/dist/a2a/client.d.ts +7 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +8 -3
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/server.d.ts.map +1 -1
- package/dist/a2a/server.js +18 -3
- package/dist/a2a/server.js.map +1 -1
- package/dist/a2a/types.d.ts +7 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/action.d.ts +15 -1
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +39 -16
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +14 -10
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +35 -7
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +2 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +3 -0
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/chat-threads/migrations.d.ts +21 -0
- package/dist/chat-threads/migrations.d.ts.map +1 -0
- package/dist/chat-threads/migrations.js +45 -0
- package/dist/chat-threads/migrations.js.map +1 -0
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +4 -0
- package/dist/cli/create.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 +36 -16
- package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +15 -0
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +6 -5
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +37 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +41 -8
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +11 -10
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +19 -3
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +150 -101
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/chat/message-components.d.ts +2 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +11 -4
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/chat/runtime.d.ts.map +1 -1
- package/dist/client/chat/runtime.js +2 -1
- package/dist/client/chat/runtime.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +4 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +14 -8
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/chat-model-groups.d.ts.map +1 -1
- package/dist/client/chat-model-groups.js +3 -0
- package/dist/client/chat-model-groups.js.map +1 -1
- package/dist/client/client-status-requests.d.ts +14 -0
- package/dist/client/client-status-requests.d.ts.map +1 -0
- package/dist/client/client-status-requests.js +126 -0
- package/dist/client/client-status-requests.js.map +1 -0
- package/dist/client/org/OrgSwitcher.js +2 -2
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js +1 -1
- package/dist/client/settings/VoiceTranscriptionSection.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +7 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +8 -3
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
- package/dist/client/use-agent-engine-configured.js +29 -43
- package/dist/client/use-agent-engine-configured.js.map +1 -1
- package/dist/client/use-chat-models.d.ts.map +1 -1
- package/dist/client/use-chat-models.js +11 -11
- package/dist/client/use-chat-models.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +23 -7
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/use-session.d.ts.map +1 -1
- package/dist/client/use-session.js +4 -4
- package/dist/client/use-session.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +48 -4
- 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 +32 -21
- package/dist/db/ensure-additive-columns.js.map +1 -1
- package/dist/db/migrations.d.ts +9 -0
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +14 -3
- package/dist/db/migrations.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/mcp/approval-store.d.ts +16 -0
- package/dist/mcp/approval-store.d.ts.map +1 -0
- package/dist/mcp/approval-store.js +85 -0
- package/dist/mcp/approval-store.js.map +1 -0
- package/dist/mcp/build-server.d.ts +1 -34
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +220 -23
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.js +1 -1
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/oauth-client-metadata.d.ts +15 -0
- package/dist/mcp/oauth-client-metadata.d.ts.map +1 -0
- package/dist/mcp/oauth-client-metadata.js +297 -0
- package/dist/mcp/oauth-client-metadata.js.map +1 -0
- package/dist/mcp/oauth-route.d.ts.map +1 -1
- package/dist/mcp/oauth-route.js +39 -53
- package/dist/mcp/oauth-route.js.map +1 -1
- package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
- package/dist/mcp/screen-memory-stdio.js +304 -296
- package/dist/mcp/screen-memory-stdio.js.map +1 -1
- package/dist/mcp/server.d.ts +12 -25
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +24 -133
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/stdio.d.ts.map +1 -1
- package/dist/mcp/stdio.js +36 -35
- package/dist/mcp/stdio.js.map +1 -1
- package/dist/mcp-client/manager.d.ts.map +1 -1
- package/dist/mcp-client/manager.js +6 -6
- package/dist/mcp-client/manager.js.map +1 -1
- package/dist/mcp-client/oauth-client.d.ts +13 -17
- package/dist/mcp-client/oauth-client.d.ts.map +1 -1
- package/dist/mcp-client/oauth-client.js +74 -16
- package/dist/mcp-client/oauth-client.js.map +1 -1
- package/dist/mcp-client/oauth-routes.d.ts +2 -2
- package/dist/mcp-client/oauth-routes.d.ts.map +1 -1
- package/dist/mcp-client/oauth-routes.js +16 -6
- package/dist/mcp-client/oauth-routes.js.map +1 -1
- package/dist/mcp-client/routes.js +5 -5
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/observability/routes.d.ts +3 -3
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
- package/dist/provider-api/actions/provider-api.d.ts +8 -8
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/agent-capabilities.d.ts.map +1 -1
- package/dist/server/agent-capabilities.js +21 -0
- package/dist/server/agent-capabilities.js.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +14 -0
- package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +16 -0
- package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +11 -1
- 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/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/server/transcribe-voice.js +12 -6
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/templates/chat/.agents/skills/security/SKILL.md +13 -2
- package/dist/templates/chat/README.md +0 -2
- package/dist/templates/default/.agents/skills/security/SKILL.md +13 -2
- package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
- package/dist/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
- package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
- package/docs/content/template-design-collaboration-and-full-apps.mdx +3 -1
- package/package.json +7 -4
- package/src/a2a/client.ts +18 -4
- package/src/a2a/server.ts +19 -3
- package/src/a2a/types.ts +7 -0
- package/src/action.ts +43 -16
- package/src/agent/engine/ai-sdk-engine.ts +14 -10
- package/src/agent/production-agent.ts +41 -3
- package/src/agent/thread-data-builder.ts +5 -0
- package/src/chat-threads/migrations.ts +53 -0
- package/src/cli/create.ts +4 -0
- package/src/cli/skills-content/design-visual-edit-skill.ts +36 -16
- package/src/cli/skills.ts +16 -0
- package/src/client/AgentPanel.tsx +9 -5
- package/src/client/AssistantChat.tsx +138 -46
- package/src/client/MultiTabAssistantChat.tsx +16 -10
- package/src/client/agent-chat-adapter.ts +21 -3
- package/src/client/analytics.ts +159 -102
- package/src/client/chat/message-components.tsx +12 -5
- package/src/client/chat/runtime.ts +2 -1
- package/src/client/chat/tool-call-display.tsx +25 -2
- package/src/client/chat-model-groups.ts +3 -0
- package/src/client/client-status-requests.ts +161 -0
- package/src/client/org/OrgSwitcher.tsx +4 -4
- package/src/client/settings/VoiceTranscriptionSection.tsx +2 -2
- package/src/client/sse-event-processor.ts +15 -3
- package/src/client/use-agent-engine-configured.ts +54 -50
- package/src/client/use-chat-models.ts +16 -11
- package/src/client/use-chat-threads.ts +32 -9
- package/src/client/use-session.ts +4 -5
- package/src/db/client.ts +83 -8
- package/src/db/ensure-additive-columns.ts +31 -20
- package/src/db/migrations.ts +22 -2
- package/src/mcp/approval-store.ts +99 -0
- package/src/mcp/build-server.ts +579 -303
- package/src/mcp/embed-app.ts +1 -1
- package/src/mcp/oauth-client-metadata.ts +380 -0
- package/src/mcp/oauth-route.ts +57 -52
- package/src/mcp/screen-memory-stdio.ts +400 -378
- package/src/mcp/server.ts +29 -145
- package/src/mcp/stdio.ts +72 -80
- package/src/mcp-client/manager.ts +6 -10
- package/src/mcp-client/oauth-client.ts +127 -36
- package/src/mcp-client/oauth-routes.ts +16 -6
- package/src/mcp-client/routes.ts +6 -5
- package/src/server/agent-capabilities.ts +20 -0
- package/src/server/agent-chat/action-filters-a2a.ts +27 -0
- package/src/server/agent-chat-plugin.ts +18 -0
- package/src/server/transcribe-voice.ts +14 -5
- package/src/templates/chat/.agents/skills/security/SKILL.md +13 -2
- package/src/templates/chat/README.md +0 -2
- package/src/templates/default/.agents/skills/security/SKILL.md +13 -2
- package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +5 -2
- package/src/templates/workspace-core/.agents/skills/external-agents/SKILL.md +18 -0
- package/src/templates/workspace-core/.agents/skills/security/SKILL.md +13 -2
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
import type { ComponentProps } from "react";
|
|
2
2
|
|
|
3
3
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
4
|
-
import { cn } from "@/lib/utils";
|
|
5
4
|
|
|
6
5
|
export function DashboardPanelSkeleton({
|
|
7
6
|
className,
|
|
8
7
|
...props
|
|
9
8
|
}: ComponentProps<typeof Skeleton>) {
|
|
10
|
-
return
|
|
11
|
-
<Skeleton
|
|
12
|
-
{...props}
|
|
13
|
-
className={cn(
|
|
14
|
-
"dashboard-panel-skeleton bg-muted-foreground/14",
|
|
15
|
-
className,
|
|
16
|
-
)}
|
|
17
|
-
/>
|
|
18
|
-
);
|
|
9
|
+
return <Skeleton {...props} className={className} />;
|
|
19
10
|
}
|
|
@@ -138,37 +138,6 @@
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
.dashboard-panel-skeleton {
|
|
142
|
-
background-color: hsl(var(--muted-foreground) / 0.14);
|
|
143
|
-
background-image: linear-gradient(
|
|
144
|
-
105deg,
|
|
145
|
-
transparent 28%,
|
|
146
|
-
hsl(var(--foreground) / 0.025) 44%,
|
|
147
|
-
hsl(var(--foreground) / 0.06) 50%,
|
|
148
|
-
hsl(var(--foreground) / 0.025) 56%,
|
|
149
|
-
transparent 72%
|
|
150
|
-
);
|
|
151
|
-
background-size: 28rem 100%;
|
|
152
|
-
background-repeat: repeat;
|
|
153
|
-
animation: dashboard-panel-skeleton-shimmer 2.6s linear infinite;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
@keyframes dashboard-panel-skeleton-shimmer {
|
|
157
|
-
0% {
|
|
158
|
-
background-position: 0 0;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
100% {
|
|
162
|
-
background-position: 28rem 0;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
@media (prefers-reduced-motion: reduce) {
|
|
167
|
-
.dashboard-panel-skeleton {
|
|
168
|
-
animation: none;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
141
|
.analytics-mobile-nav {
|
|
173
142
|
height: 2.75rem;
|
|
174
143
|
min-height: 2.75rem;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { callAction } from "@agent-native/core/client/hooks";
|
|
2
2
|
import { DASHBOARD_REPORT_ACTION_TIMEOUT_MS } from "@shared/dashboard-report-timeouts";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
MAX_CONCURRENT_FIRST_PARTY_SQL_QUERIES,
|
|
5
|
+
MAX_CONCURRENT_SQL_QUERIES,
|
|
6
|
+
} from "@shared/sql-query-limits";
|
|
4
7
|
import { useQuery } from "@tanstack/react-query";
|
|
5
8
|
|
|
6
9
|
import type { DataSourceType } from "@/pages/adhoc/sql-dashboard/types";
|
|
@@ -22,8 +25,30 @@ type PendingSqlQuerySlot = {
|
|
|
22
25
|
onAbort: () => void;
|
|
23
26
|
};
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
type SqlQueryLane = "first-party" | "external";
|
|
29
|
+
|
|
30
|
+
type SqlQuerySlotPool = {
|
|
31
|
+
active: number;
|
|
32
|
+
limit: number;
|
|
33
|
+
pending: PendingSqlQuerySlot[];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const sqlQuerySlotPools: Record<SqlQueryLane, SqlQuerySlotPool> = {
|
|
37
|
+
"first-party": {
|
|
38
|
+
active: 0,
|
|
39
|
+
limit: MAX_CONCURRENT_FIRST_PARTY_SQL_QUERIES,
|
|
40
|
+
pending: [],
|
|
41
|
+
},
|
|
42
|
+
external: {
|
|
43
|
+
active: 0,
|
|
44
|
+
limit: MAX_CONCURRENT_SQL_QUERIES,
|
|
45
|
+
pending: [],
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
function sqlQueryLane(source: DataSourceType): SqlQueryLane {
|
|
50
|
+
return source === "first-party" ? "first-party" : "external";
|
|
51
|
+
}
|
|
27
52
|
|
|
28
53
|
function createAbortError(): Error {
|
|
29
54
|
if (typeof DOMException !== "undefined") {
|
|
@@ -34,49 +59,50 @@ function createAbortError(): Error {
|
|
|
34
59
|
return error;
|
|
35
60
|
}
|
|
36
61
|
|
|
37
|
-
function createSqlQueryRelease(): () => void {
|
|
62
|
+
function createSqlQueryRelease(pool: SqlQuerySlotPool): () => void {
|
|
38
63
|
let released = false;
|
|
39
64
|
return () => {
|
|
40
65
|
if (released) return;
|
|
41
66
|
released = true;
|
|
42
|
-
|
|
43
|
-
drainSqlQuerySlots();
|
|
67
|
+
pool.active = Math.max(0, pool.active - 1);
|
|
68
|
+
drainSqlQuerySlots(pool);
|
|
44
69
|
};
|
|
45
70
|
}
|
|
46
71
|
|
|
47
|
-
function drainSqlQuerySlots(): void {
|
|
48
|
-
while (
|
|
49
|
-
|
|
50
|
-
pendingSqlQuerySlots.length > 0
|
|
51
|
-
) {
|
|
52
|
-
const pending = pendingSqlQuerySlots.shift();
|
|
72
|
+
function drainSqlQuerySlots(pool: SqlQuerySlotPool): void {
|
|
73
|
+
while (pool.active < pool.limit && pool.pending.length > 0) {
|
|
74
|
+
const pending = pool.pending.shift();
|
|
53
75
|
if (!pending) return;
|
|
54
76
|
pending.signal?.removeEventListener("abort", pending.onAbort);
|
|
55
77
|
if (pending.signal?.aborted) {
|
|
56
78
|
pending.reject(createAbortError());
|
|
57
79
|
continue;
|
|
58
80
|
}
|
|
59
|
-
|
|
60
|
-
pending.resolve(createSqlQueryRelease());
|
|
81
|
+
pool.active += 1;
|
|
82
|
+
pending.resolve(createSqlQueryRelease(pool));
|
|
61
83
|
}
|
|
62
84
|
}
|
|
63
85
|
|
|
64
|
-
async function acquireSqlQuerySlot(
|
|
86
|
+
async function acquireSqlQuerySlot(
|
|
87
|
+
source: DataSourceType,
|
|
88
|
+
signal?: AbortSignal,
|
|
89
|
+
): Promise<() => void> {
|
|
65
90
|
if (signal?.aborted) throw createAbortError();
|
|
91
|
+
const pool = sqlQuerySlotPools[sqlQueryLane(source)];
|
|
66
92
|
return new Promise((resolve, reject) => {
|
|
67
93
|
const pending: PendingSqlQuerySlot = {
|
|
68
94
|
resolve,
|
|
69
95
|
reject,
|
|
70
96
|
signal,
|
|
71
97
|
onAbort: () => {
|
|
72
|
-
const index =
|
|
73
|
-
if (index >= 0)
|
|
98
|
+
const index = pool.pending.indexOf(pending);
|
|
99
|
+
if (index >= 0) pool.pending.splice(index, 1);
|
|
74
100
|
reject(createAbortError());
|
|
75
101
|
},
|
|
76
102
|
};
|
|
77
103
|
signal?.addEventListener("abort", pending.onAbort, { once: true });
|
|
78
|
-
|
|
79
|
-
drainSqlQuerySlots();
|
|
104
|
+
pool.pending.push(pending);
|
|
105
|
+
drainSqlQuerySlots(pool);
|
|
80
106
|
});
|
|
81
107
|
}
|
|
82
108
|
|
|
@@ -91,7 +117,7 @@ export async function executeSqlQuery(
|
|
|
91
117
|
signal?: AbortSignal,
|
|
92
118
|
options?: { reportScreenshot?: boolean },
|
|
93
119
|
): Promise<SqlQueryResult> {
|
|
94
|
-
const release = await acquireSqlQuerySlot(signal);
|
|
120
|
+
const release = await acquireSqlQuerySlot(source, signal);
|
|
95
121
|
let data: DashboardPanelQueryResponse;
|
|
96
122
|
try {
|
|
97
123
|
data = await callAction<DashboardPanelQueryResponse>(
|
|
@@ -26,6 +26,7 @@ interface L1Entry {
|
|
|
26
26
|
|
|
27
27
|
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
28
28
|
const MAX_L1_ENTRIES = 500;
|
|
29
|
+
const CACHE_IO_TIMEOUT_MS = 1_000;
|
|
29
30
|
|
|
30
31
|
const l1Cache = new Map<string, L1Entry>();
|
|
31
32
|
// De-dupes identical concurrent cache misses (e.g. the same panel query fired
|
|
@@ -70,7 +71,7 @@ function setL1(key: string, result: AnalyticsQueryResult): void {
|
|
|
70
71
|
|
|
71
72
|
async function getL2(
|
|
72
73
|
key: string,
|
|
73
|
-
|
|
74
|
+
deadlineAt: number,
|
|
74
75
|
): Promise<AnalyticsQueryResult | null> {
|
|
75
76
|
try {
|
|
76
77
|
const db = getDbExec();
|
|
@@ -78,7 +79,8 @@ async function getL2(
|
|
|
78
79
|
const { rows } = await db.execute({
|
|
79
80
|
sql: "SELECT result FROM first_party_analytics_cache WHERE key = ? AND expires_at > ?",
|
|
80
81
|
args: [key, nowIso],
|
|
81
|
-
timeoutMs,
|
|
82
|
+
timeoutMs: cacheIoTimeoutMs(deadlineAt),
|
|
83
|
+
maxAttempts: 1,
|
|
82
84
|
});
|
|
83
85
|
if (!rows.length) return null;
|
|
84
86
|
const raw = (rows[0] as { result: string }).result;
|
|
@@ -93,23 +95,24 @@ async function setL2(
|
|
|
93
95
|
key: string,
|
|
94
96
|
sql: string,
|
|
95
97
|
result: AnalyticsQueryResult,
|
|
96
|
-
|
|
98
|
+
deadlineAt: number,
|
|
97
99
|
): Promise<void> {
|
|
98
100
|
try {
|
|
99
101
|
const db = getDbExec();
|
|
100
102
|
const now = new Date();
|
|
101
103
|
const expiresAt = new Date(now.getTime() + CACHE_TTL_MS);
|
|
102
104
|
const serialized = JSON.stringify(result);
|
|
103
|
-
// Upsert via delete+insert to stay dialect-agnostic (SQLite/Postgres).
|
|
104
105
|
await db.execute({
|
|
105
|
-
sql:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
sql: `INSERT INTO first_party_analytics_cache (key, sql, result, created_at, expires_at)
|
|
107
|
+
VALUES (?, ?, ?, ?, ?)
|
|
108
|
+
ON CONFLICT(key) DO UPDATE SET
|
|
109
|
+
sql = excluded.sql,
|
|
110
|
+
result = excluded.result,
|
|
111
|
+
created_at = excluded.created_at,
|
|
112
|
+
expires_at = excluded.expires_at`,
|
|
111
113
|
args: [key, sql, serialized, now.toISOString(), expiresAt.toISOString()],
|
|
112
|
-
timeoutMs,
|
|
114
|
+
timeoutMs: cacheIoTimeoutMs(deadlineAt),
|
|
115
|
+
maxAttempts: 1,
|
|
113
116
|
});
|
|
114
117
|
// Opportunistically prune expired rows so the table doesn't grow
|
|
115
118
|
// unbounded — the keyspace is effectively every distinct panel/filter
|
|
@@ -118,7 +121,8 @@ async function setL2(
|
|
|
118
121
|
await db.execute({
|
|
119
122
|
sql: "DELETE FROM first_party_analytics_cache WHERE expires_at <= ?",
|
|
120
123
|
args: [now.toISOString()],
|
|
121
|
-
timeoutMs,
|
|
124
|
+
timeoutMs: cacheIoTimeoutMs(deadlineAt),
|
|
125
|
+
maxAttempts: 1,
|
|
122
126
|
});
|
|
123
127
|
}
|
|
124
128
|
} catch (err) {
|
|
@@ -126,6 +130,17 @@ async function setL2(
|
|
|
126
130
|
}
|
|
127
131
|
}
|
|
128
132
|
|
|
133
|
+
function remainingTimeoutMs(deadlineAt: number): number {
|
|
134
|
+
return Math.max(0, deadlineAt - Date.now());
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function cacheIoTimeoutMs(deadlineAt: number): number {
|
|
138
|
+
return Math.max(
|
|
139
|
+
1,
|
|
140
|
+
Math.min(CACHE_IO_TIMEOUT_MS, remainingTimeoutMs(deadlineAt)),
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
129
144
|
/**
|
|
130
145
|
* Returns a cached result for (key, sql) if fresh, otherwise runs `compute`
|
|
131
146
|
* exactly once — even under concurrent callers with the same key — and
|
|
@@ -134,12 +149,15 @@ async function setL2(
|
|
|
134
149
|
export async function withFirstPartyCache(
|
|
135
150
|
key: string,
|
|
136
151
|
sql: string,
|
|
137
|
-
compute: () => Promise<AnalyticsQueryResult>,
|
|
152
|
+
compute: (timeoutMs: number) => Promise<AnalyticsQueryResult>,
|
|
138
153
|
options: FirstPartyCacheOptions = {},
|
|
139
154
|
): Promise<AnalyticsQueryResult> {
|
|
140
155
|
const l1Hit = getL1(key);
|
|
141
156
|
if (l1Hit) return l1Hit;
|
|
142
157
|
|
|
158
|
+
const timeoutMs = Math.max(1, options.timeoutMs ?? CACHE_IO_TIMEOUT_MS);
|
|
159
|
+
const deadlineAt = Date.now() + timeoutMs;
|
|
160
|
+
|
|
143
161
|
// A report prewarm may have a shorter deadline than a normal panel request;
|
|
144
162
|
// never let those callers inherit one another's database timeout.
|
|
145
163
|
const requestKey = inFlightKey(key, options.timeoutMs);
|
|
@@ -150,14 +168,22 @@ export async function withFirstPartyCache(
|
|
|
150
168
|
// callers racing on the same key can't both slip past the check above and
|
|
151
169
|
// both hit L2/compute.
|
|
152
170
|
const promise = (async () => {
|
|
153
|
-
const l2Hit = await getL2(key,
|
|
171
|
+
const l2Hit = await getL2(key, deadlineAt);
|
|
154
172
|
if (l2Hit) {
|
|
155
173
|
setL1(key, l2Hit);
|
|
156
174
|
return l2Hit;
|
|
157
175
|
}
|
|
158
|
-
const
|
|
176
|
+
const queryTimeoutMs = remainingTimeoutMs(deadlineAt);
|
|
177
|
+
if (queryTimeoutMs <= 0) {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`First-party analytics query timed out after ${timeoutMs}ms`,
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const result = await compute(queryTimeoutMs);
|
|
159
183
|
setL1(key, result);
|
|
160
|
-
|
|
184
|
+
// Cache persistence is best-effort. A successful panel query must not miss
|
|
185
|
+
// its delivery deadline because the cache table is slow or unavailable.
|
|
186
|
+
void setL2(key, sql, result, deadlineAt);
|
|
161
187
|
return result;
|
|
162
188
|
})().finally(() => {
|
|
163
189
|
inFlight.delete(requestKey);
|
|
@@ -660,12 +660,14 @@ export async function queryFirstPartyAnalytics(
|
|
|
660
660
|
// org_id/owner_email (see scopeClause) — a cache hit can only ever return
|
|
661
661
|
// rows the same tenant was already entitled to query.
|
|
662
662
|
const cacheKey = firstPartyCacheKey(wrappedSql, scoped.args);
|
|
663
|
-
const compute = async (
|
|
663
|
+
const compute = async (
|
|
664
|
+
queryTimeoutMs = timeoutMs,
|
|
665
|
+
): Promise<AnalyticsQueryResult> => {
|
|
664
666
|
const exec = getDbExec();
|
|
665
667
|
const result = await exec.execute({
|
|
666
668
|
sql: wrappedSql,
|
|
667
669
|
args: scoped.args,
|
|
668
|
-
timeoutMs,
|
|
670
|
+
timeoutMs: queryTimeoutMs,
|
|
669
671
|
maxAttempts: 1,
|
|
670
672
|
});
|
|
671
673
|
const rows = result.rows as Record<string, unknown>[];
|
|
@@ -163,7 +163,9 @@ private UI after the shell loads. See the `authentication` skill and
|
|
|
163
163
|
proves *someone* is signed in. To restrict an operation to some teammates, set
|
|
164
164
|
`authorize` on the `defineAction`. It wraps `run`, so it applies at every
|
|
165
165
|
dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
|
|
166
|
-
`needsApproval`, which is
|
|
166
|
+
`needsApproval`, which is honored by the agent loop and MCP 2026 hosts that
|
|
167
|
+
support elicitation, but is not an authorization boundary for every dispatch
|
|
168
|
+
site.
|
|
167
169
|
|
|
168
170
|
```ts
|
|
169
171
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -203,7 +205,16 @@ export default defineAction({
|
|
|
203
205
|
});
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
When the gate is truthy and the call is not yet approved, the loop emits
|
|
208
|
+
When the gate is truthy and the call is not yet approved, the agent loop emits
|
|
209
|
+
an `approval_required` event and **stops the turn — `run()` never executes**.
|
|
210
|
+
The human approves via the chat UI's Approve affordance, which re-issues the
|
|
211
|
+
turn with the call's stable `approvalKey`; only then does the action run. MCP
|
|
212
|
+
2026 hosts receive an `input_required` approval request bound to the exact
|
|
213
|
+
caller, action, and arguments. The signed response state is backed by a durable,
|
|
214
|
+
single-use grant, so denial, expiry, replay, missing host support, or invalid
|
|
215
|
+
state fails closed before `run()`. A predicate gates conditionally (e.g. only
|
|
216
|
+
external recipients) and **fails closed** — a throw is treated as "approval
|
|
217
|
+
required".
|
|
207
218
|
|
|
208
219
|
Rules:
|
|
209
220
|
|
|
@@ -163,7 +163,9 @@ private UI after the shell loads. See the `authentication` skill and
|
|
|
163
163
|
proves *someone* is signed in. To restrict an operation to some teammates, set
|
|
164
164
|
`authorize` on the `defineAction`. It wraps `run`, so it applies at every
|
|
165
165
|
dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
|
|
166
|
-
`needsApproval`, which is
|
|
166
|
+
`needsApproval`, which is honored by the agent loop and MCP 2026 hosts that
|
|
167
|
+
support elicitation, but is not an authorization boundary for every dispatch
|
|
168
|
+
site.
|
|
167
169
|
|
|
168
170
|
```ts
|
|
169
171
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -203,7 +205,16 @@ export default defineAction({
|
|
|
203
205
|
});
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
When the gate is truthy and the call is not yet approved, the loop emits
|
|
208
|
+
When the gate is truthy and the call is not yet approved, the agent loop emits
|
|
209
|
+
an `approval_required` event and **stops the turn — `run()` never executes**.
|
|
210
|
+
The human approves via the chat UI's Approve affordance, which re-issues the
|
|
211
|
+
turn with the call's stable `approvalKey`; only then does the action run. MCP
|
|
212
|
+
2026 hosts receive an `input_required` approval request bound to the exact
|
|
213
|
+
caller, action, and arguments. The signed response state is backed by a durable,
|
|
214
|
+
single-use grant, so denial, expiry, replay, missing host support, or invalid
|
|
215
|
+
state fails closed before `run()`. A predicate gates conditionally (e.g. only
|
|
216
|
+
external recipients) and **fails closed** — a throw is treated as "approval
|
|
217
|
+
required".
|
|
207
218
|
|
|
208
219
|
Rules:
|
|
209
220
|
|
|
@@ -163,7 +163,9 @@ private UI after the shell loads. See the `authentication` skill and
|
|
|
163
163
|
proves *someone* is signed in. To restrict an operation to some teammates, set
|
|
164
164
|
`authorize` on the `defineAction`. It wraps `run`, so it applies at every
|
|
165
165
|
dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
|
|
166
|
-
`needsApproval`, which is
|
|
166
|
+
`needsApproval`, which is honored by the agent loop and MCP 2026 hosts that
|
|
167
|
+
support elicitation, but is not an authorization boundary for every dispatch
|
|
168
|
+
site.
|
|
167
169
|
|
|
168
170
|
```ts
|
|
169
171
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -203,7 +205,16 @@ export default defineAction({
|
|
|
203
205
|
});
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
When the gate is truthy and the call is not yet approved, the loop emits
|
|
208
|
+
When the gate is truthy and the call is not yet approved, the agent loop emits
|
|
209
|
+
an `approval_required` event and **stops the turn — `run()` never executes**.
|
|
210
|
+
The human approves via the chat UI's Approve affordance, which re-issues the
|
|
211
|
+
turn with the call's stable `approvalKey`; only then does the action run. MCP
|
|
212
|
+
2026 hosts receive an `input_required` approval request bound to the exact
|
|
213
|
+
caller, action, and arguments. The signed response state is backed by a durable,
|
|
214
|
+
single-use grant, so denial, expiry, replay, missing host support, or invalid
|
|
215
|
+
state fails closed before `run()`. A predicate gates conditionally (e.g. only
|
|
216
|
+
external recipients) and **fails closed** — a throw is treated as "approval
|
|
217
|
+
required".
|
|
207
218
|
|
|
208
219
|
Rules:
|
|
209
220
|
|
|
@@ -163,7 +163,9 @@ private UI after the shell loads. See the `authentication` skill and
|
|
|
163
163
|
proves *someone* is signed in. To restrict an operation to some teammates, set
|
|
164
164
|
`authorize` on the `defineAction`. It wraps `run`, so it applies at every
|
|
165
165
|
dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
|
|
166
|
-
`needsApproval`, which is
|
|
166
|
+
`needsApproval`, which is honored by the agent loop and MCP 2026 hosts that
|
|
167
|
+
support elicitation, but is not an authorization boundary for every dispatch
|
|
168
|
+
site.
|
|
167
169
|
|
|
168
170
|
```ts
|
|
169
171
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -203,7 +205,16 @@ export default defineAction({
|
|
|
203
205
|
});
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
When the gate is truthy and the call is not yet approved, the loop emits
|
|
208
|
+
When the gate is truthy and the call is not yet approved, the agent loop emits
|
|
209
|
+
an `approval_required` event and **stops the turn — `run()` never executes**.
|
|
210
|
+
The human approves via the chat UI's Approve affordance, which re-issues the
|
|
211
|
+
turn with the call's stable `approvalKey`; only then does the action run. MCP
|
|
212
|
+
2026 hosts receive an `input_required` approval request bound to the exact
|
|
213
|
+
caller, action, and arguments. The signed response state is backed by a durable,
|
|
214
|
+
single-use grant, so denial, expiry, replay, missing host support, or invalid
|
|
215
|
+
state fails closed before `run()`. A predicate gates conditionally (e.g. only
|
|
216
|
+
external recipients) and **fails closed** — a throw is treated as "approval
|
|
217
|
+
required".
|
|
207
218
|
|
|
208
219
|
Rules:
|
|
209
220
|
|
|
@@ -163,7 +163,9 @@ private UI after the shell loads. See the `authentication` skill and
|
|
|
163
163
|
proves *someone* is signed in. To restrict an operation to some teammates, set
|
|
164
164
|
`authorize` on the `defineAction`. It wraps `run`, so it applies at every
|
|
165
165
|
dispatch site (agent tool, HTTP, frontend, MCP, A2A, CLI) — unlike
|
|
166
|
-
`needsApproval`, which is
|
|
166
|
+
`needsApproval`, which is honored by the agent loop and MCP 2026 hosts that
|
|
167
|
+
support elicitation, but is not an authorization boundary for every dispatch
|
|
168
|
+
site.
|
|
167
169
|
|
|
168
170
|
```ts
|
|
169
171
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -203,7 +205,16 @@ export default defineAction({
|
|
|
203
205
|
});
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
When the gate is truthy and the call is not yet approved, the loop emits
|
|
208
|
+
When the gate is truthy and the call is not yet approved, the agent loop emits
|
|
209
|
+
an `approval_required` event and **stops the turn — `run()` never executes**.
|
|
210
|
+
The human approves via the chat UI's Approve affordance, which re-issues the
|
|
211
|
+
turn with the call's stable `approvalKey`; only then does the action run. MCP
|
|
212
|
+
2026 hosts receive an `input_required` approval request bound to the exact
|
|
213
|
+
caller, action, and arguments. The signed response state is backed by a durable,
|
|
214
|
+
single-use grant, so denial, expiry, replay, missing host support, or invalid
|
|
215
|
+
state fails closed before `run()`. A predicate gates conditionally (e.g. only
|
|
216
|
+
external recipients) and **fails closed** — a throw is treated as "approval
|
|
217
|
+
required".
|
|
207
218
|
|
|
208
219
|
Rules:
|
|
209
220
|
|
|
@@ -323,11 +323,8 @@ function IconBtn({
|
|
|
323
323
|
|
|
324
324
|
function SkipIcon({ direction }: { direction: "back" | "forward" }) {
|
|
325
325
|
return (
|
|
326
|
-
<
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
/>
|
|
330
|
-
<span className="absolute text-[7px] font-bold leading-none">5</span>
|
|
331
|
-
</span>
|
|
326
|
+
<IconPlayerSkipForward
|
|
327
|
+
className={cn("h-5 w-5", direction === "back" && "rotate-180")}
|
|
328
|
+
/>
|
|
332
329
|
);
|
|
333
330
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
1
|
+
import { useActionQuery, useAvatarUrl } from "@agent-native/core/client/hooks";
|
|
2
2
|
import { useT } from "@agent-native/core/client/i18n";
|
|
3
3
|
import { IconTerminal2, IconUser } from "@tabler/icons-react";
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
|
|
6
|
-
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
6
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
7
7
|
import { Button } from "@/components/ui/button";
|
|
8
8
|
import {
|
|
9
9
|
Popover,
|
|
@@ -284,8 +284,12 @@ export function ViewerAvatar({
|
|
|
284
284
|
className?: string;
|
|
285
285
|
}) {
|
|
286
286
|
const anonymous = !viewer.viewerName && !viewer.viewerEmail;
|
|
287
|
+
const avatarUrl = useAvatarUrl(viewer.viewerEmail);
|
|
288
|
+
const label = viewer.viewerName || viewer.viewerEmail || "";
|
|
289
|
+
|
|
287
290
|
return (
|
|
288
291
|
<Avatar className={cn("h-6 w-6 shrink-0", className)}>
|
|
292
|
+
{avatarUrl ? <AvatarImage src={avatarUrl} alt={label} /> : null}
|
|
289
293
|
<AvatarFallback className="bg-primary text-[10px] text-primary-foreground">
|
|
290
294
|
{anonymous ? (
|
|
291
295
|
<IconUser className="h-3 w-3" />
|
|
@@ -16,6 +16,11 @@ import {
|
|
|
16
16
|
import { useQueryClient } from "@tanstack/react-query";
|
|
17
17
|
import { useMemo, useState } from "react";
|
|
18
18
|
|
|
19
|
+
import {
|
|
20
|
+
Avatar as UserAvatar,
|
|
21
|
+
AvatarFallback as UserAvatarFallback,
|
|
22
|
+
AvatarImage as UserAvatarImage,
|
|
23
|
+
} from "@/components/ui/avatar";
|
|
19
24
|
import { Button } from "@/components/ui/button";
|
|
20
25
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
21
26
|
import { Input } from "@/components/ui/input";
|
|
@@ -26,6 +31,7 @@ import {
|
|
|
26
31
|
SelectTrigger,
|
|
27
32
|
SelectValue,
|
|
28
33
|
} from "@/components/ui/select";
|
|
34
|
+
import { useVisibleAvatarUrl } from "@/lib/use-visible-avatar-url";
|
|
29
35
|
import { cn } from "@/lib/utils";
|
|
30
36
|
|
|
31
37
|
// ---------------------------------------------------------------------------
|
|
@@ -304,17 +310,26 @@ export function CopyField({
|
|
|
304
310
|
// ---------------------------------------------------------------------------
|
|
305
311
|
|
|
306
312
|
export function Avatar({ label, org }: { label: string; org?: boolean }) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
+
const { avatarRef, avatarUrl } = useVisibleAvatarUrl(org ? null : label);
|
|
314
|
+
|
|
315
|
+
if (org) {
|
|
316
|
+
return (
|
|
317
|
+
<span
|
|
318
|
+
aria-hidden
|
|
319
|
+
className="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-muted text-[11px] font-semibold text-muted-foreground"
|
|
320
|
+
>
|
|
313
321
|
<IconUsersGroup size={14} strokeWidth={1.75} />
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
322
|
+
</span>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return (
|
|
327
|
+
<UserAvatar ref={avatarRef} className="h-7 w-7 shrink-0">
|
|
328
|
+
{avatarUrl ? <UserAvatarImage src={avatarUrl} alt={label} /> : null}
|
|
329
|
+
<UserAvatarFallback className="bg-muted text-[11px] font-semibold text-muted-foreground">
|
|
330
|
+
{(label.split("@")[0]?.[0] ?? label[0] ?? "?").toUpperCase()}
|
|
331
|
+
</UserAvatarFallback>
|
|
332
|
+
</UserAvatar>
|
|
318
333
|
);
|
|
319
334
|
}
|
|
320
335
|
|
|
@@ -2,12 +2,13 @@ import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
|
2
2
|
import { useFormatters, useT } from "@agent-native/core/client/i18n";
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
|
|
5
|
-
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
6
6
|
import {
|
|
7
7
|
Popover,
|
|
8
8
|
PopoverContent,
|
|
9
9
|
PopoverTrigger,
|
|
10
10
|
} from "@/components/ui/popover";
|
|
11
|
+
import { useVisibleAvatarUrl } from "@/lib/use-visible-avatar-url";
|
|
11
12
|
import { cn } from "@/lib/utils";
|
|
12
13
|
|
|
13
14
|
interface ClipViewRecord {
|
|
@@ -100,11 +101,11 @@ export function ViewedByPopover({
|
|
|
100
101
|
key={v.id}
|
|
101
102
|
className="flex items-center gap-2 rounded-md px-2 py-1.5"
|
|
102
103
|
>
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
<ViewerAvatar
|
|
105
|
+
email={v.viewerEmail}
|
|
106
|
+
name={v.viewerName}
|
|
107
|
+
label={label}
|
|
108
|
+
/>
|
|
108
109
|
<div className="min-w-0 flex-1">
|
|
109
110
|
<div className="truncate text-sm text-foreground">
|
|
110
111
|
{label}
|
|
@@ -124,6 +125,27 @@ export function ViewedByPopover({
|
|
|
124
125
|
);
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
export function ViewerAvatar({
|
|
129
|
+
email,
|
|
130
|
+
name,
|
|
131
|
+
label,
|
|
132
|
+
}: {
|
|
133
|
+
email: string | null;
|
|
134
|
+
name: string | null;
|
|
135
|
+
label: string;
|
|
136
|
+
}) {
|
|
137
|
+
const { avatarRef, avatarUrl } = useVisibleAvatarUrl(email);
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<Avatar ref={avatarRef} className="h-6 w-6 shrink-0">
|
|
141
|
+
{avatarUrl ? <AvatarImage src={avatarUrl} alt={label} /> : null}
|
|
142
|
+
<AvatarFallback className="text-[10px] bg-primary text-primary-foreground">
|
|
143
|
+
{initials(name || email || "?")}
|
|
144
|
+
</AvatarFallback>
|
|
145
|
+
</Avatar>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
127
149
|
function initials(s: string): string {
|
|
128
150
|
return s
|
|
129
151
|
.split(/\s+|@/)
|