@agent-native/core 0.100.2 → 0.101.3
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 +83 -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/credentials/index.ts +41 -8
- package/corpus/core/src/extensions/fetch-tool.ts +33 -3
- package/corpus/core/src/notifications/channels.ts +20 -6
- 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/secrets/crypto.ts +26 -10
- package/corpus/core/src/secrets/storage.ts +16 -3
- 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 +61 -7
- package/corpus/core/src/server/derived-secret.ts +5 -1
- 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/changelog/2026-07-14-long-running-cross-app-analyses-now-continue-reliably-in-the.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +156 -3
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +8 -4
- 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/share-dialog.tsx +44 -81
- 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/i18n/ar-SA.ts +0 -8
- package/corpus/templates/clips/app/i18n/de-DE.ts +0 -9
- package/corpus/templates/clips/app/i18n/en-US.ts +0 -8
- package/corpus/templates/clips/app/i18n/es-ES.ts +0 -9
- package/corpus/templates/clips/app/i18n/fr-FR.ts +0 -9
- package/corpus/templates/clips/app/i18n/hi-IN.ts +0 -8
- package/corpus/templates/clips/app/i18n/ja-JP.ts +0 -9
- package/corpus/templates/clips/app/i18n/ko-KR.ts +0 -9
- package/corpus/templates/clips/app/i18n/pt-BR.ts +0 -9
- package/corpus/templates/clips/app/i18n/zh-CN.ts +0 -7
- package/corpus/templates/clips/app/i18n/zh-TW.ts +0 -7
- 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/changelog/2026-07-14-share-dialogs-show-separate-links-for-people-and-agents.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/dispatch/changelog/2026-07-14-dispatch-now-distinguishes-connected-agents-from-locally-mou.md +6 -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/credentials/index.d.ts +12 -7
- package/dist/credentials/index.d.ts.map +1 -1
- package/dist/credentials/index.js +33 -8
- package/dist/credentials/index.js.map +1 -1
- package/dist/extensions/fetch-tool.d.ts +13 -1
- package/dist/extensions/fetch-tool.d.ts.map +1 -1
- package/dist/extensions/fetch-tool.js +9 -2
- package/dist/extensions/fetch-tool.js.map +1 -1
- package/dist/notifications/channels.js +20 -4
- package/dist/notifications/channels.js.map +1 -1
- 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 +3 -3
- 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/crypto.d.ts +15 -7
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +23 -10
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +16 -3
- 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/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 +20 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +39 -4
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/derived-secret.d.ts +1 -1
- package/dist/server/derived-secret.d.ts.map +1 -1
- package/dist/server/derived-secret.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/clips/app/components/sharing/slack-share-hint.tsx +0 -68
- package/corpus/templates/forms/server/routes/api/forms/og/[slug]/og.png.get.ts +0 -45
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ReviewComment,
|
|
3
|
+
ReviewResourceAccess,
|
|
4
|
+
ReviewResourceContext,
|
|
5
|
+
ReviewStatusEntry,
|
|
6
|
+
} from "./types.js";
|
|
7
|
+
|
|
8
|
+
export function reviewAuthorNameFromContext(
|
|
9
|
+
ctx: ReviewResourceContext | undefined,
|
|
10
|
+
): string | null {
|
|
11
|
+
const value = ctx?.userName;
|
|
12
|
+
if (typeof value !== "string") return null;
|
|
13
|
+
const name = value.trim();
|
|
14
|
+
if (!name || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(name)) return null;
|
|
15
|
+
return name;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function shouldRedactReviewIdentity(
|
|
19
|
+
ctx: ReviewResourceContext | undefined,
|
|
20
|
+
access: Pick<ReviewResourceAccess, "role" | "visibility">,
|
|
21
|
+
): boolean {
|
|
22
|
+
return (
|
|
23
|
+
!ctx?.userEmail ||
|
|
24
|
+
(access.visibility === "public" && access.role === "viewer")
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function redactPublicReviewCommentIdentity(
|
|
29
|
+
comment: ReviewComment,
|
|
30
|
+
): ReviewComment {
|
|
31
|
+
return {
|
|
32
|
+
...comment,
|
|
33
|
+
authorEmail: null,
|
|
34
|
+
authorName: safeReviewDisplayName(comment.authorName),
|
|
35
|
+
mentions: comment.mentions.map((mention) => ({
|
|
36
|
+
label: safeReviewDisplayName(mention.label) ?? "Mentioned user",
|
|
37
|
+
})),
|
|
38
|
+
ownerEmail: null,
|
|
39
|
+
orgId: null,
|
|
40
|
+
resolvedBy: null,
|
|
41
|
+
deletedBy: null,
|
|
42
|
+
metadata: redactPublicReviewMetadata(comment.metadata),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function redactPublicReviewStatusIdentity(
|
|
47
|
+
status: ReviewStatusEntry | null,
|
|
48
|
+
): ReviewStatusEntry | null {
|
|
49
|
+
return status
|
|
50
|
+
? {
|
|
51
|
+
...status,
|
|
52
|
+
updatedBy: null,
|
|
53
|
+
ownerEmail: null,
|
|
54
|
+
orgId: null,
|
|
55
|
+
metadata: redactPublicReviewMetadata(status.metadata),
|
|
56
|
+
}
|
|
57
|
+
: null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function safeReviewDisplayName(
|
|
61
|
+
value: string | null | undefined,
|
|
62
|
+
): string | null {
|
|
63
|
+
const name = value?.trim();
|
|
64
|
+
if (!name || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(name)) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return name;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function redactPublicReviewMetadata(
|
|
71
|
+
metadata: Record<string, unknown> | null,
|
|
72
|
+
): Record<string, unknown> | null {
|
|
73
|
+
if (!metadata) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return Object.fromEntries(
|
|
77
|
+
Object.entries(metadata).flatMap(([key, value]) =>
|
|
78
|
+
/^(authorEmail|ownerEmail|orgId|updatedBy|resolvedBy|deletedBy|email|userEmail|userId)$/i.test(
|
|
79
|
+
key,
|
|
80
|
+
)
|
|
81
|
+
? []
|
|
82
|
+
: [[key, redactPublicReviewMetadataValue(value)]],
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function redactPublicReviewMetadataValue(value: unknown): unknown {
|
|
88
|
+
if (Array.isArray(value)) {
|
|
89
|
+
return value.map(redactPublicReviewMetadataValue);
|
|
90
|
+
}
|
|
91
|
+
if (typeof value === "object" && value !== null) {
|
|
92
|
+
return redactPublicReviewMetadata(value as Record<string, unknown>);
|
|
93
|
+
}
|
|
94
|
+
if (
|
|
95
|
+
typeof value === "string" &&
|
|
96
|
+
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim())
|
|
97
|
+
) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
@@ -13,6 +13,12 @@ export type {
|
|
|
13
13
|
ReviewStatusEntry,
|
|
14
14
|
ReviewableResourceRegistration,
|
|
15
15
|
} from "./types.js";
|
|
16
|
+
export {
|
|
17
|
+
redactPublicReviewCommentIdentity,
|
|
18
|
+
redactPublicReviewStatusIdentity,
|
|
19
|
+
reviewAuthorNameFromContext,
|
|
20
|
+
shouldRedactReviewIdentity,
|
|
21
|
+
} from "./identity.js";
|
|
16
22
|
export { extractReviewMentions, normalizeReviewMentions } from "./mentions.js";
|
|
17
23
|
export {
|
|
18
24
|
__resetReviewableResourcesForTests,
|
|
@@ -27,5 +33,13 @@ export {
|
|
|
27
33
|
ensureReviewTables,
|
|
28
34
|
getReviewCommentById,
|
|
29
35
|
getReviewStatus,
|
|
36
|
+
getReviewThreadSummary,
|
|
37
|
+
getReviewThreadRoot,
|
|
30
38
|
queryReviewComments,
|
|
39
|
+
routeReviewThread,
|
|
40
|
+
sendReviewThreadToAgent,
|
|
41
|
+
} from "./store.js";
|
|
42
|
+
export type {
|
|
43
|
+
GetReviewThreadSummaryInput,
|
|
44
|
+
ReviewThreadSummary,
|
|
31
45
|
} from "./store.js";
|
|
@@ -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 {
|
|
@@ -14,15 +14,22 @@
|
|
|
14
14
|
* `<APP_NAME>_SECRETS_ENCRYPTION_KEY` when set, then `SECRETS_ENCRYPTION_KEY`,
|
|
15
15
|
* then `BETTER_AUTH_SECRET`. The app-scoped key lets a local multi-app
|
|
16
16
|
* workspace read one app's encrypted production data without replacing the
|
|
17
|
-
* shared local auth secret.
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* shared local auth secret. Hosted workspace deploys (`AGENT_NATIVE_WORKSPACE`)
|
|
18
|
+
* typically set none of those literal env vars per app; in that case the
|
|
19
|
+
* shared-key variant falls back further to material derived from the
|
|
20
|
+
* workspace-wide `A2A_SECRET` (see `getWorkspaceA2ADerivedSecret`), so sibling
|
|
21
|
+
* apps in the same workspace still land on the same shared key without any
|
|
22
|
+
* extra configuration. In production we refuse to start without configured
|
|
23
|
+
* key material — a CWD-derived fallback would be effectively static (e.g.
|
|
24
|
+
* `/var/task` on Lambda), so anyone with read access to the DB could decrypt
|
|
25
|
+
* every secret.
|
|
21
26
|
*
|
|
22
27
|
* Encrypted values are tagged `v1:<iv-hex>:<ct-hex>:<tag-hex>`. The `v1:` prefix
|
|
23
28
|
* lets readers distinguish ciphertext from legacy plaintext during migration.
|
|
24
29
|
*/
|
|
25
30
|
|
|
31
|
+
import { getWorkspaceA2ADerivedSecret } from "../server/derived-secret.js";
|
|
32
|
+
|
|
26
33
|
type NodeCryptoModule = typeof import("node:crypto");
|
|
27
34
|
|
|
28
35
|
function getNodeCrypto(): NodeCryptoModule | undefined {
|
|
@@ -75,7 +82,11 @@ function appScopedEncryptionKey(): string | undefined {
|
|
|
75
82
|
|
|
76
83
|
function sharedEncryptionKeyMaterial(): string | undefined {
|
|
77
84
|
if (typeof process === "undefined") return undefined;
|
|
78
|
-
return
|
|
85
|
+
return (
|
|
86
|
+
process.env.SECRETS_ENCRYPTION_KEY ||
|
|
87
|
+
process.env.BETTER_AUTH_SECRET ||
|
|
88
|
+
getWorkspaceA2ADerivedSecret("secrets-encryption")
|
|
89
|
+
);
|
|
79
90
|
}
|
|
80
91
|
|
|
81
92
|
function deriveSecretEncryptionKey(
|
|
@@ -129,17 +140,22 @@ export function getSecretEncryptionKey(): Buffer {
|
|
|
129
140
|
* Derive the preferred workspace-shared key used by `app_secrets` rows.
|
|
130
141
|
* Unlike generic column-level encryption, workspace vault data should decrypt
|
|
131
142
|
* in sibling apps, so `SECRETS_ENCRYPTION_KEY` / `BETTER_AUTH_SECRET` take
|
|
132
|
-
* precedence.
|
|
133
|
-
*
|
|
134
|
-
*
|
|
143
|
+
* precedence. Hosted workspace deploys (`AGENT_NATIVE_WORKSPACE`) that set
|
|
144
|
+
* neither literal var still get stable shared material derived from the
|
|
145
|
+
* workspace-wide `A2A_SECRET` before falling further back. The app-scoped key
|
|
146
|
+
* remains a compatibility fallback for deployments that have not configured
|
|
147
|
+
* shared material yet; once the shared key is configured, writes use it and
|
|
148
|
+
* reads still fall back to old rows.
|
|
135
149
|
*/
|
|
136
150
|
export function getSharedSecretEncryptionKey(): Buffer {
|
|
137
151
|
return deriveSecretEncryptionKey(
|
|
138
152
|
sharedEncryptionKeyMaterial() || appScopedEncryptionKey(),
|
|
139
153
|
"[agent-native/secrets] Refusing to start in production without encryption key material for workspace secrets. " +
|
|
140
|
-
"Set SECRETS_ENCRYPTION_KEY, BETTER_AUTH_SECRET, or the app-scoped *_SECRETS_ENCRYPTION_KEY in the deploy environment
|
|
154
|
+
"Set SECRETS_ENCRYPTION_KEY, BETTER_AUTH_SECRET, or the app-scoped *_SECRETS_ENCRYPTION_KEY in the deploy environment " +
|
|
155
|
+
"— or, on a hosted workspace deploy, ensure A2A_SECRET is set so shared material can be derived from it.",
|
|
141
156
|
"[agent-native/secrets] SECRETS_ENCRYPTION_KEY not set — using app-scoped or machine-local fallback for workspace secrets. " +
|
|
142
|
-
"Set SECRETS_ENCRYPTION_KEY or BETTER_AUTH_SECRET in every workspace app so sibling apps share vault rows
|
|
157
|
+
"Set SECRETS_ENCRYPTION_KEY or BETTER_AUTH_SECRET in every workspace app so sibling apps share vault rows, " +
|
|
158
|
+
"or rely on A2A_SECRET-derived material on hosted workspace deploys.",
|
|
143
159
|
);
|
|
144
160
|
}
|
|
145
161
|
|
|
@@ -163,8 +163,9 @@ export async function writeAppSecret(args: WriteSecretArgs): Promise<string> {
|
|
|
163
163
|
const now = Date.now();
|
|
164
164
|
// Dual-write during rollout: old readers continue using encrypted_value,
|
|
165
165
|
// while new readers prefer the nullable shared ciphertext. An app-only
|
|
166
|
-
// deployment leaves the shared column null; on an update,
|
|
167
|
-
// shared
|
|
166
|
+
// deployment leaves the shared column null; on an update, a writer without
|
|
167
|
+
// shared key material clears any existing shared ciphertext rather than
|
|
168
|
+
// preserving it (see the upsert SQL below for why).
|
|
168
169
|
const encrypted = encryptLegacyValue(value);
|
|
169
170
|
const sharedEncrypted = hasSharedSecretEncryptionKeyMaterial()
|
|
170
171
|
? encryptValue(value)
|
|
@@ -182,11 +183,23 @@ export async function writeAppSecret(args: WriteSecretArgs): Promise<string> {
|
|
|
182
183
|
// keeps its original id (any stored references stay stable); only a
|
|
183
184
|
// genuinely new row gets the freshly generated `id`. This syntax is
|
|
184
185
|
// portable across SQLite (UPSERT since 3.24) and Postgres.
|
|
186
|
+
//
|
|
187
|
+
// shared_encrypted_value is overwritten with `excluded.shared_encrypted_value`
|
|
188
|
+
// (NULL when this writer lacks shared key material) rather than preserved
|
|
189
|
+
// via COALESCE. Preserving an existing shared ciphertext across a value
|
|
190
|
+
// update would let a sibling app silently decrypt a STALE value after the
|
|
191
|
+
// owner rotates it — a material-less writer has no way to produce the new
|
|
192
|
+
// shared ciphertext, so it must clear the old one instead of leaving it
|
|
193
|
+
// pointing at data that's no longer current. Siblings then get an honest
|
|
194
|
+
// cache miss (falling back to the legacy column or reporting missing) until
|
|
195
|
+
// the owning app's next read repopulates shared_encrypted_value via
|
|
196
|
+
// `populateSharedAppSecret`, which only ever fills a NULL column. A
|
|
197
|
+
// temporary miss is safer than serving rotated-away plaintext.
|
|
185
198
|
const upsertSql = `INSERT INTO app_secrets (id, scope, scope_id, key, encrypted_value, shared_encrypted_value, description, url_allowlist, created_at, updated_at)
|
|
186
199
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
187
200
|
ON CONFLICT (scope, scope_id, key) DO UPDATE SET
|
|
188
201
|
encrypted_value = excluded.encrypted_value,
|
|
189
|
-
shared_encrypted_value =
|
|
202
|
+
shared_encrypted_value = excluded.shared_encrypted_value,
|
|
190
203
|
description = excluded.description,
|
|
191
204
|
url_allowlist = excluded.url_allowlist,
|
|
192
205
|
updated_at = excluded.updated_at`;
|
|
@@ -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
|
[
|