@agent-native/core 0.100.2 → 0.101.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +65 -0
- package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +7 -0
- package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +7 -0
- package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +14 -0
- package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +7 -0
- package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +7 -0
- package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +7 -0
- package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +7 -0
- package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +14 -0
- package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +7 -0
- package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +48 -30
- package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +7 -0
- package/corpus/core/docs/content/pr-visual-recap.mdx +50 -32
- package/corpus/core/docs/content/template-plan.mdx +14 -0
- package/corpus/core/docs/content/toolkit-comments-review.mdx +16 -2
- package/corpus/core/package.json +4 -1
- package/corpus/core/src/a2a/index.ts +2 -1
- package/corpus/core/src/a2a/server.ts +34 -9
- package/corpus/core/src/agent/production-agent.ts +56 -10
- package/corpus/core/src/cli/create.ts +47 -19
- package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -3
- package/corpus/core/src/cli/recap.ts +3 -4932
- package/corpus/core/src/cli/skills-content/visual-plan-skill.ts +6 -4
- package/corpus/core/src/cli/skills-content/visual-recap-skill.ts +7 -1
- package/corpus/core/src/client/AgentPanel.tsx +19 -17
- package/corpus/core/src/client/analytics.ts +8 -1
- package/corpus/core/src/client/chat/markdown-renderer.tsx +54 -8
- package/corpus/core/src/client/chat/message-components.tsx +8 -2
- package/corpus/core/src/client/clipboard.ts +28 -1
- package/corpus/core/src/client/guided-questions.tsx +4 -0
- package/corpus/core/src/client/i18n.tsx +2 -1
- package/corpus/core/src/client/index.ts +4 -0
- package/corpus/core/src/client/review/ReviewCommentComposer.tsx +108 -0
- package/corpus/core/src/client/review/ReviewThreadPanel.tsx +482 -150
- package/corpus/core/src/client/review/index.ts +8 -0
- package/corpus/core/src/client/review/use-review.ts +35 -1
- package/corpus/core/src/client/use-agent-chat-context.ts +3 -2
- package/corpus/core/src/review/actions/create-review-comment.ts +2 -1
- package/corpus/core/src/review/actions/get-review-feedback.ts +6 -11
- package/corpus/core/src/review/actions/list-review-comments.ts +38 -4
- package/corpus/core/src/review/actions/reply-review-comment.ts +33 -22
- package/corpus/core/src/review/actions/resolve-review-thread.ts +29 -2
- package/corpus/core/src/review/actions/send-review-thread-to-agent.ts +62 -0
- package/corpus/core/src/review/identity.ts +101 -0
- package/corpus/core/src/review/index.ts +14 -0
- package/corpus/core/src/review/store.ts +315 -8
- package/corpus/core/src/review/types.ts +4 -0
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/action-routes.ts +125 -4
- package/corpus/core/src/server/agent-chat/plugin-options.ts +16 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +1 -0
- package/corpus/core/src/server/index.ts +8 -0
- package/corpus/core/src/vite/action-types-plugin.ts +4 -0
- package/corpus/templates/analytics/changelog/2026-07-14-daily-dashboard-email-captures-authenticate-with-a-sessi.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +156 -3
- package/corpus/templates/assets/app/components/generation/GenerationResults.tsx +402 -142
- package/corpus/templates/assets/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/assets/app/i18n-data.ts +1 -0
- package/corpus/templates/assets/app/routes/library.tsx +187 -48
- package/corpus/templates/assets/changelog/2026-07-13-merged-the-candidates-panel-into-the-library-as-a-drafts-tab.md +6 -0
- package/corpus/templates/chat/app/lib/agent-page.tsx +40 -0
- package/corpus/templates/chat/app/routes/agent.tsx +5 -3
- package/corpus/templates/chat/changelog/2026-07-14-fixed-chat-template-startup-with-older-core-versions.md +6 -0
- package/corpus/templates/clips/.agents/skills/ai-video-tools/SKILL.md +5 -0
- package/corpus/templates/clips/AGENTS.md +4 -0
- package/corpus/templates/clips/actions/get-feature-flags.ts +30 -0
- package/corpus/templates/clips/actions/lib/transcript-preview.ts +48 -0
- package/corpus/templates/clips/actions/request-transcript.ts +41 -1
- package/corpus/templates/clips/actions/view-screen.ts +5 -9
- package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
- package/corpus/templates/clips/app/components/player/video-player.tsx +39 -1
- package/corpus/templates/clips/app/hooks/use-desktop-promo.ts +6 -12
- package/corpus/templates/clips/app/hooks/use-mse-video-source.ts +146 -0
- package/corpus/templates/clips/app/lib/capture-install-options.ts +21 -0
- package/corpus/templates/clips/app/lib/fmp4.ts +271 -0
- package/corpus/templates/clips/app/lib/mse-video-loader.ts +585 -0
- package/corpus/templates/clips/app/routes/_app.dictate.tsx +0 -4
- package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +5 -13
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +2 -37
- package/corpus/templates/clips/app/routes/download.tsx +3 -2
- package/corpus/templates/clips/changelog/2026-07-10-shared-clips-now-start-playing-instantly-instead-of-showing-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-14-clips-now-distinguishes-transcript-previews-from-incomplete-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-14-desktop-app-prompts-stay-hidden-after-you-download-the-insta.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +5 -0
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +10 -0
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +2518 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen/live_upload.rs +353 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +558 -67
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +11 -1
- package/corpus/templates/clips/desktop/src-tauri/src/remote_flags.rs +188 -0
- package/corpus/templates/clips/desktop/src-tauri/src/whisper_speech.rs +1 -1
- package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +1 -0
- package/corpus/templates/clips/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +5 -1
- package/corpus/templates/clips/shared/feature-flags.ts +41 -0
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +3 -0
- package/corpus/templates/design/.agents/skills/design-review-feedback/SKILL.md +42 -0
- package/corpus/templates/design/AGENTS.md +6 -0
- package/corpus/templates/design/README.md +1 -0
- package/corpus/templates/design/actions/navigate.ts +4 -4
- package/corpus/templates/design/actions/view-screen.ts +116 -2
- package/corpus/templates/design/agent-native.app-skill.json +5 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +52 -59
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +13 -9
- package/corpus/templates/design/app/components/design/EditPanel.tsx +79 -11
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +78 -65
- package/corpus/templates/design/app/components/design/ReviewCommentsPanel.tsx +161 -0
- package/corpus/templates/design/app/components/design/ReviewStatusControl.tsx +96 -0
- package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +166 -0
- package/corpus/templates/design/app/components/layout/Layout.tsx +1 -0
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +1077 -0
- package/corpus/templates/design/app/components/visual-editor/index.ts +1 -0
- package/corpus/templates/design/app/components/visual-editor/review-canvas-state.ts +42 -0
- package/corpus/templates/design/app/hooks/use-design-systems.ts +2 -2
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +9 -6
- package/corpus/templates/design/app/hooks/use-question-flow.ts +7 -1
- package/corpus/templates/design/app/i18n/ar-SA.ts +56 -1
- package/corpus/templates/design/app/i18n/de-DE.ts +57 -1
- package/corpus/templates/design/app/i18n/en-US.ts +56 -1
- package/corpus/templates/design/app/i18n/es-ES.ts +56 -1
- package/corpus/templates/design/app/i18n/fr-FR.ts +57 -1
- package/corpus/templates/design/app/i18n/hi-IN.ts +56 -1
- package/corpus/templates/design/app/i18n/ja-JP.ts +56 -1
- package/corpus/templates/design/app/i18n/ko-KR.ts +56 -1
- package/corpus/templates/design/app/i18n/pt-BR.ts +57 -1
- package/corpus/templates/design/app/i18n/zh-CN.ts +56 -1
- package/corpus/templates/design/app/i18n/zh-TW.ts +51 -0
- package/corpus/templates/design/app/pages/Present.tsx +172 -14
- package/corpus/templates/design/app/pages/design-editor/overview-camera.ts +23 -0
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +10 -0
- package/corpus/templates/design/app/pages/present-review-state.ts +18 -0
- package/corpus/templates/design/app/public-routes.ts +8 -0
- package/corpus/templates/design/app/root.tsx +7 -6
- package/corpus/templates/design/changelog/2026-07-13-dragged-design-assets-now-appear-at-the-visible-drop-locatio.md +6 -0
- package/corpus/templates/design/changelog/2026-07-13-reviewers-can-pin-comments-on-shared-designs-and-apply-verif.md +6 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +10 -0
- package/corpus/templates/design/server/plugins/auth.ts +7 -5
- package/corpus/templates/design/server/plugins/review.ts +9 -0
- package/corpus/templates/design/shared/review-anchor.ts +83 -0
- package/corpus/templates/design/shared/review-summary.ts +25 -0
- package/corpus/templates/forms/.agents/skills/form-publishing/SKILL.md +2 -0
- package/corpus/templates/forms/AGENTS.md +4 -0
- package/corpus/templates/forms/actions/create-form.ts +2 -1
- package/corpus/templates/forms/actions/update-form.ts +5 -2
- package/corpus/templates/forms/app/i18n/ar-SA.ts +3 -0
- package/corpus/templates/forms/app/i18n/de-DE.ts +3 -0
- package/corpus/templates/forms/app/i18n/en-US.ts +3 -0
- package/corpus/templates/forms/app/i18n/es-ES.ts +3 -0
- package/corpus/templates/forms/app/i18n/fr-FR.ts +3 -0
- package/corpus/templates/forms/app/i18n/hi-IN.ts +3 -0
- package/corpus/templates/forms/app/i18n/ja-JP.ts +3 -0
- package/corpus/templates/forms/app/i18n/ko-KR.ts +3 -0
- package/corpus/templates/forms/app/i18n/pt-BR.ts +3 -0
- package/corpus/templates/forms/app/i18n/zh-CN.ts +3 -0
- package/corpus/templates/forms/app/i18n/zh-TW.ts +3 -0
- package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +19 -0
- package/corpus/templates/forms/changelog/2026-07-14-fixed-the-ask-forms-loading-input-appearing-above-the-welcom.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-14-form-social-previews-now-show-the-form-title-description-and.md +6 -0
- package/corpus/templates/forms/changelog/2026-07-14-forms-can-email-the-form-owner-when-new-responses-arrive.md +6 -0
- package/corpus/templates/forms/server/handlers/submissions.ts +17 -0
- package/corpus/templates/forms/server/lib/form-og-image.ts +64 -6
- package/corpus/templates/forms/server/lib/public-form-ssr.ts +6 -2
- package/corpus/templates/forms/server/lib/response-email.ts +65 -0
- package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -0
- package/corpus/templates/forms/server/routes/api/forms/og/[...slug]/og.png.get.ts +184 -0
- package/corpus/templates/forms/shared/types.ts +2 -0
- package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +6 -4
- package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +7 -1
- package/dist/a2a/index.d.ts +2 -1
- package/dist/a2a/index.d.ts.map +1 -1
- package/dist/a2a/index.js +1 -1
- package/dist/a2a/index.js.map +1 -1
- package/dist/a2a/server.d.ts +25 -0
- package/dist/a2a/server.d.ts.map +1 -1
- package/dist/a2a/server.js +30 -6
- package/dist/a2a/server.js.map +1 -1
- package/dist/agent/production-agent.d.ts +2 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +37 -12
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/cli/create.d.ts +5 -3
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +44 -19
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +2 -2
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +2 -2
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts +3 -562
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +3 -3875
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
- package/dist/cli/skills-content/visual-plan-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/visual-plan-skill.js +6 -4
- package/dist/cli/skills-content/visual-plan-skill.js.map +1 -1
- package/dist/cli/skills-content/visual-recap-skill.d.ts +1 -1
- package/dist/cli/skills-content/visual-recap-skill.d.ts.map +1 -1
- package/dist/cli/skills-content/visual-recap-skill.js +7 -1
- package/dist/cli/skills-content/visual-recap-skill.js.map +1 -1
- 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/analytics.d.ts +5 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +3 -1
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/chat/markdown-renderer.d.ts +1 -0
- package/dist/client/chat/markdown-renderer.d.ts.map +1 -1
- package/dist/client/chat/markdown-renderer.js +36 -1
- package/dist/client/chat/markdown-renderer.js.map +1 -1
- package/dist/client/chat/message-components.d.ts.map +1 -1
- package/dist/client/chat/message-components.js +5 -2
- package/dist/client/chat/message-components.js.map +1 -1
- package/dist/client/clipboard.d.ts +3 -1
- package/dist/client/clipboard.d.ts.map +1 -1
- package/dist/client/clipboard.js +24 -1
- package/dist/client/clipboard.js.map +1 -1
- package/dist/client/guided-questions.d.ts +3 -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/i18n.d.ts.map +1 -1
- package/dist/client/i18n.js +3 -1
- package/dist/client/i18n.js.map +1 -1
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/review/ReviewCommentComposer.d.ts +18 -0
- package/dist/client/review/ReviewCommentComposer.d.ts.map +1 -0
- package/dist/client/review/ReviewCommentComposer.js +30 -0
- package/dist/client/review/ReviewCommentComposer.js.map +1 -0
- package/dist/client/review/ReviewThreadPanel.d.ts +28 -1
- package/dist/client/review/ReviewThreadPanel.d.ts.map +1 -1
- package/dist/client/review/ReviewThreadPanel.js +151 -39
- package/dist/client/review/ReviewThreadPanel.js.map +1 -1
- package/dist/client/review/index.d.ts +3 -2
- package/dist/client/review/index.d.ts.map +1 -1
- package/dist/client/review/index.js +2 -1
- package/dist/client/review/index.js.map +1 -1
- package/dist/client/review/use-review.d.ts +23 -0
- package/dist/client/review/use-review.d.ts.map +1 -1
- package/dist/client/review/use-review.js +3 -0
- package/dist/client/review/use-review.js.map +1 -1
- package/dist/client/use-agent-chat-context.d.ts +1 -1
- package/dist/client/use-agent-chat-context.d.ts.map +1 -1
- package/dist/client/use-agent-chat-context.js +4 -2
- package/dist/client/use-agent-chat-context.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/review/actions/create-review-comment.js +2 -1
- package/dist/review/actions/create-review-comment.js.map +1 -1
- package/dist/review/actions/get-review-feedback.js +6 -11
- package/dist/review/actions/get-review-feedback.js.map +1 -1
- package/dist/review/actions/list-review-comments.d.ts +1 -0
- package/dist/review/actions/list-review-comments.js +25 -4
- package/dist/review/actions/list-review-comments.js.map +1 -1
- package/dist/review/actions/reply-review-comment.js +12 -5
- package/dist/review/actions/reply-review-comment.js.map +1 -1
- package/dist/review/actions/resolve-review-thread.d.ts +3 -0
- package/dist/review/actions/resolve-review-thread.js +20 -3
- package/dist/review/actions/resolve-review-thread.js.map +1 -1
- package/dist/review/actions/send-review-thread-to-agent.d.ts +17 -0
- package/dist/review/actions/send-review-thread-to-agent.d.ts.map +1 -0
- package/dist/review/actions/send-review-thread-to-agent.js +49 -0
- package/dist/review/actions/send-review-thread-to-agent.js.map +1 -0
- package/dist/review/identity.d.ts +6 -0
- package/dist/review/identity.d.ts.map +1 -0
- package/dist/review/identity.js +68 -0
- package/dist/review/identity.js.map +1 -0
- package/dist/review/index.d.ts +3 -1
- package/dist/review/index.d.ts.map +1 -1
- package/dist/review/index.js +2 -1
- package/dist/review/index.js.map +1 -1
- package/dist/review/store.d.ts +36 -0
- package/dist/review/store.d.ts.map +1 -1
- package/dist/review/store.js +223 -8
- package/dist/review/store.js.map +1 -1
- package/dist/review/types.d.ts +4 -0
- package/dist/review/types.d.ts.map +1 -1
- package/dist/review/types.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- 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/action-routes.d.ts +45 -0
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +71 -5
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-chat/plugin-options.d.ts +16 -0
- package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
- package/dist/server/agent-chat/plugin-options.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +1 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/server/index.js.map +1 -1
- package/dist/vite/action-types-plugin.d.ts.map +1 -1
- package/dist/vite/action-types-plugin.js +4 -0
- package/dist/vite/action-types-plugin.js.map +1 -1
- package/docs/content/locales/ar-SA/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/ar-SA/template-plan.mdx +7 -0
- package/docs/content/locales/de-DE/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/de-DE/template-plan.mdx +7 -0
- package/docs/content/locales/es-ES/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/es-ES/template-plan.mdx +14 -0
- package/docs/content/locales/fr-FR/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/fr-FR/template-plan.mdx +7 -0
- package/docs/content/locales/hi-IN/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/hi-IN/template-plan.mdx +7 -0
- package/docs/content/locales/ja-JP/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/ja-JP/template-plan.mdx +7 -0
- package/docs/content/locales/ko-KR/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/ko-KR/template-plan.mdx +7 -0
- package/docs/content/locales/pt-BR/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/pt-BR/template-plan.mdx +14 -0
- package/docs/content/locales/zh-CN/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/zh-CN/template-plan.mdx +7 -0
- package/docs/content/locales/zh-TW/pr-visual-recap.mdx +48 -30
- package/docs/content/locales/zh-TW/template-plan.mdx +7 -0
- package/docs/content/pr-visual-recap.mdx +50 -32
- package/docs/content/template-plan.mdx +14 -0
- package/docs/content/toolkit-comments-review.mdx +16 -2
- package/package.json +4 -1
- package/corpus/templates/forms/server/routes/api/forms/og/[slug]/og.png.get.ts +0 -45
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getDbExec, isPostgres } from "../db/client.js";
|
|
1
|
+
import { getDbExec, isPostgres, type DbExec } from "../db/client.js";
|
|
2
2
|
import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
3
3
|
import type { Visibility } from "../sharing/schema.js";
|
|
4
4
|
import type {
|
|
@@ -43,9 +43,25 @@ export interface QueryReviewCommentsInput {
|
|
|
43
43
|
includeResolved?: boolean;
|
|
44
44
|
includeDeleted?: boolean;
|
|
45
45
|
targetId?: string | null;
|
|
46
|
+
rootOnly?: boolean;
|
|
47
|
+
resolutionTargets?: readonly (ReviewResolutionTarget | null)[];
|
|
48
|
+
unconsumedOnly?: boolean;
|
|
46
49
|
limit?: number;
|
|
47
50
|
}
|
|
48
51
|
|
|
52
|
+
export interface GetReviewThreadSummaryInput {
|
|
53
|
+
resourceType: string;
|
|
54
|
+
resourceId: string;
|
|
55
|
+
scope: ReviewScope;
|
|
56
|
+
bypassScope?: boolean;
|
|
57
|
+
targetId?: string | null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ReviewThreadSummary {
|
|
61
|
+
openCount: number;
|
|
62
|
+
agentQueueCount: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
49
65
|
export interface UpsertReviewStatusInput {
|
|
50
66
|
resourceType: string;
|
|
51
67
|
resourceId: string;
|
|
@@ -112,6 +128,15 @@ export async function ensureReviewTables(): Promise<void> {
|
|
|
112
128
|
ON agent_review_comments (owner_email, created_at)`,
|
|
113
129
|
`CREATE INDEX IF NOT EXISTS idx_agent_review_comments_org
|
|
114
130
|
ON agent_review_comments (org_id, created_at)`,
|
|
131
|
+
`CREATE INDEX IF NOT EXISTS idx_agent_review_comments_queue
|
|
132
|
+
ON agent_review_comments (
|
|
133
|
+
resource_type,
|
|
134
|
+
resource_id,
|
|
135
|
+
parent_comment_id,
|
|
136
|
+
resolution_target,
|
|
137
|
+
consumed_at,
|
|
138
|
+
created_at
|
|
139
|
+
)`,
|
|
115
140
|
`CREATE INDEX IF NOT EXISTS idx_agent_review_statuses_resource
|
|
116
141
|
ON agent_review_statuses (resource_type, resource_id)`,
|
|
117
142
|
];
|
|
@@ -126,9 +151,10 @@ export async function ensureReviewTables(): Promise<void> {
|
|
|
126
151
|
await ensureIndexExists("idx_agent_review_comments_thread", indexes[1]);
|
|
127
152
|
await ensureIndexExists("idx_agent_review_comments_owner", indexes[2]);
|
|
128
153
|
await ensureIndexExists("idx_agent_review_comments_org", indexes[3]);
|
|
154
|
+
await ensureIndexExists("idx_agent_review_comments_queue", indexes[4]);
|
|
129
155
|
await ensureIndexExists(
|
|
130
156
|
"idx_agent_review_statuses_resource",
|
|
131
|
-
indexes[
|
|
157
|
+
indexes[5],
|
|
132
158
|
);
|
|
133
159
|
} else {
|
|
134
160
|
await client.execute(createCommentsSql);
|
|
@@ -145,9 +171,62 @@ export async function ensureReviewTables(): Promise<void> {
|
|
|
145
171
|
|
|
146
172
|
export async function insertReviewComment(
|
|
147
173
|
input: InsertReviewCommentInput,
|
|
174
|
+
): Promise<ReviewComment> {
|
|
175
|
+
await ensureReviewTables();
|
|
176
|
+
return insertReviewCommentWithClient(input, getDbExec());
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function insertReviewReply(
|
|
180
|
+
input: InsertReviewCommentInput & {
|
|
181
|
+
threadId: string;
|
|
182
|
+
parentCommentId: string;
|
|
183
|
+
},
|
|
184
|
+
routeTarget: ReviewResolutionTarget | null,
|
|
185
|
+
resource: { resourceType: string; resourceId: string },
|
|
148
186
|
): Promise<ReviewComment> {
|
|
149
187
|
await ensureReviewTables();
|
|
150
188
|
const client = getDbExec();
|
|
189
|
+
const insertAndRoute = async (tx: DbExec) => {
|
|
190
|
+
if (routeTarget) {
|
|
191
|
+
const routedCount = await routeReviewThreadWithClient(
|
|
192
|
+
tx,
|
|
193
|
+
input.threadId,
|
|
194
|
+
routeTarget,
|
|
195
|
+
resource,
|
|
196
|
+
);
|
|
197
|
+
if (routedCount < 1) {
|
|
198
|
+
throw new Error("Open review thread not found");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return insertReviewCommentWithClient(input, tx);
|
|
202
|
+
};
|
|
203
|
+
if (client.transaction) return client.transaction(insertAndRoute);
|
|
204
|
+
|
|
205
|
+
const reply = await insertReviewCommentWithClient(input, client);
|
|
206
|
+
if (!routeTarget) return reply;
|
|
207
|
+
try {
|
|
208
|
+
const routedCount = await routeReviewThreadWithClient(
|
|
209
|
+
client,
|
|
210
|
+
input.threadId,
|
|
211
|
+
routeTarget,
|
|
212
|
+
resource,
|
|
213
|
+
);
|
|
214
|
+
if (routedCount < 1) throw new Error("Open review thread not found");
|
|
215
|
+
return reply;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
await client.execute({
|
|
218
|
+
sql: `DELETE FROM agent_review_comments
|
|
219
|
+
WHERE id = ? AND resource_type = ? AND resource_id = ?`,
|
|
220
|
+
args: [reply.id, resource.resourceType, resource.resourceId],
|
|
221
|
+
});
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function insertReviewCommentWithClient(
|
|
227
|
+
input: InsertReviewCommentInput,
|
|
228
|
+
client: DbExec,
|
|
229
|
+
): Promise<ReviewComment> {
|
|
151
230
|
const id = createReviewId("comment");
|
|
152
231
|
const now = new Date().toISOString();
|
|
153
232
|
const comment: ReviewComment = {
|
|
@@ -180,6 +259,10 @@ export async function insertReviewComment(
|
|
|
180
259
|
createdAt: now,
|
|
181
260
|
updatedAt: now,
|
|
182
261
|
metadata: input.metadata ?? null,
|
|
262
|
+
resolutionNote:
|
|
263
|
+
typeof input.metadata?.resolutionNote === "string"
|
|
264
|
+
? input.metadata.resolutionNote
|
|
265
|
+
: null,
|
|
183
266
|
};
|
|
184
267
|
|
|
185
268
|
await client.execute({
|
|
@@ -273,18 +356,107 @@ export async function queryReviewComments(
|
|
|
273
356
|
filterParams.push(input.targetId);
|
|
274
357
|
}
|
|
275
358
|
}
|
|
359
|
+
if (input.rootOnly) {
|
|
360
|
+
filters.push("parent_comment_id IS NULL");
|
|
361
|
+
}
|
|
362
|
+
if (input.unconsumedOnly) {
|
|
363
|
+
filters.push("consumed_at IS NULL");
|
|
364
|
+
}
|
|
365
|
+
if (input.resolutionTargets !== undefined) {
|
|
366
|
+
const targets = Array.from(
|
|
367
|
+
new Set(
|
|
368
|
+
input.resolutionTargets.filter(
|
|
369
|
+
(target): target is ReviewResolutionTarget => target !== null,
|
|
370
|
+
),
|
|
371
|
+
),
|
|
372
|
+
);
|
|
373
|
+
const resolutionFilters: string[] = [];
|
|
374
|
+
if (targets.length > 0) {
|
|
375
|
+
resolutionFilters.push(
|
|
376
|
+
`resolution_target IN (${targets.map(() => "?").join(", ")})`,
|
|
377
|
+
);
|
|
378
|
+
filterParams.push(...targets);
|
|
379
|
+
}
|
|
380
|
+
if (input.resolutionTargets.includes(null)) {
|
|
381
|
+
resolutionFilters.push("resolution_target IS NULL");
|
|
382
|
+
}
|
|
383
|
+
filters.push(
|
|
384
|
+
resolutionFilters.length > 0
|
|
385
|
+
? `(${resolutionFilters.join(" OR ")})`
|
|
386
|
+
: "1 = 0",
|
|
387
|
+
);
|
|
388
|
+
}
|
|
276
389
|
|
|
390
|
+
const selectSql = input.rootOnly
|
|
391
|
+
? `SELECT ${commentColumns()}
|
|
392
|
+
FROM (
|
|
393
|
+
SELECT ${commentColumns()},
|
|
394
|
+
ROW_NUMBER() OVER (
|
|
395
|
+
PARTITION BY thread_id
|
|
396
|
+
ORDER BY created_at ASC, id ASC
|
|
397
|
+
) AS review_thread_rank
|
|
398
|
+
FROM agent_review_comments
|
|
399
|
+
WHERE ${filters.join(" AND ")}
|
|
400
|
+
) AS distinct_review_threads
|
|
401
|
+
WHERE review_thread_rank = 1`
|
|
402
|
+
: `SELECT ${commentColumns()}
|
|
403
|
+
FROM agent_review_comments
|
|
404
|
+
WHERE ${filters.join(" AND ")}`;
|
|
277
405
|
const result = await client.execute({
|
|
278
|
-
sql:
|
|
279
|
-
|
|
280
|
-
WHERE ${filters.join(" AND ")}
|
|
281
|
-
ORDER BY created_at ASC
|
|
406
|
+
sql: `${selectSql}
|
|
407
|
+
ORDER BY created_at ASC${input.rootOnly ? ", id ASC" : ""}
|
|
282
408
|
LIMIT ?`,
|
|
283
409
|
args: [...filterParams, clampLimit(input.limit)],
|
|
284
410
|
});
|
|
285
411
|
return (result.rows ?? []).map(mapCommentRow);
|
|
286
412
|
}
|
|
287
413
|
|
|
414
|
+
export async function getReviewThreadSummary(
|
|
415
|
+
input: GetReviewThreadSummaryInput,
|
|
416
|
+
): Promise<ReviewThreadSummary> {
|
|
417
|
+
await ensureReviewTables();
|
|
418
|
+
const { clause, params } = input.bypassScope
|
|
419
|
+
? { clause: "1 = 1", params: [] as unknown[] }
|
|
420
|
+
: scopedReviewClause(input.scope);
|
|
421
|
+
const filters = [
|
|
422
|
+
"resource_type = ?",
|
|
423
|
+
"resource_id = ?",
|
|
424
|
+
clause,
|
|
425
|
+
"parent_comment_id IS NULL",
|
|
426
|
+
"deleted_at IS NULL",
|
|
427
|
+
"status = 'open'",
|
|
428
|
+
];
|
|
429
|
+
const filterParams: unknown[] = [
|
|
430
|
+
input.resourceType,
|
|
431
|
+
input.resourceId,
|
|
432
|
+
...params,
|
|
433
|
+
];
|
|
434
|
+
if (input.targetId !== undefined) {
|
|
435
|
+
if (input.targetId === null) {
|
|
436
|
+
filters.push("target_id IS NULL");
|
|
437
|
+
} else {
|
|
438
|
+
filters.push("target_id = ?");
|
|
439
|
+
filterParams.push(input.targetId);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
const result = await getDbExec().execute({
|
|
443
|
+
sql: `SELECT COUNT(DISTINCT thread_id) AS open_count,
|
|
444
|
+
COUNT(DISTINCT CASE
|
|
445
|
+
WHEN (resolution_target IS NULL OR resolution_target <> 'human')
|
|
446
|
+
AND consumed_at IS NULL
|
|
447
|
+
THEN thread_id
|
|
448
|
+
END) AS agent_queue_count
|
|
449
|
+
FROM agent_review_comments
|
|
450
|
+
WHERE ${filters.join(" AND ")}`,
|
|
451
|
+
args: filterParams,
|
|
452
|
+
});
|
|
453
|
+
const row = result.rows?.[0];
|
|
454
|
+
return {
|
|
455
|
+
openCount: Number(row?.open_count ?? 0),
|
|
456
|
+
agentQueueCount: Number(row?.agent_queue_count ?? 0),
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
288
460
|
export async function getReviewCommentById(
|
|
289
461
|
id: string,
|
|
290
462
|
scope: ReviewScope,
|
|
@@ -306,27 +478,106 @@ export async function getReviewCommentById(
|
|
|
306
478
|
return row ? mapCommentRow(row) : null;
|
|
307
479
|
}
|
|
308
480
|
|
|
481
|
+
export async function getReviewThreadRoot(
|
|
482
|
+
threadId: string,
|
|
483
|
+
resource: { resourceType: string; resourceId: string },
|
|
484
|
+
scope: ReviewScope,
|
|
485
|
+
options: { bypassScope?: boolean } = {},
|
|
486
|
+
): Promise<ReviewComment | null> {
|
|
487
|
+
await ensureReviewTables();
|
|
488
|
+
const { clause, params } = options.bypassScope
|
|
489
|
+
? { clause: "1 = 1", params: [] as unknown[] }
|
|
490
|
+
: scopedReviewClause(scope);
|
|
491
|
+
const result = await getDbExec().execute({
|
|
492
|
+
sql: `SELECT ${commentColumns()}
|
|
493
|
+
FROM agent_review_comments
|
|
494
|
+
WHERE thread_id = ?
|
|
495
|
+
AND resource_type = ?
|
|
496
|
+
AND resource_id = ?
|
|
497
|
+
AND parent_comment_id IS NULL
|
|
498
|
+
AND ${clause}
|
|
499
|
+
ORDER BY created_at ASC, id ASC
|
|
500
|
+
LIMIT 1`,
|
|
501
|
+
args: [threadId, resource.resourceType, resource.resourceId, ...params],
|
|
502
|
+
});
|
|
503
|
+
const row = result.rows?.[0];
|
|
504
|
+
return row ? mapCommentRow(row) : null;
|
|
505
|
+
}
|
|
506
|
+
|
|
309
507
|
export async function resolveReviewThread(
|
|
310
508
|
threadId: string,
|
|
311
509
|
resolvedBy?: string | null,
|
|
312
510
|
resource?: { resourceType: string; resourceId: string },
|
|
511
|
+
resolutionNote?: string,
|
|
313
512
|
): Promise<number> {
|
|
314
513
|
await ensureReviewTables();
|
|
514
|
+
const client = getDbExec();
|
|
515
|
+
const resolve = (tx: DbExec) =>
|
|
516
|
+
resolveReviewThreadWithClient(
|
|
517
|
+
tx,
|
|
518
|
+
threadId,
|
|
519
|
+
resolvedBy,
|
|
520
|
+
resource,
|
|
521
|
+
resolutionNote,
|
|
522
|
+
);
|
|
523
|
+
return client.transaction ? client.transaction(resolve) : resolve(client);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function resolveReviewThreadWithClient(
|
|
527
|
+
client: DbExec,
|
|
528
|
+
threadId: string,
|
|
529
|
+
resolvedBy?: string | null,
|
|
530
|
+
resource?: { resourceType: string; resourceId: string },
|
|
531
|
+
resolutionNote?: string,
|
|
532
|
+
): Promise<number> {
|
|
315
533
|
const now = new Date().toISOString();
|
|
316
534
|
const resourceClause = resource
|
|
317
535
|
? "AND resource_type = ? AND resource_id = ?"
|
|
318
536
|
: "";
|
|
319
|
-
|
|
537
|
+
let rootMetadata: Record<string, unknown> | null = null;
|
|
538
|
+
if (resolutionNote !== undefined) {
|
|
539
|
+
const root = await client.execute({
|
|
540
|
+
sql: `SELECT metadata_json
|
|
541
|
+
FROM agent_review_comments
|
|
542
|
+
WHERE thread_id = ?
|
|
543
|
+
AND parent_comment_id IS NULL
|
|
544
|
+
AND deleted_at IS NULL
|
|
545
|
+
${resourceClause}
|
|
546
|
+
ORDER BY created_at ASC, id ASC
|
|
547
|
+
LIMIT 1`,
|
|
548
|
+
args: [
|
|
549
|
+
threadId,
|
|
550
|
+
...(resource ? [resource.resourceType, resource.resourceId] : []),
|
|
551
|
+
],
|
|
552
|
+
});
|
|
553
|
+
if (!root.rows?.[0]) {
|
|
554
|
+
return 0;
|
|
555
|
+
}
|
|
556
|
+
rootMetadata = {
|
|
557
|
+
...(parseObject(root.rows[0].metadata_json) ?? {}),
|
|
558
|
+
resolutionNote,
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const metadataAssignment =
|
|
563
|
+
resolutionNote === undefined
|
|
564
|
+
? ""
|
|
565
|
+
: ", metadata_json = CASE WHEN parent_comment_id IS NULL THEN ? ELSE metadata_json END";
|
|
566
|
+
const result = await client.execute({
|
|
320
567
|
sql: `UPDATE agent_review_comments
|
|
321
568
|
SET status = 'resolved',
|
|
322
569
|
resolved_by = ?,
|
|
323
570
|
resolved_at = ?,
|
|
324
571
|
updated_at = ?
|
|
572
|
+
${metadataAssignment}
|
|
325
573
|
WHERE thread_id = ? AND deleted_at IS NULL ${resourceClause}`,
|
|
326
574
|
args: [
|
|
327
575
|
resolvedBy ?? null,
|
|
328
576
|
now,
|
|
329
577
|
now,
|
|
578
|
+
...(resolutionNote === undefined
|
|
579
|
+
? []
|
|
580
|
+
: [stringifyOptionalJson(rootMetadata)]),
|
|
330
581
|
threadId,
|
|
331
582
|
...(resource ? [resource.resourceType, resource.resourceId] : []),
|
|
332
583
|
],
|
|
@@ -334,6 +585,57 @@ export async function resolveReviewThread(
|
|
|
334
585
|
return result.rowsAffected ?? 0;
|
|
335
586
|
}
|
|
336
587
|
|
|
588
|
+
export async function routeReviewThread(
|
|
589
|
+
threadId: string,
|
|
590
|
+
resolutionTarget: ReviewResolutionTarget,
|
|
591
|
+
resource: { resourceType: string; resourceId: string },
|
|
592
|
+
): Promise<number> {
|
|
593
|
+
await ensureReviewTables();
|
|
594
|
+
return routeReviewThreadWithClient(
|
|
595
|
+
getDbExec(),
|
|
596
|
+
threadId,
|
|
597
|
+
resolutionTarget,
|
|
598
|
+
resource,
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
async function routeReviewThreadWithClient(
|
|
603
|
+
client: DbExec,
|
|
604
|
+
threadId: string,
|
|
605
|
+
resolutionTarget: ReviewResolutionTarget,
|
|
606
|
+
resource: { resourceType: string; resourceId: string },
|
|
607
|
+
): Promise<number> {
|
|
608
|
+
const now = new Date().toISOString();
|
|
609
|
+
const result = await client.execute({
|
|
610
|
+
sql: `UPDATE agent_review_comments
|
|
611
|
+
SET resolution_target = ?,
|
|
612
|
+
consumed_at = CASE WHEN ? = 'agent' THEN NULL ELSE consumed_at END,
|
|
613
|
+
updated_at = ?
|
|
614
|
+
WHERE thread_id = ?
|
|
615
|
+
AND resource_type = ?
|
|
616
|
+
AND resource_id = ?
|
|
617
|
+
AND parent_comment_id IS NULL
|
|
618
|
+
AND status = 'open'
|
|
619
|
+
AND deleted_at IS NULL`,
|
|
620
|
+
args: [
|
|
621
|
+
resolutionTarget,
|
|
622
|
+
resolutionTarget,
|
|
623
|
+
now,
|
|
624
|
+
threadId,
|
|
625
|
+
resource.resourceType,
|
|
626
|
+
resource.resourceId,
|
|
627
|
+
],
|
|
628
|
+
});
|
|
629
|
+
return result.rowsAffected ?? 0;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export async function sendReviewThreadToAgent(
|
|
633
|
+
threadId: string,
|
|
634
|
+
resource: { resourceType: string; resourceId: string },
|
|
635
|
+
): Promise<number> {
|
|
636
|
+
return routeReviewThread(threadId, "agent", resource);
|
|
637
|
+
}
|
|
638
|
+
|
|
337
639
|
export async function deleteReviewComment(
|
|
338
640
|
id: string,
|
|
339
641
|
deletedBy?: string | null,
|
|
@@ -560,6 +862,7 @@ function scopedReviewClause(scope: ReviewScope): {
|
|
|
560
862
|
}
|
|
561
863
|
|
|
562
864
|
function mapCommentRow(row: Record<string, unknown>): ReviewComment {
|
|
865
|
+
const metadata = parseObject(row.metadata_json);
|
|
563
866
|
return {
|
|
564
867
|
id: String(row.id),
|
|
565
868
|
resourceType: String(row.resource_type),
|
|
@@ -586,7 +889,11 @@ function mapCommentRow(row: Record<string, unknown>): ReviewComment {
|
|
|
586
889
|
deletedAt: nullableString(row.deleted_at),
|
|
587
890
|
createdAt: String(row.created_at),
|
|
588
891
|
updatedAt: String(row.updated_at),
|
|
589
|
-
metadata
|
|
892
|
+
metadata,
|
|
893
|
+
resolutionNote:
|
|
894
|
+
typeof metadata?.resolutionNote === "string"
|
|
895
|
+
? metadata.resolutionNote
|
|
896
|
+
: null,
|
|
590
897
|
};
|
|
591
898
|
}
|
|
592
899
|
|
|
@@ -75,6 +75,10 @@ export interface ReviewComment {
|
|
|
75
75
|
createdAt: string;
|
|
76
76
|
updatedAt: string;
|
|
77
77
|
metadata: Record<string, unknown> | null;
|
|
78
|
+
/** Persisted on the root comment's metadata when a thread is resolved. */
|
|
79
|
+
resolutionNote?: string | null;
|
|
80
|
+
/** Caller-specific capability computed by list-review-comments. */
|
|
81
|
+
canDelete?: boolean;
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
export interface ReviewStatusEntry {
|
|
@@ -686,6 +686,10 @@ export async function mergeCoreSharingActions(
|
|
|
686
686
|
"set-review-status",
|
|
687
687
|
() => import("../review/actions/set-review-status.js"),
|
|
688
688
|
],
|
|
689
|
+
[
|
|
690
|
+
"send-review-thread-to-agent",
|
|
691
|
+
() => import("../review/actions/send-review-thread-to-agent.js"),
|
|
692
|
+
],
|
|
689
693
|
// Org service tokens (CI credentials, e.g. PLAN_RECAP_TOKEN). Mint/revoke
|
|
690
694
|
// are toolCallable:false — preserved via preserveActionFlags below.
|
|
691
695
|
[
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
createError,
|
|
2
3
|
defineEventHandler,
|
|
3
4
|
setResponseStatus,
|
|
4
5
|
setResponseHeader,
|
|
@@ -10,6 +11,7 @@ import {
|
|
|
10
11
|
|
|
11
12
|
import { isAgentActionStopError } from "../action.js";
|
|
12
13
|
import type { ActionEntry } from "../agent/production-agent.js";
|
|
14
|
+
import { resolveOrgIdForEmail } from "../org/context.js";
|
|
13
15
|
import { readBody } from "../server/h3-helpers.js";
|
|
14
16
|
import { EMBED_TARGET_HEADER } from "../shared/embed-auth.js";
|
|
15
17
|
import {
|
|
@@ -18,6 +20,10 @@ import {
|
|
|
18
20
|
shouldAllowMcpEmbedCredentials,
|
|
19
21
|
} from "../shared/mcp-embed-headers.js";
|
|
20
22
|
import { notifyActionChange } from "./action-change.js";
|
|
23
|
+
import {
|
|
24
|
+
seedAgentRunOwnerContext,
|
|
25
|
+
type AgentRunOwnerContext,
|
|
26
|
+
} from "./agent-run-context.js";
|
|
21
27
|
import {
|
|
22
28
|
getAllowedCorsOrigin as resolveAllowedCorsOrigin,
|
|
23
29
|
readCorsAllowedOrigins,
|
|
@@ -164,6 +170,51 @@ function handleOptionsRequest(event: any): string {
|
|
|
164
170
|
return "";
|
|
165
171
|
}
|
|
166
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Declarative auth adapter for the HTTP action route. Its `resolveCaller` runs
|
|
175
|
+
* BEFORE the framework's `getOwnerFromEvent` / `getSession` chain, letting an
|
|
176
|
+
* app accept caller identities `getSession` doesn't understand (e.g. an A2A
|
|
177
|
+
* JWT) without reaching into request context from a Nitro `request` hook.
|
|
178
|
+
*
|
|
179
|
+
* Scoped to `/_agent-native/actions/*` only — it does not affect other routes.
|
|
180
|
+
*/
|
|
181
|
+
export type ActionRouteResolvedCaller = AgentRunOwnerContext & {
|
|
182
|
+
/**
|
|
183
|
+
* Org to scope the request to, verified from the same credential as the
|
|
184
|
+
* caller identity (e.g. the A2A token's org claim). When omitted, the org
|
|
185
|
+
* is derived from the verified owner email via the framework's owner→org
|
|
186
|
+
* membership lookup. The ambient session/org state on the request is never
|
|
187
|
+
* consulted for adapter-resolved callers: a request can carry both a valid
|
|
188
|
+
* A2A bearer and an unrelated browser cookie, and the cookie user's org
|
|
189
|
+
* must not leak into the token caller's request context.
|
|
190
|
+
*/
|
|
191
|
+
orgId?: string;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export interface ActionRouteAuthAdapter {
|
|
195
|
+
/**
|
|
196
|
+
* Resolve a caller from the raw event before the cookie/bearer chain.
|
|
197
|
+
*
|
|
198
|
+
* - Return the resolved caller to run the action scoped to that identity.
|
|
199
|
+
* Org scoping comes exclusively from the caller: the returned `orgId` if
|
|
200
|
+
* set, otherwise the owner-email membership lookup — never from the
|
|
201
|
+
* request's session cookie or org context.
|
|
202
|
+
* - Return `null` when the credential isn't yours to judge — the request
|
|
203
|
+
* defers to `getOwnerFromEvent` / `getSession`.
|
|
204
|
+
* - THROW to hard-reject: the credential is present but invalid (e.g. an
|
|
205
|
+
* expired or forged A2A bearer). The action route responds 401 and does
|
|
206
|
+
* NOT fall through to the cookie/session chain, so a valid same-origin
|
|
207
|
+
* session cookie can't be used to execute the request as the logged-in
|
|
208
|
+
* user. Do not throw merely to signal "not mine" — return `null` for that.
|
|
209
|
+
*/
|
|
210
|
+
resolveCaller?: (
|
|
211
|
+
event: any,
|
|
212
|
+
) =>
|
|
213
|
+
| ActionRouteResolvedCaller
|
|
214
|
+
| null
|
|
215
|
+
| Promise<ActionRouteResolvedCaller | null>;
|
|
216
|
+
}
|
|
217
|
+
|
|
167
218
|
export interface MountActionRoutesOptions {
|
|
168
219
|
/** Resolve owner email from the H3 event (for data scoping). */
|
|
169
220
|
getOwnerFromEvent?: (event: any) => string | Promise<string>;
|
|
@@ -173,6 +224,18 @@ export interface MountActionRoutesOptions {
|
|
|
173
224
|
) => string | undefined | Promise<string | undefined>;
|
|
174
225
|
/** Resolve org ID from the H3 event (for org scoping). */
|
|
175
226
|
resolveOrgId?: (event: any) => string | null | Promise<string | null>;
|
|
227
|
+
/**
|
|
228
|
+
* Optional caller resolver that runs before the `getOwnerFromEvent` /
|
|
229
|
+
* `getSession` chain. Lets apps accept A2A JWTs (or other bearer schemes) on
|
|
230
|
+
* the action route declaratively. See {@link ActionRouteAuthAdapter}.
|
|
231
|
+
*/
|
|
232
|
+
actionRouteAuth?: ActionRouteAuthAdapter;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function normalizeOrgId(value: string | null | undefined): string | undefined {
|
|
236
|
+
return typeof value === "string" && value.trim().length > 0
|
|
237
|
+
? value.trim()
|
|
238
|
+
: undefined;
|
|
176
239
|
}
|
|
177
240
|
|
|
178
241
|
function isAuthResolutionFailure(error: unknown): boolean {
|
|
@@ -259,7 +322,42 @@ export function mountActionRoutes(
|
|
|
259
322
|
// Resolve auth context for per-request scoping
|
|
260
323
|
let userEmail: string | undefined;
|
|
261
324
|
let userName: string | undefined;
|
|
262
|
-
|
|
325
|
+
// An app-supplied auth adapter runs first: it can accept caller
|
|
326
|
+
// identities the framework's getSession chain doesn't understand (e.g.
|
|
327
|
+
// an A2A JWT). A resolved caller is seeded onto the event context so any
|
|
328
|
+
// downstream resolveAgentRunOwnerContext (nested agent runs) sees the
|
|
329
|
+
// same identity. The adapter is only consulted for the action route, so
|
|
330
|
+
// it can't affect other surfaces.
|
|
331
|
+
//
|
|
332
|
+
// Contract: `resolveCaller` returning `null` means "this credential
|
|
333
|
+
// isn't mine — defer to the cookie/session chain below". THROWING means
|
|
334
|
+
// "the credential is mine but invalid" (e.g. an expired/forged A2A
|
|
335
|
+
// bearer) and is a hard rejection: we surface a 401 instead of falling
|
|
336
|
+
// through, so a live same-origin session cookie can't silently execute
|
|
337
|
+
// the request as the logged-in user.
|
|
338
|
+
let resolvedCaller: ActionRouteResolvedCaller | null = null;
|
|
339
|
+
if (options?.actionRouteAuth?.resolveCaller) {
|
|
340
|
+
let caller: ActionRouteResolvedCaller | null;
|
|
341
|
+
try {
|
|
342
|
+
caller = await options.actionRouteAuth.resolveCaller(event);
|
|
343
|
+
} catch {
|
|
344
|
+
throw createError({
|
|
345
|
+
statusCode: 401,
|
|
346
|
+
statusMessage: "Unauthorized",
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
if (caller) {
|
|
350
|
+
seedAgentRunOwnerContext(event, {
|
|
351
|
+
owner: caller.owner,
|
|
352
|
+
anonymous: caller.anonymous,
|
|
353
|
+
name: caller.name,
|
|
354
|
+
});
|
|
355
|
+
userEmail = caller.owner;
|
|
356
|
+
userName = caller.name;
|
|
357
|
+
resolvedCaller = caller;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (!resolvedCaller && options?.getOwnerFromEvent) {
|
|
263
361
|
try {
|
|
264
362
|
userEmail = await options.getOwnerFromEvent(event);
|
|
265
363
|
userName = options?.getUserNameFromEvent
|
|
@@ -277,9 +375,32 @@ export function mountActionRoutes(
|
|
|
277
375
|
}
|
|
278
376
|
}
|
|
279
377
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
378
|
+
// Org scoping. For adapter-resolved callers the org must come
|
|
379
|
+
// exclusively from the verified credential: the adapter-asserted
|
|
380
|
+
// orgId when present, otherwise the owner-email membership lookup.
|
|
381
|
+
// The request's ambient session/org state (`resolveOrgId`, usually
|
|
382
|
+
// getSession-backed) is deliberately NOT consulted — a request can
|
|
383
|
+
// carry both a valid A2A bearer and an unrelated same-origin browser
|
|
384
|
+
// cookie, and the cookie user's org must not become the org the
|
|
385
|
+
// token caller's actions execute under. Non-adapter callers keep the
|
|
386
|
+
// original resolveOrgId-only behavior.
|
|
387
|
+
let orgId: string | undefined;
|
|
388
|
+
if (resolvedCaller) {
|
|
389
|
+
orgId = normalizeOrgId(resolvedCaller.orgId);
|
|
390
|
+
if (!orgId && resolvedCaller.owner && !resolvedCaller.anonymous) {
|
|
391
|
+
try {
|
|
392
|
+
orgId = normalizeOrgId(
|
|
393
|
+
await resolveOrgIdForEmail(resolvedCaller.owner),
|
|
394
|
+
);
|
|
395
|
+
} catch {
|
|
396
|
+
// Org tables may not exist yet on first boot.
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
orgId = options?.resolveOrgId
|
|
401
|
+
? ((await options.resolveOrgId(event)) ?? undefined)
|
|
402
|
+
: undefined;
|
|
403
|
+
}
|
|
283
404
|
const timezone = readTimezoneHeader(event);
|
|
284
405
|
|
|
285
406
|
return runWithRequestContext(
|
|
@@ -109,6 +109,22 @@ export interface AgentChatPluginOptions {
|
|
|
109
109
|
* agent mutations.
|
|
110
110
|
*/
|
|
111
111
|
anonymousReadOnly?: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Optional auth adapter for the HTTP action route
|
|
114
|
+
* (`/_agent-native/actions/*`). Its `resolveCaller` runs before the
|
|
115
|
+
* cookie/bearer `getSession` chain, letting an app accept caller identities
|
|
116
|
+
* `getSession` doesn't understand (e.g. an A2A JWT verified with
|
|
117
|
+
* `verifyA2AToken`) declaratively, instead of pre-seeding request context
|
|
118
|
+
* from a Nitro `request` hook.
|
|
119
|
+
*
|
|
120
|
+
* Returning `null` defers to the normal chain; THROWING hard-rejects with a
|
|
121
|
+
* 401 (an invalid/forged credential must not fall through to a same-origin
|
|
122
|
+
* session cookie). A resolved caller's org comes exclusively from the
|
|
123
|
+
* verified credential — the returned `orgId` or the owner-email membership
|
|
124
|
+
* lookup — never from the request's session cookie.
|
|
125
|
+
* See {@link import("../action-routes.js").ActionRouteAuthAdapter}.
|
|
126
|
+
*/
|
|
127
|
+
actionRouteAuth?: import("../action-routes.js").ActionRouteAuthAdapter;
|
|
112
128
|
/**
|
|
113
129
|
* Optional callback to append template-specific context to the system
|
|
114
130
|
* prompt on each request. Runs after AGENTS.md / skills / memory are
|
|
@@ -241,6 +241,7 @@ export {
|
|
|
241
241
|
renderAgentNativeOgImageSvg,
|
|
242
242
|
type AgentNativeOgImageInput,
|
|
243
243
|
} from "./social-og-image.js";
|
|
244
|
+
export { resolveOgFontFiles } from "./og-fonts.js";
|
|
244
245
|
export {
|
|
245
246
|
createBrowserSessionActionEntries,
|
|
246
247
|
type CreateBrowserSessionActionEntriesOptions,
|
|
@@ -320,7 +321,14 @@ export {
|
|
|
320
321
|
export {
|
|
321
322
|
mountActionRoutes,
|
|
322
323
|
type MountActionRoutesOptions,
|
|
324
|
+
type ActionRouteAuthAdapter,
|
|
325
|
+
type ActionRouteResolvedCaller,
|
|
323
326
|
} from "./action-routes.js";
|
|
327
|
+
export {
|
|
328
|
+
AGENT_RUN_OWNER_CONTEXT_KEY,
|
|
329
|
+
seedAgentRunOwnerContext,
|
|
330
|
+
type AgentRunOwnerContext,
|
|
331
|
+
} from "./agent-run-context.js";
|
|
324
332
|
export {
|
|
325
333
|
runWithRequestContext,
|
|
326
334
|
hasRequestContext,
|
|
@@ -141,6 +141,10 @@ const CORE_SHARING_ACTIONS: Array<{ name: string; specifier: string }> = [
|
|
|
141
141
|
name: "set-review-status",
|
|
142
142
|
specifier: "@agent-native/core/review/actions/set-review-status",
|
|
143
143
|
},
|
|
144
|
+
{
|
|
145
|
+
name: "send-review-thread-to-agent",
|
|
146
|
+
specifier: "@agent-native/core/review/actions/send-review-thread-to-agent",
|
|
147
|
+
},
|
|
144
148
|
];
|
|
145
149
|
|
|
146
150
|
function isRuntimeSourceFile(filename: string): boolean {
|