@agent-native/core 0.96.0 → 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 +39 -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 +9 -2
- package/corpus/core/src/a2a/auth-policy.ts +33 -0
- package/corpus/core/src/agent/durable-background.ts +15 -7
- package/corpus/core/src/agent/run-store.ts +13 -4
- 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/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/agent-chat-adapter.ts +9 -9
- 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/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/secrets/storage.ts +47 -27
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +54 -12
- package/corpus/core/src/server/core-routes-plugin.ts +30 -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 +9 -0
- package/corpus/core/src/server/request-origin.ts +63 -0
- package/corpus/core/src/server/transcribe-voice.ts +1 -50
- 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/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/templates/analytics/actions/compose-dashboard.ts +65 -37
- 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/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-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 +3 -2
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +154 -11
- 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/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/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/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/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/agent-chat-adapter.js +7 -3
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/collab/struct-routes.d.ts +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/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/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.map +1 -1
- package/dist/server/agent-chat-plugin.js +47 -13
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +23 -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 +9 -0
- 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/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/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/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 +10 -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/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
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared file-walking / offset helpers used by every guard in this
|
|
3
|
+
* directory. Ported (behavior-for-behavior) from the identical helpers
|
|
4
|
+
* duplicated across `scripts/guard-*.mjs` — see each guard file for the
|
|
5
|
+
* specific `scripts/guard-<name>.mjs` it was derived from.
|
|
6
|
+
*
|
|
7
|
+
* The original scripts walk directories with `fs/promises` async
|
|
8
|
+
* generators. Guard functions here are synchronous pure functions (per the
|
|
9
|
+
* plan's `scan<Name>(options): GuardResult` contract), so this module uses
|
|
10
|
+
* `fs.readdirSync` instead — same skip-dir semantics, no behavior change.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
|
|
16
|
+
/** Directories skipped by every guard's repo/app walk. */
|
|
17
|
+
export const DEFAULT_SKIP_DIRS = new Set([
|
|
18
|
+
"node_modules",
|
|
19
|
+
".git",
|
|
20
|
+
"dist",
|
|
21
|
+
"build",
|
|
22
|
+
".next",
|
|
23
|
+
".nuxt",
|
|
24
|
+
".output",
|
|
25
|
+
".cache",
|
|
26
|
+
".turbo",
|
|
27
|
+
".netlify",
|
|
28
|
+
".vercel",
|
|
29
|
+
".wrangler",
|
|
30
|
+
".react-router",
|
|
31
|
+
".generated",
|
|
32
|
+
".claude",
|
|
33
|
+
"out",
|
|
34
|
+
"coverage",
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
/** Recursively walk `dir`, yielding absolute file paths. Skips any
|
|
38
|
+
* directory whose basename is in `skipDirs`. */
|
|
39
|
+
export function* walk(
|
|
40
|
+
dir: string,
|
|
41
|
+
skipDirs: Set<string> = DEFAULT_SKIP_DIRS,
|
|
42
|
+
): Generator<string> {
|
|
43
|
+
let entries: fs.Dirent[];
|
|
44
|
+
try {
|
|
45
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
46
|
+
} catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
const full = path.join(dir, entry.name);
|
|
51
|
+
if (entry.isDirectory()) {
|
|
52
|
+
if (skipDirs.has(entry.name)) continue;
|
|
53
|
+
yield* walk(full, skipDirs);
|
|
54
|
+
} else if (entry.isFile()) {
|
|
55
|
+
yield full;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function lineColForOffset(
|
|
61
|
+
contents: string,
|
|
62
|
+
offset: number,
|
|
63
|
+
): { line: number; col: number } {
|
|
64
|
+
let line = 1;
|
|
65
|
+
let lineStart = 0;
|
|
66
|
+
for (let i = 0; i < offset; i++) {
|
|
67
|
+
if (contents.charCodeAt(i) === 10) {
|
|
68
|
+
line++;
|
|
69
|
+
lineStart = i + 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { line, col: offset - lineStart + 1 };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function isCommentLine(lineText: string): boolean {
|
|
76
|
+
const trimmed = lineText.trimStart();
|
|
77
|
+
return (
|
|
78
|
+
trimmed.startsWith("*") ||
|
|
79
|
+
trimmed.startsWith("//") ||
|
|
80
|
+
trimmed.startsWith("/*")
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function relPosix(root: string, file: string): string {
|
|
85
|
+
return path.relative(root, file).replaceAll("\\", "/");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function readFileSafe(file: string): string | null {
|
|
89
|
+
try {
|
|
90
|
+
return fs.readFileSync(file, "utf8");
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** True if `rel` matches an exact-path entry in `extraExemptPaths`
|
|
97
|
+
* (posix-relative, exact string match — the same shape the exempt-path
|
|
98
|
+
* sets used in the original monorepo guards). */
|
|
99
|
+
export function isExtraExempt(
|
|
100
|
+
rel: string,
|
|
101
|
+
extraExemptPaths: string[] | undefined,
|
|
102
|
+
): boolean {
|
|
103
|
+
if (!extraExemptPaths || extraExemptPaths.length === 0) return false;
|
|
104
|
+
return extraExemptPaths.includes(rel);
|
|
105
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for `agent-native doctor`'s guard functions.
|
|
3
|
+
*
|
|
4
|
+
* Each guard is a pure function `scan<Name>(options): GuardResult` that
|
|
5
|
+
* scans a single app root and returns a flat list of findings. Guards do
|
|
6
|
+
* not print, exit, or read `agent-native.json` themselves — that
|
|
7
|
+
* orchestration lives in `../cli/doctor.ts` so the functions stay pure and
|
|
8
|
+
* independently testable/importable (see the `./guards` package export).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface GuardFinding {
|
|
12
|
+
/** Path to the offending file, relative to the scanned root. */
|
|
13
|
+
file: string;
|
|
14
|
+
/** 1-based line number of the offending line. */
|
|
15
|
+
line: number;
|
|
16
|
+
/** Human-readable explanation of the finding. */
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface GuardResult {
|
|
21
|
+
/** Stable guard identifier, e.g. "no-drizzle-push". Matches the name used
|
|
22
|
+
* by `--only` and `agent-native.json`'s `doctor.disabledGuards`. */
|
|
23
|
+
name: string;
|
|
24
|
+
findings: GuardFinding[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface GuardScanOptions {
|
|
28
|
+
/** Absolute path to the app root to scan. */
|
|
29
|
+
root: string;
|
|
30
|
+
}
|
|
@@ -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({
|
|
@@ -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",
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getMethod,
|
|
9
9
|
getQuery,
|
|
10
10
|
getHeader,
|
|
11
|
+
getRequestIP,
|
|
11
12
|
type H3Event,
|
|
12
13
|
} from "h3";
|
|
13
14
|
|
|
@@ -19,7 +20,8 @@ import {
|
|
|
19
20
|
} from "../a2a/artifact-response.js";
|
|
20
21
|
import {
|
|
21
22
|
hasConfiguredA2ASecret,
|
|
22
|
-
|
|
23
|
+
isLoopbackAddress,
|
|
24
|
+
isTrustedLocalRuntime,
|
|
23
25
|
} from "../a2a/auth-policy.js";
|
|
24
26
|
import {
|
|
25
27
|
applyAgentTextEventToBuffer,
|
|
@@ -1065,6 +1067,10 @@ function appStateKeyForBrowserTab(key: string, browserTabId: unknown): string {
|
|
|
1065
1067
|
*/
|
|
1066
1068
|
const generateTitleRateLimit = new Map<string, number[]>();
|
|
1067
1069
|
|
|
1070
|
+
/** Only sweep drained rate-limit entries once the map grows past this size,
|
|
1071
|
+
* so the common small-map case stays O(1). */
|
|
1072
|
+
const RATE_LIMIT_SWEEP_THRESHOLD = 1000;
|
|
1073
|
+
|
|
1068
1074
|
/**
|
|
1069
1075
|
* Creates the `set-search-params` / `set-url-path` tools. Writes a one-shot
|
|
1070
1076
|
* URL command to application_state; the client's URLSync component applies
|
|
@@ -6551,12 +6557,17 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
6551
6557
|
setResponseStatus(event, 401);
|
|
6552
6558
|
return { error: "Invalid or expired processor token" };
|
|
6553
6559
|
}
|
|
6554
|
-
} else
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
+
} else {
|
|
6561
|
+
const loopback = isLoopbackAddress(
|
|
6562
|
+
getRequestIP(event, { xForwardedFor: false }),
|
|
6563
|
+
);
|
|
6564
|
+
if (!isTrustedLocalRuntime({ loopback })) {
|
|
6565
|
+
setResponseStatus(event, 503);
|
|
6566
|
+
return {
|
|
6567
|
+
error:
|
|
6568
|
+
"Agent Teams processor not configured — set A2A_SECRET on this deployment (or A2A_ALLOW_UNSIGNED_INTERNAL=1 for trusted local dev).",
|
|
6569
|
+
};
|
|
6570
|
+
}
|
|
6560
6571
|
}
|
|
6561
6572
|
|
|
6562
6573
|
try {
|
|
@@ -7224,6 +7235,11 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7224
7235
|
setResponseHeader(event, "Content-Type", "application/x-ndjson");
|
|
7225
7236
|
setResponseHeader(event, "Cache-Control", "no-cache");
|
|
7226
7237
|
|
|
7238
|
+
// Lets `cancel()` signal in-flight source work to stop early on
|
|
7239
|
+
// client disconnect, instead of only being noticed the next time
|
|
7240
|
+
// `flush()`'s `controller.enqueue` throws.
|
|
7241
|
+
const mentionsAbort = new AbortController();
|
|
7242
|
+
|
|
7227
7243
|
const stream = new ReadableStream({
|
|
7228
7244
|
start(controller) {
|
|
7229
7245
|
return runWithRequestContext(
|
|
@@ -7235,7 +7251,9 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7235
7251
|
);
|
|
7236
7252
|
},
|
|
7237
7253
|
cancel() {
|
|
7238
|
-
// Client disconnected — stop enqueuing
|
|
7254
|
+
// Client disconnected — stop enqueuing and let in-flight source
|
|
7255
|
+
// work short-circuit instead of running to completion unseen.
|
|
7256
|
+
mentionsAbort.abort();
|
|
7239
7257
|
},
|
|
7240
7258
|
});
|
|
7241
7259
|
|
|
@@ -7246,10 +7264,13 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7246
7264
|
) {
|
|
7247
7265
|
const MAX_RESULTS = 50;
|
|
7248
7266
|
let totalSent = 0;
|
|
7249
|
-
let cancelled =
|
|
7267
|
+
let cancelled = mentionsAbort.signal.aborted;
|
|
7250
7268
|
|
|
7251
7269
|
const flush = (batch: MentionItemResponse[]) => {
|
|
7252
|
-
if (cancelled)
|
|
7270
|
+
if (cancelled || mentionsAbort.signal.aborted) {
|
|
7271
|
+
cancelled = true;
|
|
7272
|
+
return;
|
|
7273
|
+
}
|
|
7253
7274
|
const filtered = batch.filter(matchesQuery);
|
|
7254
7275
|
if (filtered.length === 0) return;
|
|
7255
7276
|
const remaining = MAX_RESULTS - totalSent;
|
|
@@ -7332,6 +7353,8 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7332
7353
|
|
|
7333
7354
|
// 3. Custom mention providers (each flushes independently)
|
|
7334
7355
|
for (const [key, provider] of Object.entries(mentionProviders)) {
|
|
7356
|
+
// Client already disconnected — don't spawn more provider work.
|
|
7357
|
+
if (mentionsAbort.signal.aborted) break;
|
|
7335
7358
|
sources.push(
|
|
7336
7359
|
(async () => {
|
|
7337
7360
|
try {
|
|
@@ -7419,7 +7442,9 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7419
7442
|
);
|
|
7420
7443
|
|
|
7421
7444
|
await Promise.all(sources);
|
|
7422
|
-
if (!cancelled
|
|
7445
|
+
if (!cancelled && !mentionsAbort.signal.aborted) {
|
|
7446
|
+
controller.close();
|
|
7447
|
+
}
|
|
7423
7448
|
}
|
|
7424
7449
|
}),
|
|
7425
7450
|
);
|
|
@@ -7450,6 +7475,19 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
7450
7475
|
recent.push(now);
|
|
7451
7476
|
generateTitleRateLimit.set(ownerEmail, recent);
|
|
7452
7477
|
|
|
7478
|
+
// Opportunistic eviction: keep the map from growing unbounded by
|
|
7479
|
+
// the count of distinct users on a long-lived process. Drop any
|
|
7480
|
+
// key whose window has fully drained (no timestamps within the
|
|
7481
|
+
// rolling window).
|
|
7482
|
+
if (generateTitleRateLimit.size > RATE_LIMIT_SWEEP_THRESHOLD) {
|
|
7483
|
+
for (const [email, times] of generateTitleRateLimit) {
|
|
7484
|
+
if (email === ownerEmail) continue;
|
|
7485
|
+
if (times.every((t) => now - t >= limitWindowMs)) {
|
|
7486
|
+
generateTitleRateLimit.delete(email);
|
|
7487
|
+
}
|
|
7488
|
+
}
|
|
7489
|
+
}
|
|
7490
|
+
|
|
7453
7491
|
const body = await readBody(event);
|
|
7454
7492
|
const message = body?.message;
|
|
7455
7493
|
if (!message || typeof message !== "string") {
|
|
@@ -8438,10 +8476,14 @@ Non-code requests are still fine on this surface: read data, navigate the UI, su
|
|
|
8438
8476
|
|
|
8439
8477
|
// Validate + HMAC-authenticate the self-dispatch and prepare the
|
|
8440
8478
|
// background-worker body. Pure decision (unit-tested in
|
|
8441
|
-
// durable-background.spec.ts); the route only wires it to h3.
|
|
8479
|
+
// durable-background.spec.ts); the route only wires it to h3. This
|
|
8480
|
+
// handler DOES have the h3 event, so it can see the real socket
|
|
8481
|
+
// peer — thread the loopback signal through so unsigned local-dev
|
|
8482
|
+
// self-dispatch (no A2A_SECRET) still works over 127.0.0.1/::1.
|
|
8442
8483
|
const prepared = prepareProcessRunRequest(
|
|
8443
8484
|
processBody,
|
|
8444
8485
|
getHeader(event, "authorization"),
|
|
8486
|
+
isLoopbackAddress(getRequestIP(event, { xForwardedFor: false })),
|
|
8445
8487
|
);
|
|
8446
8488
|
if (!prepared.ok) {
|
|
8447
8489
|
// DIAGNOSTIC: record the auth/validation failure ONTO the run
|