@agent-native/core 0.100.1 → 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 +73 -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/AssistantChat.tsx +4 -0
- 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/composer/PromptComposer.tsx +4 -0
- package/corpus/core/src/client/composer/TiptapComposer.tsx +30 -3
- 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/integrations/webhook-handler.ts +4 -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/secrets/crypto.ts +98 -47
- package/corpus/core/src/secrets/index.ts +2 -1
- package/corpus/core/src/secrets/schema.ts +3 -0
- package/corpus/core/src/secrets/storage.ts +164 -31
- 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/components/layout/Layout.tsx +4 -0
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +3 -1
- 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-assets-keeps-the-right-chat-sidebar-closed-when-the-full-pag.md +6 -0
- 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/AssistantChat.d.ts +2 -0
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +2 -2
- package/dist/client/AssistantChat.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/composer/PromptComposer.d.ts +2 -0
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +2 -2
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts +3 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +20 -5
- package/dist/client/composer/TiptapComposer.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/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/webhook-handler.d.ts.map +1 -1
- package/dist/integrations/webhook-handler.js +3 -2
- package/dist/integrations/webhook-handler.js.map +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/crypto.d.ts +35 -13
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +78 -41
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/secrets/index.d.ts.map +1 -1
- package/dist/secrets/index.js +2 -1
- package/dist/secrets/index.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/secrets/schema.d.ts +20 -1
- package/dist/secrets/schema.d.ts.map +1 -1
- package/dist/secrets/schema.js +3 -0
- package/dist/secrets/schema.js.map +1 -1
- package/dist/secrets/storage.d.ts +7 -7
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +121 -30
- 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.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 {
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared AES-256-GCM encryption for secret values at rest.
|
|
3
3
|
*
|
|
4
|
-
* Used by
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Used by the framework secrets vault, per-user/per-org credentials
|
|
5
|
+
* (`resolveCredential` / `saveCredential`, stored in `settings`), and other
|
|
6
|
+
* column-level encrypted values. The `app_secrets` storage layer uses the
|
|
7
|
+
* shared-key variant below because workspace-scoped vault rows are readable by
|
|
8
|
+
* sibling apps in the same workspace. When a deployment has not been given
|
|
9
|
+
* shared key material yet, that variant falls back to the app-scoped key so
|
|
10
|
+
* existing single-app deployments can keep reading and writing secrets while
|
|
11
|
+
* they migrate to the shared key.
|
|
7
12
|
*
|
|
8
|
-
* The encryption key is derived from
|
|
9
|
-
* set, then `SECRETS_ENCRYPTION_KEY`,
|
|
10
|
-
* key lets a local multi-app
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* The default encryption key is derived from
|
|
14
|
+
* `<APP_NAME>_SECRETS_ENCRYPTION_KEY` when set, then `SECRETS_ENCRYPTION_KEY`,
|
|
15
|
+
* then `BETTER_AUTH_SECRET`. The app-scoped key lets a local multi-app
|
|
16
|
+
* workspace read one app's encrypted production data without replacing the
|
|
17
|
+
* shared local auth secret. In production we refuse to start without
|
|
18
|
+
* configured key material — a CWD-derived fallback would be effectively
|
|
19
|
+
* static (e.g. `/var/task` on Lambda), so anyone with read access to the DB
|
|
20
|
+
* could decrypt every secret.
|
|
15
21
|
*
|
|
16
22
|
* Encrypted values are tagged `v1:<iv-hex>:<ct-hex>:<tag-hex>`. The `v1:` prefix
|
|
17
23
|
* lets readers distinguish ciphertext from legacy plaintext during migration.
|
|
@@ -67,45 +73,26 @@ function appScopedEncryptionKey(): string | undefined {
|
|
|
67
73
|
: undefined;
|
|
68
74
|
}
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
function sharedEncryptionKeyMaterial(): string | undefined {
|
|
77
|
+
if (typeof process === "undefined") return undefined;
|
|
78
|
+
return process.env.SECRETS_ENCRYPTION_KEY || process.env.BETTER_AUTH_SECRET;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function deriveSecretEncryptionKey(
|
|
82
|
+
explicit: string | undefined,
|
|
83
|
+
errorMessage: string,
|
|
84
|
+
warningMessage: string,
|
|
85
|
+
): Buffer {
|
|
75
86
|
const { createHash } = requireNodeCrypto();
|
|
76
|
-
const explicit =
|
|
77
|
-
appScopedEncryptionKey() ||
|
|
78
|
-
(typeof process === "undefined"
|
|
79
|
-
? undefined
|
|
80
|
-
: process.env.SECRETS_ENCRYPTION_KEY) ||
|
|
81
|
-
(typeof process === "undefined"
|
|
82
|
-
? undefined
|
|
83
|
-
: process.env.BETTER_AUTH_SECRET);
|
|
84
87
|
|
|
85
88
|
if (!explicit) {
|
|
86
89
|
if (processNodeEnv() === "production") {
|
|
87
|
-
|
|
88
|
-
typeof process === "undefined"
|
|
89
|
-
? undefined
|
|
90
|
-
: process.env.APP_NAME?.trim() // guard:allow-env-credential — deploy-level app configuration selects the scoped encryption key.
|
|
91
|
-
.toUpperCase()
|
|
92
|
-
.replace(/[^A-Z0-9]+/g, "_")
|
|
93
|
-
.replace(/^_+|_+$/g, "");
|
|
94
|
-
throw new Error(
|
|
95
|
-
"[agent-native/secrets] Refusing to start in production without an encryption key. " +
|
|
96
|
-
`Set ${appName ? `${appName}_SECRETS_ENCRYPTION_KEY, ` : ""}SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET in the deploy environment. ` +
|
|
97
|
-
"The previous CWD-derived fallback was effectively static (e.g. `/var/task` on Lambda), " +
|
|
98
|
-
"which means anyone with read access to the secrets table could decrypt every user's secrets.",
|
|
99
|
-
);
|
|
90
|
+
throw new Error(errorMessage);
|
|
100
91
|
}
|
|
101
92
|
if (!_warnedFallback) {
|
|
102
93
|
_warnedFallback = true;
|
|
103
94
|
// eslint-disable-next-line no-console
|
|
104
|
-
console.warn(
|
|
105
|
-
"[agent-native/secrets] SECRETS_ENCRYPTION_KEY not set — using a machine-local fallback. " +
|
|
106
|
-
"Set an app-scoped *_SECRETS_ENCRYPTION_KEY, SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET for production. " +
|
|
107
|
-
"Production deploys without one of these env vars now hard-fail.",
|
|
108
|
-
);
|
|
95
|
+
console.warn(warningMessage);
|
|
109
96
|
}
|
|
110
97
|
}
|
|
111
98
|
|
|
@@ -113,10 +100,56 @@ export function getSecretEncryptionKey(): Buffer {
|
|
|
113
100
|
return createHash("sha256").update(material).digest();
|
|
114
101
|
}
|
|
115
102
|
|
|
116
|
-
/**
|
|
117
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Derive the key used by generic encrypted values such as credentials and
|
|
105
|
+
* OAuth tokens. App-specific key material is allowed for these app-local
|
|
106
|
+
* values.
|
|
107
|
+
*/
|
|
108
|
+
export function getSecretEncryptionKey(): Buffer {
|
|
109
|
+
const appName =
|
|
110
|
+
typeof process === "undefined"
|
|
111
|
+
? undefined
|
|
112
|
+
: process.env.APP_NAME?.trim() // guard:allow-env-credential — deploy-level app configuration selects the scoped encryption key.
|
|
113
|
+
.toUpperCase()
|
|
114
|
+
.replace(/[^A-Z0-9]+/g, "_")
|
|
115
|
+
.replace(/^_+|_+$/g, "");
|
|
116
|
+
return deriveSecretEncryptionKey(
|
|
117
|
+
appScopedEncryptionKey() || sharedEncryptionKeyMaterial(),
|
|
118
|
+
"[agent-native/secrets] Refusing to start in production without an encryption key. " +
|
|
119
|
+
`Set ${appName ? `${appName}_SECRETS_ENCRYPTION_KEY, ` : ""}SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET in the deploy environment. ` +
|
|
120
|
+
"The previous CWD-derived fallback was effectively static (e.g. `/var/task` on Lambda), " +
|
|
121
|
+
"which means anyone with read access to the DB could decrypt every secret.",
|
|
122
|
+
"[agent-native/secrets] SECRETS_ENCRYPTION_KEY not set — using a machine-local fallback. " +
|
|
123
|
+
"Set an app-scoped *_SECRETS_ENCRYPTION_KEY, SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET for production. " +
|
|
124
|
+
"Production deploys without one of these env vars now hard-fail.",
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Derive the preferred workspace-shared key used by `app_secrets` rows.
|
|
130
|
+
* Unlike generic column-level encryption, workspace vault data should decrypt
|
|
131
|
+
* in sibling apps, so `SECRETS_ENCRYPTION_KEY` / `BETTER_AUTH_SECRET` take
|
|
132
|
+
* precedence. The app-scoped key remains a compatibility fallback for
|
|
133
|
+
* deployments that have not configured shared material yet; once the shared
|
|
134
|
+
* key is configured, writes use it and reads still fall back to old rows.
|
|
135
|
+
*/
|
|
136
|
+
export function getSharedSecretEncryptionKey(): Buffer {
|
|
137
|
+
return deriveSecretEncryptionKey(
|
|
138
|
+
sharedEncryptionKeyMaterial() || appScopedEncryptionKey(),
|
|
139
|
+
"[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.",
|
|
141
|
+
"[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.",
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Whether this deployment has stable workspace-shared key material. */
|
|
147
|
+
export function hasSharedSecretEncryptionKeyMaterial(): boolean {
|
|
148
|
+
return Boolean(sharedEncryptionKeyMaterial());
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function encryptWithKey(plaintext: string, key: Buffer): string {
|
|
118
152
|
const { createCipheriv, randomBytes } = requireNodeCrypto();
|
|
119
|
-
const key = getSecretEncryptionKey();
|
|
120
153
|
const iv = randomBytes(12);
|
|
121
154
|
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
122
155
|
const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
|
|
@@ -124,8 +157,7 @@ export function encryptSecretValue(plaintext: string): string {
|
|
|
124
157
|
return `v1:${iv.toString("hex")}:${ct.toString("hex")}:${tag.toString("hex")}`;
|
|
125
158
|
}
|
|
126
159
|
|
|
127
|
-
|
|
128
|
-
export function decryptSecretValue(encrypted: string): string {
|
|
160
|
+
function decryptWithKey(encrypted: string, key: Buffer): string {
|
|
129
161
|
const { createDecipheriv } = requireNodeCrypto();
|
|
130
162
|
if (!encrypted.startsWith("v1:")) {
|
|
131
163
|
throw new Error("Unrecognised secret encoding");
|
|
@@ -134,7 +166,6 @@ export function decryptSecretValue(encrypted: string): string {
|
|
|
134
166
|
if (!ivHex || !ctHex || !tagHex) {
|
|
135
167
|
throw new Error("Corrupt secret payload");
|
|
136
168
|
}
|
|
137
|
-
const key = getSecretEncryptionKey();
|
|
138
169
|
const decipher = createDecipheriv(
|
|
139
170
|
"aes-256-gcm",
|
|
140
171
|
key,
|
|
@@ -148,6 +179,26 @@ export function decryptSecretValue(encrypted: string): string {
|
|
|
148
179
|
return pt.toString("utf8");
|
|
149
180
|
}
|
|
150
181
|
|
|
182
|
+
/** Encrypt a plain-text value with the generic app-local key. */
|
|
183
|
+
export function encryptSecretValue(plaintext: string): string {
|
|
184
|
+
return encryptWithKey(plaintext, getSecretEncryptionKey());
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Decrypt a value produced by `encryptSecretValue`. Throws on tampering. */
|
|
188
|
+
export function decryptSecretValue(encrypted: string): string {
|
|
189
|
+
return decryptWithKey(encrypted, getSecretEncryptionKey());
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Encrypt a workspace-shared `app_secrets` value. */
|
|
193
|
+
export function encryptSharedSecretValue(plaintext: string): string {
|
|
194
|
+
return encryptWithKey(plaintext, getSharedSecretEncryptionKey());
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Decrypt a workspace-shared `app_secrets` value. */
|
|
198
|
+
export function decryptSharedSecretValue(encrypted: string): string {
|
|
199
|
+
return decryptWithKey(encrypted, getSharedSecretEncryptionKey());
|
|
200
|
+
}
|
|
201
|
+
|
|
151
202
|
/**
|
|
152
203
|
* Strict check for a value produced by `encryptSecretValue`: `v1:` followed by
|
|
153
204
|
* three hex segments. Intentionally strict so a legacy plaintext credential
|
|
@@ -38,7 +38,8 @@ export { APP_SECRETS_CREATE_SQL, appSecrets } from "./schema.js";
|
|
|
38
38
|
|
|
39
39
|
// AES-256-GCM helpers for column-level encryption of per-row secret values
|
|
40
40
|
// (e.g. a per-record share password) that don't fit the keyed app_secrets /
|
|
41
|
-
// credentials stores.
|
|
41
|
+
// credentials stores. These use the app-local key, unlike the workspace-shared
|
|
42
|
+
// app_secrets storage layer.
|
|
42
43
|
export {
|
|
43
44
|
encryptSecretValue,
|
|
44
45
|
decryptSecretValue,
|
|
@@ -22,6 +22,8 @@ export const appSecrets = table("app_secrets", {
|
|
|
22
22
|
key: text("key").notNull(),
|
|
23
23
|
/** Encrypted value — never return this through any API. */
|
|
24
24
|
encryptedValue: text("encrypted_value").notNull(),
|
|
25
|
+
/** Preferred workspace-shared ciphertext; nullable during key migration. */
|
|
26
|
+
sharedEncryptedValue: text("shared_encrypted_value"),
|
|
25
27
|
/** Optional human-readable description (used for ad-hoc keys). */
|
|
26
28
|
description: text("description"),
|
|
27
29
|
/** JSON array of allowed URL origins. Null = allow all. */
|
|
@@ -41,6 +43,7 @@ export const APP_SECRETS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS app_secrets (
|
|
|
41
43
|
scope_id TEXT NOT NULL,
|
|
42
44
|
key TEXT NOT NULL,
|
|
43
45
|
encrypted_value TEXT NOT NULL,
|
|
46
|
+
shared_encrypted_value TEXT,
|
|
44
47
|
description TEXT,
|
|
45
48
|
url_allowlist TEXT,
|
|
46
49
|
created_at INTEGER NOT NULL,
|