@agent-native/core 0.95.1 → 0.97.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +64 -0
- package/corpus/core/docs/content/audit-log.mdx +2 -1
- package/corpus/core/docs/content/automations.mdx +7 -7
- package/corpus/core/docs/content/creating-templates.mdx +1 -1
- package/corpus/core/docs/content/internationalization.mdx +2 -1
- package/corpus/core/docs/content/locales/ar-SA/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/ar-SA/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/ar-SA/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/ar-SA/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
- package/corpus/core/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/ar-SA/security.mdx +8 -0
- package/corpus/core/docs/content/locales/de-DE/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/de-DE/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/de-DE/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/de-DE/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
- package/corpus/core/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/de-DE/security.mdx +8 -0
- package/corpus/core/docs/content/locales/es-ES/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/es-ES/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/es-ES/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/es-ES/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
- package/corpus/core/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/es-ES/security.mdx +8 -0
- package/corpus/core/docs/content/locales/fr-FR/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/fr-FR/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/fr-FR/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/fr-FR/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
- package/corpus/core/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
- package/corpus/core/docs/content/locales/fr-FR/security.mdx +8 -0
- package/corpus/core/docs/content/locales/hi-IN/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/hi-IN/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/hi-IN/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/hi-IN/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
- package/corpus/core/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/hi-IN/security.mdx +8 -0
- package/corpus/core/docs/content/locales/ja-JP/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/ja-JP/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/ja-JP/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/ja-JP/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
- package/corpus/core/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
- package/corpus/core/docs/content/locales/ja-JP/security.mdx +8 -0
- package/corpus/core/docs/content/locales/ko-KR/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/ko-KR/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/ko-KR/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/ko-KR/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
- package/corpus/core/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
- package/corpus/core/docs/content/locales/ko-KR/security.mdx +8 -0
- package/corpus/core/docs/content/locales/pt-BR/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/pt-BR/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/pt-BR/getting-started.mdx +7 -1
- package/corpus/core/docs/content/locales/pt-BR/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
- package/corpus/core/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/pt-BR/security.mdx +8 -0
- package/corpus/core/docs/content/locales/zh-CN/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/zh-CN/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/zh-CN/getting-started.mdx +6 -4
- package/corpus/core/docs/content/locales/zh-CN/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
- package/corpus/core/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
- package/corpus/core/docs/content/locales/zh-CN/security.mdx +8 -0
- package/corpus/core/docs/content/locales/zh-TW/audit-log.mdx +2 -1
- package/corpus/core/docs/content/locales/zh-TW/automations.mdx +7 -7
- package/corpus/core/docs/content/locales/zh-TW/getting-started.mdx +6 -4
- package/corpus/core/docs/content/locales/zh-TW/internationalization.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
- package/corpus/core/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
- package/corpus/core/docs/content/locales/zh-TW/security.mdx +8 -0
- package/corpus/core/docs/content/package-lifecycle.mdx +51 -0
- package/corpus/core/docs/content/sandbox-adapters.mdx +16 -5
- package/corpus/core/package.json +11 -2
- package/corpus/core/src/a2a/auth-policy.ts +33 -0
- package/corpus/core/src/a2a/client.ts +11 -1
- package/corpus/core/src/agent/durable-background.ts +15 -7
- package/corpus/core/src/agent/production-agent.ts +87 -27
- package/corpus/core/src/agent/run-store.ts +13 -4
- package/corpus/core/src/agent/thread-data-builder.ts +7 -5
- package/corpus/core/src/audit/actions/export-audit-events.ts +162 -0
- package/corpus/core/src/audit/store.ts +8 -3
- package/corpus/core/src/audit/types.ts +3 -0
- package/corpus/core/src/chat-threads/store.ts +20 -1
- package/corpus/core/src/cli/code-agent-executor.ts +1 -0
- package/corpus/core/src/cli/code-agent-runs.ts +14 -0
- package/corpus/core/src/cli/doctor.ts +396 -0
- package/corpus/core/src/cli/index.ts +56 -0
- package/corpus/core/src/cli/info.ts +3 -3
- package/corpus/core/src/cli/package-lifecycle.ts +821 -0
- package/corpus/core/src/client/AgentPanel.tsx +32 -25
- package/corpus/core/src/client/AssistantChat.tsx +36 -20
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +141 -90
- package/corpus/core/src/client/agent-chat-adapter.ts +9 -9
- package/corpus/core/src/client/chat/ChatHistoryList.tsx +404 -0
- package/corpus/core/src/client/chat/index.ts +6 -0
- package/corpus/core/src/client/chat/tool-call-display.tsx +37 -3
- package/corpus/core/src/client/code-agent-chat-adapter.ts +72 -4
- package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +52 -11
- package/corpus/core/src/client/composer/TiptapComposer.tsx +157 -20
- package/corpus/core/src/client/composer/VoiceButton.tsx +28 -0
- package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +5 -0
- package/corpus/core/src/client/conversation/code-agent-transcript.ts +4 -5
- package/corpus/core/src/client/guided-questions.tsx +2 -1
- package/corpus/core/src/client/index.ts +11 -0
- package/corpus/core/src/client/use-chat-threads.ts +10 -1
- package/corpus/core/src/client/voice-provider-status.ts +44 -0
- package/corpus/core/src/code-agents/transcript-normalizer.ts +76 -0
- package/corpus/core/src/deploy/build.ts +12 -11
- package/corpus/core/src/guards/db-tool-scoping.ts +129 -0
- package/corpus/core/src/guards/index.ts +23 -0
- package/corpus/core/src/guards/no-drizzle-push.ts +116 -0
- package/corpus/core/src/guards/no-env-credentials.ts +229 -0
- package/corpus/core/src/guards/no-env-mutation.ts +114 -0
- package/corpus/core/src/guards/no-localhost-fallback.ts +124 -0
- package/corpus/core/src/guards/no-unscoped-credentials.ts +227 -0
- package/corpus/core/src/guards/no-unscoped-queries.ts +834 -0
- package/corpus/core/src/guards/scan-utils.ts +105 -0
- package/corpus/core/src/guards/types.ts +30 -0
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +35 -5
- package/corpus/core/src/integrations/adapters/slack.ts +74 -21
- package/corpus/core/src/integrations/webhook-handler.ts +14 -0
- package/corpus/core/src/localization/default-messages.ts +6 -0
- package/corpus/core/src/observability/hosted-model-experiment.ts +118 -0
- package/corpus/core/src/observability/routes.ts +17 -0
- package/corpus/core/src/observability/store.ts +4 -1
- package/corpus/core/src/observability/traces.ts +41 -2
- package/corpus/core/src/org/accept-pending.ts +9 -1
- package/corpus/core/src/org/migrations.ts +41 -0
- package/corpus/core/src/package-lifecycle/index.ts +1 -0
- package/corpus/core/src/package-lifecycle/manifest.ts +120 -0
- package/corpus/core/src/provider-api/custom-registry.ts +56 -0
- package/corpus/core/src/provider-api/index.ts +2 -0
- package/corpus/core/src/scripts/agent-engines/list-agent-engines.ts +19 -1
- package/corpus/core/src/scripts/call-agent.ts +53 -3
- package/corpus/core/src/scripts/chat/search-chats.ts +11 -8
- package/corpus/core/src/secrets/register-framework-secrets.ts +1 -1
- package/corpus/core/src/secrets/storage.ts +47 -27
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +95 -18
- package/corpus/core/src/server/core-routes-plugin.ts +45 -14
- package/corpus/core/src/server/csrf.ts +24 -5
- package/corpus/core/src/server/framework-request-handler.ts +34 -0
- package/corpus/core/src/server/google-realtime-session.ts +1 -36
- package/corpus/core/src/server/poll.ts +24 -2
- package/corpus/core/src/server/realtime-voice.ts +80 -24
- package/corpus/core/src/server/request-origin.ts +63 -0
- package/corpus/core/src/server/transcribe-voice.ts +1 -50
- package/corpus/core/src/server/voice-providers-status.ts +4 -3
- package/corpus/core/src/shared/cache-control.ts +2 -8
- package/corpus/core/src/styles/agent-native.css +1 -0
- package/corpus/core/src/styles/chat-history-list.css +313 -0
- package/corpus/core/src/templates/default/package.json +2 -2
- package/corpus/core/src/templates/headless/package.json +0 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
- package/corpus/core/src/templates/workspace-core/package.json +0 -1
- package/corpus/core/src/templates/workspace-root/package.json +0 -1
- package/corpus/core/src/triggers/actions.ts +11 -3
- package/corpus/core/src/triggers/types.ts +7 -1
- package/corpus/core/src/vite/client.ts +1 -0
- package/corpus/templates/analytics/AGENTS.md +7 -0
- package/corpus/templates/analytics/actions/bigquery.ts +26 -2
- package/corpus/templates/analytics/actions/compose-dashboard.ts +65 -37
- package/corpus/templates/analytics/actions/data-source-status.ts +18 -0
- package/corpus/templates/analytics/actions/mutate-dashboard.ts +73 -24
- package/corpus/templates/analytics/actions/reorder-dashboard-panels.ts +16 -13
- package/corpus/templates/analytics/actions/update-dashboard.ts +53 -42
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +5 -4
- package/corpus/templates/analytics/app/global.css +10 -0
- package/corpus/templates/analytics/app/pages/Ask.tsx +25 -2
- package/corpus/templates/analytics/changelog/2026-07-10-analytics-answers-simple-time-bounded-metrics-without-unnece.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-analytics-chat-reliably-uses-connected-data-sources-across-s.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-chat-threads-now-stay-clear-of-top-actions-and-fade-smoothly.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-context-chips-can-now-be-selected-and-removed-one-at-a-time-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-dashboard-edits-from-the-agent-and-the-ui-at-the-same-time-n.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-the-ask-composer-no-longer-carries-dashboard-or-panel-contex.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-the-ask-section-stays-highlighted-while-its-chat-history-is-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-10-timed-out-bigquery-jobs-are-now-cancelled-so-they-stop-consu.md +6 -0
- package/corpus/templates/analytics/package.json +1 -2
- package/corpus/templates/analytics/server/lib/bigquery.ts +91 -14
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +154 -11
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +82 -7
- package/corpus/templates/assets/package.json +1 -2
- package/corpus/templates/brain/package.json +1 -2
- package/corpus/templates/calendar/package.json +1 -2
- package/corpus/templates/chat/package.json +1 -2
- package/corpus/templates/clips/changelog/2026-07-10-atomic-view-tracking.md +6 -0
- package/corpus/templates/clips/chrome-extension/package.json +3 -2
- package/corpus/templates/clips/desktop/package.json +6 -4
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +26 -13
- package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +4 -1
- package/corpus/templates/clips/package.json +1 -2
- package/corpus/templates/clips/server/db/schema.ts +29 -17
- package/corpus/templates/clips/server/plugins/db.ts +8 -0
- package/corpus/templates/clips/server/routes/api/view-event.post.ts +260 -105
- package/corpus/templates/content/.agents/skills/notion-integration/SKILL.md +16 -0
- package/corpus/templates/content/AGENTS.md +72 -62
- package/corpus/templates/content/actions/_content-database-source-adapters.ts +84 -0
- package/corpus/templates/content/actions/_database-source-utils.ts +206 -96
- package/corpus/templates/content/actions/_notion-database-source-adapter.ts +216 -0
- package/corpus/templates/content/actions/_position-utils.ts +76 -0
- package/corpus/templates/content/actions/_property-utils.ts +31 -20
- package/corpus/templates/content/actions/add-database-item.ts +52 -39
- package/corpus/templates/content/actions/attach-content-database-source.ts +95 -11
- package/corpus/templates/content/actions/change-content-database-source-role.ts +24 -1
- package/corpus/templates/content/actions/configure-document-property.ts +36 -27
- package/corpus/templates/content/actions/create-content-database.ts +40 -30
- package/corpus/templates/content/actions/create-document.ts +39 -32
- package/corpus/templates/content/actions/duplicate-document-property.ts +37 -28
- package/corpus/templates/content/actions/list-notion-database-sources.ts +80 -0
- package/corpus/templates/content/actions/move-document.ts +64 -48
- package/corpus/templates/content/actions/refresh-content-database-source.ts +42 -3
- package/corpus/templates/content/actions/share-local-file-document.ts +36 -27
- package/corpus/templates/content/actions/suggest-source-join-key.ts +39 -16
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +89 -23
- package/corpus/templates/content/app/components/editor/database/settings.tsx +1 -1
- package/corpus/templates/content/app/hooks/use-content-database.ts +18 -1
- package/corpus/templates/content/app/i18n/zh-TW.ts +4 -0
- package/corpus/templates/content/app/i18n-data.ts +40 -0
- package/corpus/templates/content/changelog/2026-07-10-failed-row-creation-and-source-attach-now-show-an-error-inst.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-items-added-at-the-same-time-no-longer-end-up-with-identical.md +6 -0
- package/corpus/templates/content/changelog/2026-07-10-notion-databases-can-add-read-only-details-to-content-tables.md +6 -0
- package/corpus/templates/content/package.json +1 -2
- package/corpus/templates/content/parity/matrix.md +25 -25
- package/corpus/templates/content/parity/matrix.ts +1 -0
- package/corpus/templates/content/server/routes/api/documents/[id]/move.patch.ts +43 -26
- package/corpus/templates/content/shared/api.ts +16 -1
- package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +8 -4
- package/corpus/templates/design/AGENTS.md +12 -2
- package/corpus/templates/design/DEVELOPING.md +10 -6
- package/corpus/templates/design/actions/apply-visual-edit.ts +191 -6
- package/corpus/templates/design/actions/duplicate-design.ts +31 -25
- package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +5 -1
- package/corpus/templates/design/changelog/2026-07-10-duplicating-a-design-can-no-longer-produce-a-copy-with-missi.md +6 -0
- package/corpus/templates/design/changelog/2026-07-10-local-visual-edits-can-now-save-safe-leaf-text-class-and.md +6 -0
- package/corpus/templates/design/package.json +1 -1
- package/corpus/templates/design/shared/local-jsx-visual-edit.ts +388 -0
- package/corpus/templates/dispatch/package.json +1 -2
- package/corpus/templates/forms/actions/patch-form-fields.ts +82 -44
- package/corpus/templates/forms/changelog/2026-07-10-simultaneous-edits-to-different-form-fields-no-longer-overwr.md +6 -0
- package/corpus/templates/forms/package.json +1 -2
- package/corpus/templates/macros/package.json +1 -2
- package/corpus/templates/mail/app/components/email/EmailThread.tsx +3 -1
- package/corpus/templates/mail/changelog/2026-07-10-fixed-calendar-rsvp-buttons-in-emails-firing-duplicate-respo.md +6 -0
- package/corpus/templates/mail/package.json +1 -2
- package/corpus/templates/plan/actions/restore-plan-version.ts +126 -96
- package/corpus/templates/plan/changelog/2026-07-10-restoring-a-plan-version-can-no-longer-leave-a-plan-half-res.md +6 -0
- package/corpus/templates/plan/package.json +1 -2
- package/corpus/templates/plan/server/plans.ts +9 -2
- package/corpus/templates/slides/actions/list-decks.ts +28 -0
- package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +41 -26
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +4 -8
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +2 -4
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +4 -8
- package/corpus/templates/slides/app/components/editor/ExportMenu.tsx +5 -12
- package/corpus/templates/slides/app/components/editor/HistoryPanel.tsx +3 -6
- package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +3 -6
- package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +2 -5
- package/corpus/templates/slides/app/context/DeckContext.tsx +47 -4
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +11 -30
- package/corpus/templates/slides/app/pages/Index.tsx +2 -3
- package/corpus/templates/slides/app/root.tsx +0 -5
- package/corpus/templates/slides/changelog/2026-07-10-deck-lists-load-much-faster-and-no-longer-re-download-slide-.md +6 -0
- package/corpus/templates/slides/package.json +1 -2
- package/dist/a2a/auth-policy.d.ts +19 -0
- package/dist/a2a/auth-policy.d.ts.map +1 -1
- package/dist/a2a/auth-policy.js +32 -0
- package/dist/a2a/auth-policy.js.map +1 -1
- package/dist/a2a/client.d.ts +7 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +2 -1
- package/dist/a2a/client.js.map +1 -1
- package/dist/agent/durable-background.d.ts +8 -5
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +15 -8
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts +6 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +63 -26
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-store.d.ts.map +1 -1
- package/dist/agent/run-store.js +9 -4
- package/dist/agent/run-store.js.map +1 -1
- package/dist/agent/thread-data-builder.d.ts +1 -0
- package/dist/agent/thread-data-builder.d.ts.map +1 -1
- package/dist/agent/thread-data-builder.js +6 -5
- package/dist/agent/thread-data-builder.js.map +1 -1
- package/dist/audit/actions/export-audit-events.d.ts +27 -0
- package/dist/audit/actions/export-audit-events.d.ts.map +1 -0
- package/dist/audit/actions/export-audit-events.js +144 -0
- package/dist/audit/actions/export-audit-events.js.map +1 -0
- package/dist/audit/store.d.ts +1 -0
- package/dist/audit/store.d.ts.map +1 -1
- package/dist/audit/store.js +8 -3
- package/dist/audit/store.js.map +1 -1
- package/dist/audit/types.d.ts +3 -0
- package/dist/audit/types.d.ts.map +1 -1
- package/dist/audit/types.js.map +1 -1
- package/dist/chat-threads/store.d.ts +10 -0
- package/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +6 -0
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/code-agent-executor.d.ts.map +1 -1
- package/dist/cli/code-agent-executor.js +1 -0
- package/dist/cli/code-agent-executor.js.map +1 -1
- package/dist/cli/code-agent-runs.d.ts +12 -0
- package/dist/cli/code-agent-runs.d.ts.map +1 -1
- package/dist/cli/code-agent-runs.js +1 -0
- package/dist/cli/code-agent-runs.js.map +1 -1
- package/dist/cli/doctor.d.ts +83 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +277 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.js +50 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/info.js +3 -3
- package/dist/cli/info.js.map +1 -1
- package/dist/cli/package-lifecycle.d.ts +50 -0
- package/dist/cli/package-lifecycle.d.ts.map +1 -0
- package/dist/cli/package-lifecycle.js +560 -0
- package/dist/cli/package-lifecycle.js.map +1 -0
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +3 -3
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/AssistantChat.d.ts +11 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +7 -4
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +83 -10
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/agent-chat-adapter.js +7 -3
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/chat/ChatHistoryList.d.ts +88 -0
- package/dist/client/chat/ChatHistoryList.d.ts.map +1 -0
- package/dist/client/chat/ChatHistoryList.js +83 -0
- package/dist/client/chat/ChatHistoryList.js.map +1 -0
- package/dist/client/chat/index.d.ts +1 -0
- package/dist/client/chat/index.d.ts.map +1 -1
- package/dist/client/chat/index.js +1 -0
- package/dist/client/chat/index.js.map +1 -1
- package/dist/client/chat/tool-call-display.d.ts +13 -1
- package/dist/client/chat/tool-call-display.d.ts.map +1 -1
- package/dist/client/chat/tool-call-display.js +10 -2
- package/dist/client/chat/tool-call-display.js.map +1 -1
- package/dist/client/code-agent-chat-adapter.d.ts +17 -1
- package/dist/client/code-agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/code-agent-chat-adapter.js +47 -3
- package/dist/client/code-agent-chat-adapter.js.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.d.ts +11 -1
- package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/RealtimeVoiceMode.js +5 -3
- package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +25 -2
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +96 -5
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/VoiceButton.d.ts.map +1 -1
- package/dist/client/composer/VoiceButton.js +21 -1
- package/dist/client/composer/VoiceButton.js.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
- package/dist/client/composer/useRealtimeVoiceMode.js +5 -0
- package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
- package/dist/client/conversation/code-agent-transcript.d.ts +2 -0
- package/dist/client/conversation/code-agent-transcript.d.ts.map +1 -1
- package/dist/client/conversation/code-agent-transcript.js +3 -5
- package/dist/client/conversation/code-agent-transcript.js.map +1 -1
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +2 -1
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/index.d.ts +3 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +9 -1
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/client/voice-provider-status.d.ts +10 -0
- package/dist/client/voice-provider-status.d.ts.map +1 -0
- package/dist/client/voice-provider-status.js +32 -0
- package/dist/client/voice-provider-status.js.map +1 -0
- package/dist/code-agents/transcript-normalizer.d.ts +29 -0
- package/dist/code-agents/transcript-normalizer.d.ts.map +1 -1
- package/dist/code-agents/transcript-normalizer.js +55 -0
- package/dist/code-agents/transcript-normalizer.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +13 -11
- package/dist/deploy/build.js.map +1 -1
- package/dist/guards/db-tool-scoping.d.ts +38 -0
- package/dist/guards/db-tool-scoping.d.ts.map +1 -0
- package/dist/guards/db-tool-scoping.js +114 -0
- package/dist/guards/db-tool-scoping.js.map +1 -0
- package/dist/guards/index.d.ts +23 -0
- package/dist/guards/index.d.ts.map +1 -0
- package/dist/guards/index.js +19 -0
- package/dist/guards/index.js.map +1 -0
- package/dist/guards/no-drizzle-push.d.ts +14 -0
- package/dist/guards/no-drizzle-push.d.ts.map +1 -0
- package/dist/guards/no-drizzle-push.js +97 -0
- package/dist/guards/no-drizzle-push.js.map +1 -0
- package/dist/guards/no-env-credentials.d.ts +36 -0
- package/dist/guards/no-env-credentials.d.ts.map +1 -0
- package/dist/guards/no-env-credentials.js +204 -0
- package/dist/guards/no-env-credentials.js.map +1 -0
- package/dist/guards/no-env-mutation.d.ts +23 -0
- package/dist/guards/no-env-mutation.d.ts.map +1 -0
- package/dist/guards/no-env-mutation.js +90 -0
- package/dist/guards/no-env-mutation.js.map +1 -0
- package/dist/guards/no-localhost-fallback.d.ts +30 -0
- package/dist/guards/no-localhost-fallback.d.ts.map +1 -0
- package/dist/guards/no-localhost-fallback.js +105 -0
- package/dist/guards/no-localhost-fallback.js.map +1 -0
- package/dist/guards/no-unscoped-credentials.d.ts +28 -0
- package/dist/guards/no-unscoped-credentials.d.ts.map +1 -0
- package/dist/guards/no-unscoped-credentials.js +199 -0
- package/dist/guards/no-unscoped-credentials.js.map +1 -0
- package/dist/guards/no-unscoped-queries.d.ts +32 -0
- package/dist/guards/no-unscoped-queries.d.ts.map +1 -0
- package/dist/guards/no-unscoped-queries.js +720 -0
- package/dist/guards/no-unscoped-queries.js.map +1 -0
- package/dist/guards/scan-utils.d.ts +28 -0
- package/dist/guards/scan-utils.d.ts.map +1 -0
- package/dist/guards/scan-utils.js +92 -0
- package/dist/guards/scan-utils.js.map +1 -0
- package/dist/guards/types.d.ts +28 -0
- package/dist/guards/types.d.ts.map +1 -0
- package/dist/guards/types.js +11 -0
- package/dist/guards/types.js.map +1 -0
- package/dist/integrations/a2a-continuation-processor.d.ts.map +1 -1
- package/dist/integrations/a2a-continuation-processor.js +25 -5
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/integrations/adapters/slack.d.ts.map +1 -1
- package/dist/integrations/adapters/slack.js +55 -21
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +11 -0
- package/dist/integrations/webhook-handler.js.map +1 -1
- package/dist/localization/default-messages.d.ts +5 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +5 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/hosted-model-experiment.d.ts +39 -0
- package/dist/observability/hosted-model-experiment.d.ts.map +1 -0
- package/dist/observability/hosted-model-experiment.js +90 -0
- package/dist/observability/hosted-model-experiment.js.map +1 -0
- package/dist/observability/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts.map +1 -1
- package/dist/observability/routes.js +16 -0
- package/dist/observability/routes.js.map +1 -1
- package/dist/observability/store.d.ts.map +1 -1
- package/dist/observability/store.js +4 -1
- package/dist/observability/store.js.map +1 -1
- package/dist/observability/traces.d.ts +6 -0
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +28 -2
- package/dist/observability/traces.js.map +1 -1
- package/dist/org/accept-pending.d.ts.map +1 -1
- package/dist/org/accept-pending.js +9 -1
- package/dist/org/accept-pending.js.map +1 -1
- package/dist/org/migrations.d.ts +7 -2
- package/dist/org/migrations.d.ts.map +1 -1
- package/dist/org/migrations.js +41 -0
- package/dist/org/migrations.js.map +1 -1
- package/dist/package-lifecycle/index.d.ts +2 -0
- package/dist/package-lifecycle/index.d.ts.map +1 -0
- package/dist/package-lifecycle/index.js +2 -0
- package/dist/package-lifecycle/index.js.map +1 -0
- package/dist/package-lifecycle/manifest.d.ts +24 -0
- package/dist/package-lifecycle/manifest.d.ts.map +1 -0
- package/dist/package-lifecycle/manifest.js +81 -0
- package/dist/package-lifecycle/manifest.js.map +1 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/custom-registry.d.ts +32 -1
- package/dist/provider-api/custom-registry.d.ts.map +1 -1
- package/dist/provider-api/custom-registry.js +41 -1
- package/dist/provider-api/custom-registry.js.map +1 -1
- package/dist/provider-api/index.d.ts +1 -1
- package/dist/provider-api/index.d.ts.map +1 -1
- package/dist/provider-api/index.js +1 -1
- package/dist/provider-api/index.js.map +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.d.ts.map +1 -1
- package/dist/scripts/agent-engines/list-agent-engines.js +19 -1
- package/dist/scripts/agent-engines/list-agent-engines.js.map +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +43 -3
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/chat/search-chats.d.ts +1 -1
- package/dist/scripts/chat/search-chats.d.ts.map +1 -1
- package/dist/scripts/chat/search-chats.js +11 -8
- package/dist/scripts/chat/search-chats.js.map +1 -1
- package/dist/secrets/register-framework-secrets.js +1 -1
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +45 -27
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +4 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +11 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +74 -19
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/core-routes-plugin.d.ts +3 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +36 -12
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/csrf.d.ts.map +1 -1
- package/dist/server/csrf.js +18 -5
- package/dist/server/csrf.js.map +1 -1
- package/dist/server/framework-request-handler.d.ts.map +1 -1
- package/dist/server/framework-request-handler.js +34 -0
- package/dist/server/framework-request-handler.js.map +1 -1
- package/dist/server/google-realtime-session.d.ts.map +1 -1
- package/dist/server/google-realtime-session.js +2 -34
- package/dist/server/google-realtime-session.js.map +1 -1
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +24 -2
- package/dist/server/poll.js.map +1 -1
- package/dist/server/realtime-voice.d.ts.map +1 -1
- package/dist/server/realtime-voice.js +64 -21
- package/dist/server/realtime-voice.js.map +1 -1
- package/dist/server/request-origin.d.ts +10 -0
- package/dist/server/request-origin.d.ts.map +1 -0
- package/dist/server/request-origin.js +60 -0
- package/dist/server/request-origin.js.map +1 -0
- package/dist/server/transcribe-voice.d.ts.map +1 -1
- package/dist/server/transcribe-voice.js +2 -48
- package/dist/server/transcribe-voice.js.map +1 -1
- package/dist/server/voice-providers-status.d.ts.map +1 -1
- package/dist/server/voice-providers-status.js +2 -3
- package/dist/server/voice-providers-status.js.map +1 -1
- package/dist/shared/cache-control.d.ts +4 -6
- package/dist/shared/cache-control.d.ts.map +1 -1
- package/dist/shared/cache-control.js +2 -7
- package/dist/shared/cache-control.js.map +1 -1
- package/dist/styles/agent-native.css +1 -0
- package/dist/styles/chat-history-list.css +313 -0
- package/dist/templates/default/package.json +2 -2
- package/dist/templates/headless/package.json +0 -1
- package/dist/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
- package/dist/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
- package/dist/templates/workspace-core/package.json +0 -1
- package/dist/templates/workspace-root/package.json +0 -1
- package/dist/triggers/actions.d.ts.map +1 -1
- package/dist/triggers/actions.js +8 -3
- package/dist/triggers/actions.js.map +1 -1
- package/dist/triggers/types.d.ts +7 -1
- package/dist/triggers/types.d.ts.map +1 -1
- package/dist/triggers/types.js.map +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +1 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/audit-log.mdx +2 -1
- package/docs/content/automations.mdx +7 -7
- package/docs/content/creating-templates.mdx +1 -1
- package/docs/content/internationalization.mdx +2 -1
- package/docs/content/locales/ar-SA/audit-log.mdx +2 -1
- package/docs/content/locales/ar-SA/automations.mdx +7 -7
- package/docs/content/locales/ar-SA/getting-started.mdx +7 -1
- package/docs/content/locales/ar-SA/internationalization.mdx +1 -1
- package/docs/content/locales/ar-SA/package-lifecycle.mdx +19 -0
- package/docs/content/locales/ar-SA/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/ar-SA/security.mdx +8 -0
- package/docs/content/locales/de-DE/audit-log.mdx +2 -1
- package/docs/content/locales/de-DE/automations.mdx +7 -7
- package/docs/content/locales/de-DE/getting-started.mdx +7 -1
- package/docs/content/locales/de-DE/internationalization.mdx +1 -1
- package/docs/content/locales/de-DE/package-lifecycle.mdx +22 -0
- package/docs/content/locales/de-DE/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/de-DE/security.mdx +8 -0
- package/docs/content/locales/es-ES/audit-log.mdx +2 -1
- package/docs/content/locales/es-ES/automations.mdx +7 -7
- package/docs/content/locales/es-ES/getting-started.mdx +7 -1
- package/docs/content/locales/es-ES/internationalization.mdx +1 -1
- package/docs/content/locales/es-ES/package-lifecycle.mdx +22 -0
- package/docs/content/locales/es-ES/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/es-ES/security.mdx +8 -0
- package/docs/content/locales/fr-FR/audit-log.mdx +2 -1
- package/docs/content/locales/fr-FR/automations.mdx +7 -7
- package/docs/content/locales/fr-FR/getting-started.mdx +7 -1
- package/docs/content/locales/fr-FR/internationalization.mdx +1 -1
- package/docs/content/locales/fr-FR/package-lifecycle.mdx +21 -0
- package/docs/content/locales/fr-FR/sandbox-adapters.mdx +19 -8
- package/docs/content/locales/fr-FR/security.mdx +8 -0
- package/docs/content/locales/hi-IN/audit-log.mdx +2 -1
- package/docs/content/locales/hi-IN/automations.mdx +7 -7
- package/docs/content/locales/hi-IN/getting-started.mdx +7 -1
- package/docs/content/locales/hi-IN/internationalization.mdx +1 -1
- package/docs/content/locales/hi-IN/package-lifecycle.mdx +20 -0
- package/docs/content/locales/hi-IN/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/hi-IN/security.mdx +8 -0
- package/docs/content/locales/ja-JP/audit-log.mdx +2 -1
- package/docs/content/locales/ja-JP/automations.mdx +7 -7
- package/docs/content/locales/ja-JP/getting-started.mdx +7 -1
- package/docs/content/locales/ja-JP/internationalization.mdx +1 -1
- package/docs/content/locales/ja-JP/package-lifecycle.mdx +21 -0
- package/docs/content/locales/ja-JP/sandbox-adapters.mdx +17 -6
- package/docs/content/locales/ja-JP/security.mdx +8 -0
- package/docs/content/locales/ko-KR/audit-log.mdx +2 -1
- package/docs/content/locales/ko-KR/automations.mdx +7 -7
- package/docs/content/locales/ko-KR/getting-started.mdx +7 -1
- package/docs/content/locales/ko-KR/internationalization.mdx +1 -1
- package/docs/content/locales/ko-KR/package-lifecycle.mdx +20 -0
- package/docs/content/locales/ko-KR/sandbox-adapters.mdx +17 -6
- package/docs/content/locales/ko-KR/security.mdx +8 -0
- package/docs/content/locales/pt-BR/audit-log.mdx +2 -1
- package/docs/content/locales/pt-BR/automations.mdx +7 -7
- package/docs/content/locales/pt-BR/getting-started.mdx +7 -1
- package/docs/content/locales/pt-BR/internationalization.mdx +1 -1
- package/docs/content/locales/pt-BR/package-lifecycle.mdx +21 -0
- package/docs/content/locales/pt-BR/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/pt-BR/security.mdx +8 -0
- package/docs/content/locales/zh-CN/audit-log.mdx +2 -1
- package/docs/content/locales/zh-CN/automations.mdx +7 -7
- package/docs/content/locales/zh-CN/getting-started.mdx +6 -4
- package/docs/content/locales/zh-CN/internationalization.mdx +1 -1
- package/docs/content/locales/zh-CN/package-lifecycle.mdx +19 -0
- package/docs/content/locales/zh-CN/sandbox-adapters.mdx +17 -6
- package/docs/content/locales/zh-CN/security.mdx +8 -0
- package/docs/content/locales/zh-TW/audit-log.mdx +2 -1
- package/docs/content/locales/zh-TW/automations.mdx +7 -7
- package/docs/content/locales/zh-TW/getting-started.mdx +6 -4
- package/docs/content/locales/zh-TW/internationalization.mdx +1 -1
- package/docs/content/locales/zh-TW/package-lifecycle.mdx +19 -0
- package/docs/content/locales/zh-TW/sandbox-adapters.mdx +18 -7
- package/docs/content/locales/zh-TW/security.mdx +8 -0
- package/docs/content/package-lifecycle.mdx +51 -0
- package/docs/content/sandbox-adapters.mdx +16 -5
- package/package.json +12 -3
- package/src/templates/default/package.json +2 -2
- package/src/templates/headless/package.json +0 -1
- package/src/templates/workspace-core/.agents/skills/audit-log/SKILL.md +3 -0
- package/src/templates/workspace-core/.agents/skills/automations/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +20 -0
- package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +8 -4
- package/src/templates/workspace-core/package.json +0 -1
- package/src/templates/workspace-root/package.json +0 -1
- package/corpus/templates/slides/app/components/ui/toast.tsx +0 -1
- package/corpus/templates/slides/app/components/ui/toaster.tsx +0 -1
- package/corpus/templates/slides/app/hooks/use-toast.ts +0 -1
|
@@ -63,8 +63,16 @@ export async function acceptPendingInvitationsForEmail(
|
|
|
63
63
|
});
|
|
64
64
|
if (existing.rows.length === 0) {
|
|
65
65
|
const role = inv.role === "admin" ? "admin" : "member";
|
|
66
|
+
// The SELECT above is a cheap pre-check, not a correctness guard —
|
|
67
|
+
// two concurrent acceptances (e.g. a retried signup hook) can both
|
|
68
|
+
// pass it before either INSERT commits. `ON CONFLICT (org_id,
|
|
69
|
+
// LOWER(email)) DO NOTHING` targets the unique expression index
|
|
70
|
+
// added in migrations.ts (org-members-unique-lower-email-idx) so the
|
|
71
|
+
// race's loser is a silent no-op instead of a thrown unique
|
|
72
|
+
// constraint violation or a duplicate row.
|
|
66
73
|
await db.execute({
|
|
67
|
-
sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)
|
|
74
|
+
sql: `INSERT INTO org_members (id, org_id, email, role, joined_at) VALUES (?, ?, ?, ?, ?)
|
|
75
|
+
ON CONFLICT (org_id, LOWER(email)) DO NOTHING`,
|
|
68
76
|
args: [nanoid(), inv.orgId, email, role, Date.now()],
|
|
69
77
|
});
|
|
70
78
|
}
|
|
@@ -61,4 +61,45 @@ export const ORG_MIGRATIONS = [
|
|
|
61
61
|
version: 1008,
|
|
62
62
|
sql: `CREATE INDEX IF NOT EXISTS organizations_lower_allowed_domain_idx ON organizations (LOWER(allowed_domain))`,
|
|
63
63
|
},
|
|
64
|
+
{
|
|
65
|
+
// De-dup pass ahead of the unique index below. `org_members` has always
|
|
66
|
+
// had a `UNIQUE(org_id, email)` constraint (see v1002), but that's an
|
|
67
|
+
// exact-string match — callers that insert a session's raw-case email
|
|
68
|
+
// (e.g. handlers.ts's acceptInvitationHandler) can still create a
|
|
69
|
+
// second row for the same person under a different case, and every
|
|
70
|
+
// membership *read* in this module already matches case-insensitively
|
|
71
|
+
// via `LOWER(email)`. This keeps exactly one row per (org_id,
|
|
72
|
+
// LOWER(email)) — the row with the oldest `joined_at` (ties broken by
|
|
73
|
+
// `id` for determinism) — by deleting any row for which a strictly
|
|
74
|
+
// "older" row exists in the same group. Portable across SQLite and
|
|
75
|
+
// Postgres (plain correlated EXISTS subquery, no window functions).
|
|
76
|
+
// Must run before the unique index below or that CREATE would fail on
|
|
77
|
+
// any database that already has case-variant duplicates.
|
|
78
|
+
version: 1009,
|
|
79
|
+
name: "org-members-dedupe-lower-email",
|
|
80
|
+
sql: `DELETE FROM org_members
|
|
81
|
+
WHERE EXISTS (
|
|
82
|
+
SELECT 1 FROM org_members older
|
|
83
|
+
WHERE older.org_id = org_members.org_id
|
|
84
|
+
AND LOWER(older.email) = LOWER(org_members.email)
|
|
85
|
+
AND (
|
|
86
|
+
older.joined_at < org_members.joined_at
|
|
87
|
+
OR (older.joined_at = org_members.joined_at AND older.id < org_members.id)
|
|
88
|
+
)
|
|
89
|
+
)`,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
// Closes the TOCTOU window in `acceptPendingInvitationsForEmail`
|
|
93
|
+
// (org/accept-pending.ts): a SELECT-then-INSERT check with no unique
|
|
94
|
+
// constraint standing behind the case-insensitive comparison every
|
|
95
|
+
// reader uses. Without this, two concurrent acceptances (e.g. a
|
|
96
|
+
// retried signup hook) can both pass the SELECT and both INSERT,
|
|
97
|
+
// producing duplicate membership rows. This expression index makes
|
|
98
|
+
// (org_id, LOWER(email)) unique at the database level so an
|
|
99
|
+
// `ON CONFLICT` insert (or a raw duplicate insert) is rejected
|
|
100
|
+
// instead of silently creating a second row.
|
|
101
|
+
version: 1010,
|
|
102
|
+
name: "org-members-unique-lower-email-idx",
|
|
103
|
+
sql: `CREATE UNIQUE INDEX IF NOT EXISTS org_members_org_lower_email_uidx ON org_members (org_id, LOWER(email))`,
|
|
104
|
+
},
|
|
64
105
|
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./manifest.js";
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export const AGENT_NATIVE_PACKAGE_MANIFEST_VERSION = 1 as const;
|
|
2
|
+
|
|
3
|
+
export interface AgentNativePackageManifest {
|
|
4
|
+
manifestVersion: typeof AGENT_NATIVE_PACKAGE_MANIFEST_VERSION;
|
|
5
|
+
name: string;
|
|
6
|
+
actions: string[];
|
|
7
|
+
schemaEntryPoint: string;
|
|
8
|
+
docs: {
|
|
9
|
+
llms: string;
|
|
10
|
+
llmsFull: string;
|
|
11
|
+
skills: string[];
|
|
12
|
+
};
|
|
13
|
+
requiredSecrets: { key: string; label: string; optional?: boolean }[];
|
|
14
|
+
peerProviders: string[];
|
|
15
|
+
eject?: {
|
|
16
|
+
sourceRoot: string;
|
|
17
|
+
targetDirectory: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function assertAgentNativePackageManifest(
|
|
22
|
+
value: unknown,
|
|
23
|
+
): asserts value is AgentNativePackageManifest {
|
|
24
|
+
const manifest = value as Partial<AgentNativePackageManifest> | null;
|
|
25
|
+
if (!manifest || typeof manifest !== "object") {
|
|
26
|
+
throw new Error("Package manifest must be an object");
|
|
27
|
+
}
|
|
28
|
+
if (manifest.manifestVersion !== AGENT_NATIVE_PACKAGE_MANIFEST_VERSION) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Unsupported package manifest version ${String(manifest.manifestVersion)}`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
if (!manifest.name || !manifest.schemaEntryPoint) {
|
|
34
|
+
throw new Error("Package manifest is missing name or schemaEntryPoint");
|
|
35
|
+
}
|
|
36
|
+
if (!/^(@[a-z0-9._-]+\/)?[a-z0-9._-]+$/.test(manifest.name)) {
|
|
37
|
+
throw new Error(`Invalid package manifest name: ${manifest.name}`);
|
|
38
|
+
}
|
|
39
|
+
if (
|
|
40
|
+
!manifest.schemaEntryPoint.startsWith(`${manifest.name}/`) ||
|
|
41
|
+
!isSafeSpecifier(manifest.schemaEntryPoint)
|
|
42
|
+
) {
|
|
43
|
+
throw new Error("Package manifest schemaEntryPoint is unsafe");
|
|
44
|
+
}
|
|
45
|
+
if (
|
|
46
|
+
!Array.isArray(manifest.actions) ||
|
|
47
|
+
!manifest.docs ||
|
|
48
|
+
!Array.isArray(manifest.docs.skills) ||
|
|
49
|
+
!Array.isArray(manifest.requiredSecrets) ||
|
|
50
|
+
!Array.isArray(manifest.peerProviders)
|
|
51
|
+
) {
|
|
52
|
+
throw new Error("Package manifest contribution lists are invalid");
|
|
53
|
+
}
|
|
54
|
+
for (const action of manifest.actions) {
|
|
55
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(action)) {
|
|
56
|
+
throw new Error(`Invalid package action name: ${action}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const skill of manifest.docs.skills) {
|
|
60
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(skill)) {
|
|
61
|
+
throw new Error(`Invalid package skill name: ${skill}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const docPath of [manifest.docs.llms, manifest.docs.llmsFull]) {
|
|
65
|
+
if (!isSafeRelativePath(docPath)) {
|
|
66
|
+
throw new Error(`Unsafe package documentation path: ${docPath}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (const secret of manifest.requiredSecrets) {
|
|
70
|
+
if (
|
|
71
|
+
!secret ||
|
|
72
|
+
!/^[A-Z][A-Z0-9_]*$/.test(secret.key) ||
|
|
73
|
+
typeof secret.label !== "string" ||
|
|
74
|
+
secret.label.length === 0 ||
|
|
75
|
+
secret.label.length > 120 ||
|
|
76
|
+
(secret.optional !== undefined && typeof secret.optional !== "boolean")
|
|
77
|
+
) {
|
|
78
|
+
throw new Error("Invalid package requiredSecrets entry");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const provider of manifest.peerProviders) {
|
|
82
|
+
if (
|
|
83
|
+
typeof provider !== "string" ||
|
|
84
|
+
!/^[a-z0-9][a-z0-9-]*$/.test(provider)
|
|
85
|
+
) {
|
|
86
|
+
throw new Error(`Invalid package peer provider: ${String(provider)}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (
|
|
90
|
+
manifest.eject &&
|
|
91
|
+
(!isSafeRelativePath(manifest.eject.sourceRoot) ||
|
|
92
|
+
!isSafeRelativePath(manifest.eject.targetDirectory))
|
|
93
|
+
) {
|
|
94
|
+
throw new Error("Package eject paths must be safe relative paths");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isSafeRelativePath(value: unknown): value is string {
|
|
99
|
+
if (typeof value !== "string" || value.length === 0 || value.includes("\\")) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
if (
|
|
103
|
+
value.includes("\0") ||
|
|
104
|
+
value.startsWith("/") ||
|
|
105
|
+
pathSegments(value).includes("..")
|
|
106
|
+
) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return !value.split("/").includes("");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isSafeSpecifier(value: string): boolean {
|
|
113
|
+
return (
|
|
114
|
+
/^[A-Za-z0-9@._/-]+$/.test(value) && !pathSegments(value).includes("..")
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function pathSegments(value: string): string[] {
|
|
119
|
+
return value.split("/");
|
|
120
|
+
}
|
|
@@ -64,6 +64,12 @@ export interface UpsertCustomProviderArgs {
|
|
|
64
64
|
allowedHostSuffixes?: string[];
|
|
65
65
|
defaultHeaders?: Record<string, string>;
|
|
66
66
|
notes?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Caller's org role in the target org (`scopeId` when `scope === "org"`),
|
|
69
|
+
* resolved by the caller (e.g. `provider-api-register.ts`) before invoking
|
|
70
|
+
* this function. Required — see `assertCanMutateCustomProviderScope`.
|
|
71
|
+
*/
|
|
72
|
+
orgRole: string | null;
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
// ---------------------------------------------------------------------------
|
|
@@ -258,6 +264,53 @@ export function validateAllowedHostSuffixes(suffixes: string[]): string[] {
|
|
|
258
264
|
return normalized;
|
|
259
265
|
}
|
|
260
266
|
|
|
267
|
+
// ---------------------------------------------------------------------------
|
|
268
|
+
// Authorization
|
|
269
|
+
// ---------------------------------------------------------------------------
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Thrown by `assertCanMutateCustomProviderScope` when the caller is not
|
|
273
|
+
* authorized to mutate a scoped custom provider row.
|
|
274
|
+
*/
|
|
275
|
+
export class CustomProviderAuthError extends Error {
|
|
276
|
+
constructor(
|
|
277
|
+
readonly statusCode: number,
|
|
278
|
+
message: string,
|
|
279
|
+
) {
|
|
280
|
+
super(message);
|
|
281
|
+
this.name = "CustomProviderAuthError";
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Defense-in-depth authorization guard for custom-provider mutations. Mirrors
|
|
287
|
+
* `assertCanMutateScope` in `server/scoped-key-storage.ts`: user-scoped writes
|
|
288
|
+
* need no role check (each user only ever mutates their own row), but
|
|
289
|
+
* org-scoped writes require the caller to be an owner or admin of the target
|
|
290
|
+
* org (`scopeId`) — otherwise a plain member could repoint an org-wide
|
|
291
|
+
* provider's base URL or attach a different credential key name that other
|
|
292
|
+
* members' agents then trust. `scopeId` is accepted (unused today) for
|
|
293
|
+
* signature parity with `assertCanMutateScope` in case a future scope value
|
|
294
|
+
* needs it (e.g. a solo-workspace carve-out).
|
|
295
|
+
*
|
|
296
|
+
* The only caller today is `provider-api-register.ts`, which resolves
|
|
297
|
+
* `orgRole` itself and enforces this same check before calling in; this
|
|
298
|
+
* function is the second, harder-to-bypass layer so a future caller can't
|
|
299
|
+
* reintroduce the gap by forgetting to check.
|
|
300
|
+
*/
|
|
301
|
+
export function assertCanMutateCustomProviderScope(
|
|
302
|
+
scope: CustomProviderScope,
|
|
303
|
+
_scopeId: string,
|
|
304
|
+
orgRole: string | null,
|
|
305
|
+
): void {
|
|
306
|
+
if (scope === "user") return;
|
|
307
|
+
if (orgRole === "owner" || orgRole === "admin") return;
|
|
308
|
+
throw new CustomProviderAuthError(
|
|
309
|
+
403,
|
|
310
|
+
"Only organization owners and admins can register or modify org-scoped custom providers.",
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
261
314
|
// ---------------------------------------------------------------------------
|
|
262
315
|
// CRUD
|
|
263
316
|
// ---------------------------------------------------------------------------
|
|
@@ -269,6 +322,7 @@ export function validateAllowedHostSuffixes(suffixes: string[]): string[] {
|
|
|
269
322
|
export async function upsertCustomProvider(
|
|
270
323
|
args: UpsertCustomProviderArgs,
|
|
271
324
|
): Promise<string> {
|
|
325
|
+
assertCanMutateCustomProviderScope(args.scope, args.scopeId, args.orgRole);
|
|
272
326
|
await ensureTable();
|
|
273
327
|
validateProviderId(args.id);
|
|
274
328
|
validateAuth(args.auth);
|
|
@@ -333,7 +387,9 @@ export async function deleteCustomProvider(
|
|
|
333
387
|
scope: CustomProviderScope,
|
|
334
388
|
scopeId: string,
|
|
335
389
|
id: string,
|
|
390
|
+
orgRole: string | null,
|
|
336
391
|
): Promise<boolean> {
|
|
392
|
+
assertCanMutateCustomProviderScope(scope, scopeId, orgRole);
|
|
337
393
|
await ensureTable();
|
|
338
394
|
const client = getDbExec();
|
|
339
395
|
const { rowsAffected } = await client.execute({
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
normalizeModelForEngine,
|
|
16
16
|
} from "../../agent/engine/index.js";
|
|
17
17
|
import type { ActionTool } from "../../agent/types.js";
|
|
18
|
+
import { resolveHostedDefaultModelExperiment } from "../../observability/hosted-model-experiment.js";
|
|
19
|
+
import { getRequestUserEmail } from "../../server/request-context.js";
|
|
18
20
|
import { getSetting } from "../../settings/index.js";
|
|
19
21
|
|
|
20
22
|
export const tool: ActionTool = {
|
|
@@ -82,13 +84,23 @@ export async function run(args: Record<string, string> = {}): Promise<string> {
|
|
|
82
84
|
? current?.model
|
|
83
85
|
: undefined;
|
|
84
86
|
const currentEngineName = currentEntry?.name ?? "anthropic";
|
|
85
|
-
|
|
87
|
+
let currentModel =
|
|
86
88
|
currentEntry && !envUnavailable
|
|
87
89
|
? normalizeModelForEngine(
|
|
88
90
|
currentEntry,
|
|
89
91
|
currentModelCandidate ?? currentEntry.defaultModel,
|
|
90
92
|
)
|
|
91
93
|
: (currentModelCandidate ?? DEFAULT_MODEL);
|
|
94
|
+
const hostedExperiment =
|
|
95
|
+
currentEntry && !currentModelCandidate
|
|
96
|
+
? resolveHostedDefaultModelExperiment({
|
|
97
|
+
userId: getRequestUserEmail(),
|
|
98
|
+
engineName: currentEntry.name,
|
|
99
|
+
isDefaultModelSelection: true,
|
|
100
|
+
supportedModels: currentEntry.supportedModels,
|
|
101
|
+
})
|
|
102
|
+
: null;
|
|
103
|
+
if (hostedExperiment) currentModel = hostedExperiment.model;
|
|
92
104
|
|
|
93
105
|
const result = {
|
|
94
106
|
engines: engines.map((e) => ({
|
|
@@ -107,6 +119,12 @@ export async function run(args: Record<string, string> = {}): Promise<string> {
|
|
|
107
119
|
: {
|
|
108
120
|
engine: currentEngineName,
|
|
109
121
|
model: currentModel,
|
|
122
|
+
...(hostedExperiment
|
|
123
|
+
? {
|
|
124
|
+
modelSelectionSource: "experiment",
|
|
125
|
+
experimentAssignment: hostedExperiment.assignment,
|
|
126
|
+
}
|
|
127
|
+
: {}),
|
|
110
128
|
},
|
|
111
129
|
};
|
|
112
130
|
|
|
@@ -321,7 +321,14 @@ export async function run(
|
|
|
321
321
|
orgDomain: callerOrgDomain,
|
|
322
322
|
orgSecret: callerOrgSecret,
|
|
323
323
|
onUpdate: onRemotePollUpdate,
|
|
324
|
-
...(callTimeoutMs
|
|
324
|
+
...(callTimeoutMs
|
|
325
|
+
? {
|
|
326
|
+
timeoutMs: callTimeoutMs,
|
|
327
|
+
// Integration callers must keep the timeout task id so the
|
|
328
|
+
// catch below can enqueue durable continuation polling.
|
|
329
|
+
returnRecoverableArtifactsOnTimeout: false,
|
|
330
|
+
}
|
|
331
|
+
: {}),
|
|
325
332
|
});
|
|
326
333
|
responseText =
|
|
327
334
|
formatDownstreamLlmCredentialFailure(agent.name, responseText) ??
|
|
@@ -348,8 +355,23 @@ export async function run(
|
|
|
348
355
|
`The ${agent.name} agent accepted this delegated subtask and will post its own final result to the originating integration thread automatically. ` +
|
|
349
356
|
`Do not call ${agent.name} again for this same subtask. Continue any other requested work, then answer with the completed results you have; if needed, mention that ${agent.name} is posting its result separately.`;
|
|
350
357
|
} else {
|
|
351
|
-
|
|
352
|
-
|
|
358
|
+
// The normal integration path must preserve the timeout task id so
|
|
359
|
+
// it can enqueue a durable continuation. If that enqueue fails,
|
|
360
|
+
// do not hide receiver-verified artifacts that were already
|
|
361
|
+
// returned with the last poll; this mirrors callAgent's default
|
|
362
|
+
// timeout behavior without treating arbitrary remote status text
|
|
363
|
+
// as a completed response.
|
|
364
|
+
const recoverableArtifactText =
|
|
365
|
+
extractRecoverableTimeoutArtifactText(pollErr);
|
|
366
|
+
if (recoverableArtifactText) {
|
|
367
|
+
responseText = expandRelativeUrls(
|
|
368
|
+
recoverableArtifactText,
|
|
369
|
+
agent.url,
|
|
370
|
+
);
|
|
371
|
+
} else {
|
|
372
|
+
const reason = pollErr?.message ?? "unknown error";
|
|
373
|
+
responseText = `The ${agent.name} agent is taking longer than expected and didn't reply in time. (${reason})`;
|
|
374
|
+
}
|
|
353
375
|
}
|
|
354
376
|
} else {
|
|
355
377
|
const reason = pollErr?.message ?? "unknown error";
|
|
@@ -490,6 +512,34 @@ function getA2ATaskTimeoutTaskId(err: unknown): string | null {
|
|
|
490
512
|
return match?.[1] ?? null;
|
|
491
513
|
}
|
|
492
514
|
|
|
515
|
+
/**
|
|
516
|
+
* Mirrors the A2A client's default timeout recovery for the exceptional case
|
|
517
|
+
* where an integration cannot enqueue a durable continuation. Only an
|
|
518
|
+
* explicitly receiver-marked task message is safe to surface as a completed
|
|
519
|
+
* partial result; ordinary working-state text remains a timeout failure.
|
|
520
|
+
*/
|
|
521
|
+
function extractRecoverableTimeoutArtifactText(err: unknown): string {
|
|
522
|
+
const candidate = err as
|
|
523
|
+
| { lastTask?: Task | unknown; name?: unknown }
|
|
524
|
+
| null
|
|
525
|
+
| undefined;
|
|
526
|
+
const lastTask =
|
|
527
|
+
err instanceof A2ATaskTimeoutError
|
|
528
|
+
? err.lastTask
|
|
529
|
+
: candidate?.name === "A2ATaskTimeoutError"
|
|
530
|
+
? (candidate.lastTask as Task | undefined)
|
|
531
|
+
: undefined;
|
|
532
|
+
const message = lastTask?.status?.message;
|
|
533
|
+
if (!message?.metadata?.agentNativeRecoverableArtifacts) return "";
|
|
534
|
+
|
|
535
|
+
return message.parts
|
|
536
|
+
.filter(
|
|
537
|
+
(part): part is { type: "text"; text: string } => part.type === "text",
|
|
538
|
+
)
|
|
539
|
+
.map((part) => part.text)
|
|
540
|
+
.join("\n");
|
|
541
|
+
}
|
|
542
|
+
|
|
493
543
|
async function formatExistingIntegrationContinuationIfRetry(
|
|
494
544
|
agent: {
|
|
495
545
|
name: string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Search or list past agent chat threads.
|
|
5
5
|
*
|
|
6
6
|
* Usage:
|
|
7
|
-
* pnpm action search-chats [--query "search term"] [--limit N] [--format json]
|
|
7
|
+
* pnpm action search-chats [--query "search term"] [--limit N] [--format json] [--includeArchived]
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { searchThreads, listThreads } from "../../chat-threads/store.js";
|
|
@@ -40,15 +40,17 @@ export default async function searchChats(args: string[]): Promise<void> {
|
|
|
40
40
|
console.log(`Usage: pnpm action search-chats [options]
|
|
41
41
|
|
|
42
42
|
Options:
|
|
43
|
-
--query <text>
|
|
44
|
-
--limit N
|
|
45
|
-
--format json
|
|
46
|
-
--
|
|
43
|
+
--query <text> Search chats by title, preview, or content
|
|
44
|
+
--limit N Max results (default: 20)
|
|
45
|
+
--format json Output as JSON
|
|
46
|
+
--includeArchived Also include archived chats (excluded by default)
|
|
47
|
+
--help Show this help message
|
|
47
48
|
|
|
48
49
|
Examples:
|
|
49
50
|
pnpm action search-chats --query "email setup"
|
|
50
51
|
pnpm action search-chats --limit 5
|
|
51
|
-
pnpm action search-chats --format json
|
|
52
|
+
pnpm action search-chats --format json
|
|
53
|
+
pnpm action search-chats --includeArchived`);
|
|
52
54
|
return;
|
|
53
55
|
}
|
|
54
56
|
|
|
@@ -56,10 +58,11 @@ Examples:
|
|
|
56
58
|
const limit = parsed.limit ? parseInt(parsed.limit, 10) : 20;
|
|
57
59
|
if (isNaN(limit) || limit < 1) fail("--limit must be a positive integer");
|
|
58
60
|
|
|
61
|
+
const includeArchived = parsed.includeArchived === "true";
|
|
59
62
|
const query = parsed.query;
|
|
60
63
|
const threads = query
|
|
61
|
-
? await searchThreads(owner, query, limit)
|
|
62
|
-
: await listThreads(owner, limit, 0);
|
|
64
|
+
? await searchThreads(owner, query, limit, { includeArchived })
|
|
65
|
+
: await listThreads(owner, { limit, offset: 0, includeArchived });
|
|
63
66
|
|
|
64
67
|
if (parsed.format === "json") {
|
|
65
68
|
console.log(
|
|
@@ -22,7 +22,7 @@ export function registerFrameworkSecrets(): void {
|
|
|
22
22
|
key: "OPENAI_API_KEY",
|
|
23
23
|
label: "OpenAI API key",
|
|
24
24
|
description:
|
|
25
|
-
"
|
|
25
|
+
"Optional fallback for realtime voice when Builder is not connected, and for OpenAI transcription.",
|
|
26
26
|
docsUrl: "https://platform.openai.com/api-keys",
|
|
27
27
|
scope: "user",
|
|
28
28
|
kind: "api-key",
|
|
@@ -142,37 +142,57 @@ export async function writeAppSecret(args: WriteSecretArgs): Promise<string> {
|
|
|
142
142
|
const client = getDbExec();
|
|
143
143
|
const now = Date.now();
|
|
144
144
|
const encrypted = encryptValue(value);
|
|
145
|
+
const id = randomUUID();
|
|
145
146
|
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
147
|
+
// Atomic upsert by (scope, scope_id, key). Previously this was a
|
|
148
|
+
// SELECT-then-branch (UPDATE if found, else INSERT): under concurrent
|
|
149
|
+
// writers for the same key both could see "no row" and both attempt
|
|
150
|
+
// INSERT, and the loser threw a raw UNIQUE(scope, scope_id, key)
|
|
151
|
+
// constraint violation (a user-facing 500) instead of updating. A single
|
|
152
|
+
// `INSERT ... ON CONFLICT DO UPDATE` closes that window — it's one
|
|
153
|
+
// statement, so there's no gap between "check" and "act". `id` is
|
|
154
|
+
// deliberately left out of the `DO UPDATE SET` list so an existing row
|
|
155
|
+
// keeps its original id (any stored references stay stable); only a
|
|
156
|
+
// genuinely new row gets the freshly generated `id`. This syntax is
|
|
157
|
+
// portable across SQLite (UPSERT since 3.24) and Postgres.
|
|
158
|
+
const upsertSql = `INSERT INTO app_secrets (id, scope, scope_id, key, encrypted_value, description, url_allowlist, created_at, updated_at)
|
|
159
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
160
|
+
ON CONFLICT (scope, scope_id, key) DO UPDATE SET
|
|
161
|
+
encrypted_value = excluded.encrypted_value,
|
|
162
|
+
description = excluded.description,
|
|
163
|
+
url_allowlist = excluded.url_allowlist,
|
|
164
|
+
updated_at = excluded.updated_at`;
|
|
165
|
+
const upsertArgs = [
|
|
166
|
+
id,
|
|
167
|
+
scope,
|
|
168
|
+
scopeId,
|
|
169
|
+
key,
|
|
170
|
+
encrypted,
|
|
171
|
+
description ?? null,
|
|
172
|
+
urlAllowlist ?? null,
|
|
173
|
+
now,
|
|
174
|
+
now,
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
if (isPostgres()) {
|
|
178
|
+
const { rows } = await client.execute({
|
|
179
|
+
sql: `${upsertSql} RETURNING id`,
|
|
180
|
+
args: upsertArgs,
|
|
157
181
|
});
|
|
158
|
-
return id;
|
|
182
|
+
return String(rows[0]?.id ?? id);
|
|
159
183
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
urlAllowlist ?? null,
|
|
171
|
-
now,
|
|
172
|
-
now,
|
|
173
|
-
],
|
|
184
|
+
|
|
185
|
+
// SQLite: RETURNING support varies across better-sqlite3/libsql builds, so
|
|
186
|
+
// (matching the convention elsewhere in this codebase, e.g.
|
|
187
|
+
// integrations/pending-tasks-store.ts) re-read the id afterward instead of
|
|
188
|
+
// relying on it. The row is guaranteed to exist at this point, so this is
|
|
189
|
+
// a plain lookup rather than a TOCTOU-prone gate on the write itself.
|
|
190
|
+
await client.execute({ sql: upsertSql, args: upsertArgs });
|
|
191
|
+
const { rows } = await client.execute({
|
|
192
|
+
sql: `SELECT id FROM app_secrets WHERE scope = ? AND scope_id = ? AND key = ? LIMIT 1`,
|
|
193
|
+
args: [scope, scopeId, key],
|
|
174
194
|
});
|
|
175
|
-
return id;
|
|
195
|
+
return String(rows[0]?.id ?? id);
|
|
176
196
|
}
|
|
177
197
|
|
|
178
198
|
export interface ReadSecretResult {
|
|
@@ -606,6 +606,10 @@ export async function mergeCoreSharingActions(
|
|
|
606
606
|
() => import("../audit/actions/list-audit-events.js"),
|
|
607
607
|
],
|
|
608
608
|
["get-audit-event", () => import("../audit/actions/get-audit-event.js")],
|
|
609
|
+
[
|
|
610
|
+
"export-audit-events",
|
|
611
|
+
() => import("../audit/actions/export-audit-events.js"),
|
|
612
|
+
],
|
|
609
613
|
// History kit — reusable version snapshots and restore surface.
|
|
610
614
|
[
|
|
611
615
|
"create-resource-version",
|