@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-review.js","sourceRoot":"","sources":["../../../src/client/review/use-review.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"use-review.js","sourceRoot":"","sources":["../../../src/client/review/use-review.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAyFrE,MAAM,UAAU,iBAAiB,CAC/B,MAAgC,EAChC,OAA+B;IAE/B,OAAO,cAAc,CACnB,sBAAsB,EACtB,MAAM,EACN;QACE,OAAO,EACL,OAAO,EAAE,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,CAAC;KACxE,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,MAA+B,EAC/B,OAA+B;IAE/B,OAAO,cAAc,CACnB,qBAAqB,EACrB,MAAM,EACN;QACE,OAAO,EACL,OAAO,EAAE,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,CAAC;KACxE,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,iBAAiB,CACtB,uBAAuB,CACxB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO,iBAAiB,CACtB,sBAAsB,CACvB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,iBAAiB,CAStB,uBAAuB,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,iBAAiB,CAGtB,uBAAuB,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,wBAAwB;IACtC,OAAO,iBAAiB,CAGtB,yBAAyB,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,OAAO,iBAAiB,CAatB,6BAA6B,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,iBAAiB,CACtB,mBAAmB,CACpB,CAAC;AACJ,CAAC","sourcesContent":["import type {\n ReviewComment,\n ReviewCommentKind,\n ReviewMention,\n ReviewResolutionTarget,\n ReviewStatus,\n ReviewStatusEntry,\n} from \"../../review/types.js\";\nimport { useActionMutation, useActionQuery } from \"../use-action.js\";\n\nexport interface ListReviewCommentsParams {\n resourceType: string;\n resourceId: string;\n includeResolved?: boolean;\n includeDeleted?: boolean;\n targetId?: string | null;\n limit?: number;\n}\n\nexport interface ListReviewCommentsResult {\n comments: ReviewComment[];\n reviewStatus: ReviewStatusEntry | null;\n summary: {\n openCount: number;\n agentQueueCount: number;\n };\n}\n\nexport interface GetReviewFeedbackParams {\n resourceType: string;\n resourceId: string;\n includeHumanTargeted?: boolean;\n limit?: number;\n}\n\nexport interface GetReviewFeedbackResult {\n comments: ReviewComment[];\n}\n\nexport interface CreateReviewCommentInput {\n resourceType: string;\n resourceId: string;\n targetId?: string | null;\n kind?: ReviewCommentKind;\n anchor?: unknown | null;\n body: string;\n authorName?: string | null;\n resolutionTarget?: ReviewResolutionTarget | null;\n mentions?: ReviewMention[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface ReplyReviewCommentInput {\n resourceType: string;\n resourceId: string;\n commentId: string;\n body: string;\n authorName?: string | null;\n resolutionTarget?: ReviewResolutionTarget | null;\n mentions?: ReviewMention[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface ResolveReviewThreadInput {\n resourceType: string;\n resourceId: string;\n threadId?: string;\n commentId?: string;\n resolutionNote?: string;\n}\n\nexport interface DeleteReviewCommentInput {\n resourceType: string;\n resourceId: string;\n commentId: string;\n}\n\nexport interface ConsumeReviewFeedbackInput {\n resourceType: string;\n resourceId: string;\n commentIds: string[];\n}\n\nexport interface SendReviewThreadToAgentInput {\n resourceType: string;\n resourceId: string;\n threadId: string;\n}\n\nexport interface SetReviewStatusInput {\n resourceType: string;\n resourceId: string;\n status: ReviewStatus;\n note?: string | null;\n metadata?: Record<string, unknown>;\n}\n\nexport function useReviewComments(\n params: ListReviewCommentsParams,\n options?: { enabled?: boolean },\n) {\n return useActionQuery<ListReviewCommentsResult>(\n \"list-review-comments\",\n params,\n {\n enabled:\n options?.enabled ?? Boolean(params.resourceType && params.resourceId),\n },\n );\n}\n\nexport function useReviewFeedback(\n params: GetReviewFeedbackParams,\n options?: { enabled?: boolean },\n) {\n return useActionQuery<GetReviewFeedbackResult>(\n \"get-review-feedback\",\n params,\n {\n enabled:\n options?.enabled ?? Boolean(params.resourceType && params.resourceId),\n },\n );\n}\n\nexport function useCreateReviewComment() {\n return useActionMutation<ReviewComment, CreateReviewCommentInput>(\n \"create-review-comment\",\n );\n}\n\nexport function useReplyReviewComment() {\n return useActionMutation<ReviewComment, ReplyReviewCommentInput>(\n \"reply-review-comment\",\n );\n}\n\nexport function useResolveReviewThread() {\n return useActionMutation<\n {\n threadId: string;\n resolved: true;\n updatedCount: number;\n resolutionNote: string | null;\n comment: ReviewComment;\n },\n ResolveReviewThreadInput\n >(\"resolve-review-thread\");\n}\n\nexport function useDeleteReviewComment() {\n return useActionMutation<\n { commentId: string; deleted: true; updatedCount: number },\n DeleteReviewCommentInput\n >(\"delete-review-comment\");\n}\n\nexport function useConsumeReviewFeedback() {\n return useActionMutation<\n { consumedCommentIds: string[]; updatedCount: number },\n ConsumeReviewFeedbackInput\n >(\"consume-review-feedback\");\n}\n\nexport function useSendReviewThreadToAgent() {\n return useActionMutation<\n {\n resourceType: string;\n resourceId: string;\n threadId: string;\n resolutionTarget: \"agent\";\n consumedAt: null;\n updatedCount: number;\n ownerEmail: string | null;\n orgId: string | null;\n visibility: \"private\" | \"org\" | \"public\";\n },\n SendReviewThreadToAgentInput\n >(\"send-review-thread-to-agent\");\n}\n\nexport function useSetReviewStatus() {\n return useActionMutation<ReviewStatusEntry, SetReviewStatusInput>(\n \"set-review-status\",\n );\n}\n"]}
|
|
@@ -10,6 +10,6 @@ export interface UseAgentChatContextResult extends AgentChatContextState {
|
|
|
10
10
|
* composer's staged context chips. Simple send/prefill flows should use
|
|
11
11
|
* `sendToAgentChat({ message, context, submit })` directly.
|
|
12
12
|
*/
|
|
13
|
-
export declare function useAgentChatContext(): UseAgentChatContextResult;
|
|
13
|
+
export declare function useAgentChatContext(enabled?: boolean): UseAgentChatContextResult;
|
|
14
14
|
export type { AgentChatContextItem };
|
|
15
15
|
//# sourceMappingURL=use-agent-chat-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent-chat-context.d.ts","sourceRoot":"","sources":["../../src/client/use-agent-chat-context.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,oBAAoB,EACzB,KAAK,+BAA+B,EACpC,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAC;AAGzB,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;IACtE,GAAG,CAAC,IAAI,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,OAAO,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACvD,OAAO,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"use-agent-chat-context.d.ts","sourceRoot":"","sources":["../../src/client/use-agent-chat-context.ts"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,oBAAoB,EACzB,KAAK,+BAA+B,EACpC,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAC;AAGzB,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;IACtE,GAAG,CAAC,IAAI,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,OAAO,CAAC,EAAE,+BAA+B,GAAG,IAAI,CAAC;IACvD,OAAO,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,UAAO,GAAG,yBAAyB,CAgC7E;AAED,YAAY,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -6,12 +6,14 @@ import { useChangeVersion } from "./use-change-version.js";
|
|
|
6
6
|
* composer's staged context chips. Simple send/prefill flows should use
|
|
7
7
|
* `sendToAgentChat({ message, context, submit })` directly.
|
|
8
8
|
*/
|
|
9
|
-
export function useAgentChatContext() {
|
|
9
|
+
export function useAgentChatContext(enabled = true) {
|
|
10
10
|
const appStateVersion = useChangeVersion("app-state");
|
|
11
11
|
const state = useSyncExternalStore(subscribeAgentChatContext, getAgentChatContextState, getAgentChatContextState);
|
|
12
12
|
useEffect(() => {
|
|
13
|
+
if (!enabled)
|
|
14
|
+
return;
|
|
13
15
|
void refreshAgentChatContext();
|
|
14
|
-
}, [appStateVersion]);
|
|
16
|
+
}, [appStateVersion, enabled]);
|
|
15
17
|
const set = useCallback((item) => {
|
|
16
18
|
setAgentChatContextItem(item);
|
|
17
19
|
}, []);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent-chat-context.js","sourceRoot":"","sources":["../../src/client/use-agent-chat-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAErE,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,EAC1B,uBAAuB,EACvB,yBAAyB,GAK1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAS3D;;;;GAIG;AACH,MAAM,UAAU,mBAAmB;
|
|
1
|
+
{"version":3,"file":"use-agent-chat-context.js","sourceRoot":"","sources":["../../src/client/use-agent-chat-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAErE,OAAO,EACL,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,0BAA0B,EAC1B,uBAAuB,EACvB,yBAAyB,GAK1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAS3D;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAAG,IAAI;IAChD,MAAM,eAAe,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,oBAAoB,CAChC,yBAAyB,EACzB,wBAAwB,EACxB,wBAAwB,CACzB,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,KAAK,uBAAuB,EAAE,CAAC;IACjC,CAAC,EAAE,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC;IAE/B,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,IAAgC,EAAE,EAAE;QAC3D,uBAAuB,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,GAAW,EAAE,EAAE;QACzC,0BAA0B,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,OAAyC,EAAE,EAAE;QACtE,qBAAqB,CAAC,OAAO,CAAC,CAAC;IACjC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO;QACL,GAAG,KAAK;QACR,GAAG;QACH,MAAM;QACN,KAAK;QACL,OAAO,EAAE,uBAAuB;KACjC,CAAC;AACJ,CAAC","sourcesContent":["import { useCallback, useEffect, useSyncExternalStore } from \"react\";\n\nimport {\n clearAgentChatContext,\n getAgentChatContextState,\n refreshAgentChatContext,\n removeAgentChatContextItem,\n setAgentChatContextItem,\n subscribeAgentChatContext,\n type AgentChatContextItem,\n type AgentChatContextMutationOptions,\n type AgentChatContextSetOptions,\n type AgentChatContextState,\n} from \"./agent-chat.js\";\nimport { useChangeVersion } from \"./use-change-version.js\";\n\nexport interface UseAgentChatContextResult extends AgentChatContextState {\n set(item: AgentChatContextSetOptions): void;\n remove(key: string): void;\n clear(options?: AgentChatContextMutationOptions): void;\n refresh(): Promise<AgentChatContextState>;\n}\n\n/**\n * Advanced hook for UIs that need to stay in sync with the active chat\n * composer's staged context chips. Simple send/prefill flows should use\n * `sendToAgentChat({ message, context, submit })` directly.\n */\nexport function useAgentChatContext(enabled = true): UseAgentChatContextResult {\n const appStateVersion = useChangeVersion(\"app-state\");\n const state = useSyncExternalStore(\n subscribeAgentChatContext,\n getAgentChatContextState,\n getAgentChatContextState,\n );\n\n useEffect(() => {\n if (!enabled) return;\n void refreshAgentChatContext();\n }, [appStateVersion, enabled]);\n\n const set = useCallback((item: AgentChatContextSetOptions) => {\n setAgentChatContextItem(item);\n }, []);\n\n const remove = useCallback((key: string) => {\n removeAgentChatContextItem(key);\n }, []);\n\n const clear = useCallback((options?: AgentChatContextMutationOptions) => {\n clearAgentChatContext(options);\n }, []);\n\n return {\n ...state,\n set,\n remove,\n clear,\n refresh: refreshAgentChatContext,\n };\n}\n\nexport type { AgentChatContextItem };\n"]}
|
|
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
|
|
|
62
62
|
error: string;
|
|
63
63
|
states?: undefined;
|
|
64
64
|
} | {
|
|
65
|
+
error?: undefined;
|
|
65
66
|
states: {
|
|
66
67
|
clientId: number;
|
|
67
68
|
state: string;
|
|
68
69
|
}[];
|
|
69
|
-
error?: undefined;
|
|
70
70
|
}>>;
|
|
71
71
|
/**
|
|
72
72
|
* GET /_agent-native/collab/:docId/users
|
|
@@ -77,10 +77,10 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
|
|
|
77
77
|
error: string;
|
|
78
78
|
users?: undefined;
|
|
79
79
|
} | {
|
|
80
|
+
error?: undefined;
|
|
80
81
|
users: {
|
|
81
82
|
clientId: number;
|
|
82
83
|
lastSeen: number;
|
|
83
84
|
}[];
|
|
84
|
-
error?: undefined;
|
|
85
85
|
}>>;
|
|
86
86
|
//# sourceMappingURL=awareness.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD,wBAAgB,mBAAmB,IAAI,YAAY,CAElD;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,cAAc,GAAG,SAAS,GAChC,IAAI,CAON;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,KAAK,CAAC,EAAE,cAAc,GACrB,IAAI,CAgBN;AAoBD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAmB,GAC7B,IAAI,CAON;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1E;AAiBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAO1E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAOnE;AAkCD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAgB3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAOD,wBAAgB,mBAAmB,IAAI,YAAY,CAElD;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,cAAc,GAAG,SAAS,GAChC,IAAI,CAON;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,KAAK,CAAC,EAAE,cAAc,GACrB,IAAI,CAgBN;AAoBD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,SAAS,GAAE,MAAmB,GAC7B,IAAI,CAON;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAE1E;AAiBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAO1E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAOnE;AAkCD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;kBAwDa,MAAM;eAAS,MAAM;;GAoB1D,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;kBAYM,MAAM;kBAAY,MAAM;;GAMvD,CAAC"}
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
|
|
14
14
|
*/
|
|
15
15
|
export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
16
|
-
error: string;
|
|
17
16
|
ok?: undefined;
|
|
17
|
+
error: string;
|
|
18
18
|
} | {
|
|
19
19
|
error?: undefined;
|
|
20
20
|
ok: boolean;
|
|
@@ -14,16 +14,21 @@ export declare function resolveCredentialForScope(key: string, ctx: CredentialCo
|
|
|
14
14
|
scope: CredentialStorageScope;
|
|
15
15
|
}): Promise<string | undefined>;
|
|
16
16
|
/**
|
|
17
|
-
* Resolve a credential
|
|
18
|
-
*
|
|
17
|
+
* Resolve a credential across the encrypted app_secrets store and the legacy
|
|
18
|
+
* settings-backed credential store. User overrides win, followed by the
|
|
19
|
+
* active org/workspace shared value.
|
|
19
20
|
*
|
|
20
21
|
* SECURITY: NEVER reads from process.env. Env vars are global to the
|
|
21
|
-
* deployment and would leak across users in a multi-tenant app.
|
|
22
|
-
* sources are per-user / per-org rows in the SQL `settings` table.
|
|
22
|
+
* deployment and would leak across users in a multi-tenant app.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
* 1.
|
|
26
|
-
* 2.
|
|
24
|
+
* Read order:
|
|
25
|
+
* 1. user-scoped app_secrets
|
|
26
|
+
* 2. user-scoped legacy settings credential
|
|
27
|
+
* 3. org-scoped app_secrets
|
|
28
|
+
* 4. legacy workspace-scoped app_secrets for the org
|
|
29
|
+
* 5. org-scoped legacy settings credential
|
|
30
|
+
*
|
|
31
|
+
* Without an active org, step 3 is replaced by the solo workspace scope.
|
|
27
32
|
*/
|
|
28
33
|
export declare function resolveCredential(key: string, ctx: CredentialContext): Promise<string | undefined>;
|
|
29
34
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,KAAK,CAAC;AA2CpD;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,iBAAiB,GAAG;IAAE,KAAK,EAAE,sBAAsB,CAAA;CAAE,GACzD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAO7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA2B7B;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,OAAO,CAAC,CAElB;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,iBAAiB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;CAAE,GAClD,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,iBAAiB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAA;CAAE,GAClD,OAAO,CAAC,IAAI,CAAC,CAcf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { encryptSecretValue, decryptSecretValue, isEncryptedSecretValue, } from "../secrets/crypto.js";
|
|
2
|
+
import { readAppSecret } from "../secrets/storage.js";
|
|
2
3
|
import { getSetting, putSetting, deleteSetting } from "../settings/store.js";
|
|
3
4
|
const SETTING_PREFIX = "credential:";
|
|
4
5
|
function userCredentialSettingKey(email, key) {
|
|
@@ -26,6 +27,16 @@ async function readCredentialSetting(settingKey) {
|
|
|
26
27
|
return undefined;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
30
|
+
async function readScopedAppSecret(key, scope, scopeId) {
|
|
31
|
+
try {
|
|
32
|
+
return (await readAppSecret({ key, scope, scopeId }))?.value;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Older databases may not have app_secrets yet. Keep the legacy
|
|
36
|
+
// credential store available while the table bootstraps.
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
29
40
|
/**
|
|
30
41
|
* Resolve a credential from one explicit legacy SQL credential scope.
|
|
31
42
|
*
|
|
@@ -44,20 +55,28 @@ export async function resolveCredentialForScope(key, ctx) {
|
|
|
44
55
|
return readCredentialSetting(userCredentialSettingKey(ctx.userEmail, key));
|
|
45
56
|
}
|
|
46
57
|
/**
|
|
47
|
-
* Resolve a credential
|
|
48
|
-
*
|
|
58
|
+
* Resolve a credential across the encrypted app_secrets store and the legacy
|
|
59
|
+
* settings-backed credential store. User overrides win, followed by the
|
|
60
|
+
* active org/workspace shared value.
|
|
49
61
|
*
|
|
50
62
|
* SECURITY: NEVER reads from process.env. Env vars are global to the
|
|
51
|
-
* deployment and would leak across users in a multi-tenant app.
|
|
52
|
-
*
|
|
63
|
+
* deployment and would leak across users in a multi-tenant app.
|
|
64
|
+
*
|
|
65
|
+
* Read order:
|
|
66
|
+
* 1. user-scoped app_secrets
|
|
67
|
+
* 2. user-scoped legacy settings credential
|
|
68
|
+
* 3. org-scoped app_secrets
|
|
69
|
+
* 4. legacy workspace-scoped app_secrets for the org
|
|
70
|
+
* 5. org-scoped legacy settings credential
|
|
53
71
|
*
|
|
54
|
-
*
|
|
55
|
-
* 1. u:<email>:credential:<KEY> — per-user override
|
|
56
|
-
* 2. o:<orgId>:credential:<KEY> — per-org shared credential (if orgId given)
|
|
72
|
+
* Without an active org, step 3 is replaced by the solo workspace scope.
|
|
57
73
|
*/
|
|
58
74
|
export async function resolveCredential(key, ctx) {
|
|
59
75
|
if (!ctx?.userEmail)
|
|
60
76
|
return undefined;
|
|
77
|
+
const userSecret = await readScopedAppSecret(key, "user", ctx.userEmail);
|
|
78
|
+
if (userSecret)
|
|
79
|
+
return userSecret;
|
|
61
80
|
const userSetting = await resolveCredentialForScope(key, {
|
|
62
81
|
...ctx,
|
|
63
82
|
scope: "user",
|
|
@@ -65,9 +84,15 @@ export async function resolveCredential(key, ctx) {
|
|
|
65
84
|
if (userSetting)
|
|
66
85
|
return userSetting;
|
|
67
86
|
if (ctx.orgId) {
|
|
87
|
+
const orgSecret = await readScopedAppSecret(key, "org", ctx.orgId);
|
|
88
|
+
if (orgSecret)
|
|
89
|
+
return orgSecret;
|
|
90
|
+
const workspaceSecret = await readScopedAppSecret(key, "workspace", ctx.orgId);
|
|
91
|
+
if (workspaceSecret)
|
|
92
|
+
return workspaceSecret;
|
|
68
93
|
return resolveCredentialForScope(key, { ...ctx, scope: "org" });
|
|
69
94
|
}
|
|
70
|
-
return
|
|
95
|
+
return readScopedAppSecret(key, "workspace", `solo:${ctx.userEmail}`);
|
|
71
96
|
}
|
|
72
97
|
/**
|
|
73
98
|
* Check if a credential is available for the given context.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE7E,MAAM,cAAc,GAAG,aAAa,CAAC;AASrC,SAAS,wBAAwB,CAAC,KAAa,EAAE,GAAW;IAC1D,OAAO,KAAK,KAAK,CAAC,WAAW,EAAE,IAAI,cAAc,GAAG,GAAG,EAAE,CAAC;AAC5D,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAa,EAAE,GAAW;IACzD,OAAO,KAAK,KAAK,IAAI,cAAc,GAAG,GAAG,EAAE,CAAC;AAC9C,CAAC;AAED,KAAK,UAAU,qBAAqB,CAClC,UAAkB;IAElB,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC;IAC7C,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IACpE,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAC7B,sEAAsE;IACtE,uEAAuE;IACvE,sEAAsE;IACtE,IAAI,CAAC,sBAAsB,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IACnD,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;QACvE,iEAAiE;QACjE,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,GAAW,EACX,GAA0D;IAE1D,IAAI,CAAC,GAAG,EAAE,SAAS;QAAE,OAAO,SAAS,CAAC;IACtC,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QACjC,OAAO,qBAAqB,CAAC,uBAAuB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,qBAAqB,CAAC,wBAAwB,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/credentials/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n encryptSecretValue,\n decryptSecretValue,\n isEncryptedSecretValue,\n} from \"../secrets/crypto.js\";\nimport { readAppSecret, type SecretRef } from \"../secrets/storage.js\";\nimport { getSetting, putSetting, deleteSetting } from \"../settings/store.js\";\n\nconst SETTING_PREFIX = \"credential:\";\n\nexport interface CredentialContext {\n userEmail: string;\n orgId?: string | null;\n}\n\nexport type CredentialStorageScope = \"user\" | \"org\";\n\nfunction userCredentialSettingKey(email: string, key: string): string {\n return `u:${email.toLowerCase()}:${SETTING_PREFIX}${key}`;\n}\n\nfunction orgCredentialSettingKey(orgId: string, key: string): string {\n return `o:${orgId}:${SETTING_PREFIX}${key}`;\n}\n\nasync function readCredentialSetting(\n settingKey: string,\n): Promise<string | undefined> {\n const setting = await getSetting(settingKey);\n if (!setting || typeof setting.value !== \"string\") return undefined;\n const stored = setting.value;\n // Values written by saveCredential are AES-256-GCM encrypted at rest.\n // Rows that predate encryption are plaintext — read them transparently\n // (the migrate-encrypt-credentials script re-encrypts them in place).\n if (!isEncryptedSecretValue(stored)) return stored;\n try {\n return decryptSecretValue(stored);\n } catch {\n // Key rotated, corrupt, or tampered row — treat as not set rather than\n // surfacing ciphertext or throwing into every credential lookup.\n return undefined;\n }\n}\n\nasync function readScopedAppSecret(\n key: string,\n scope: SecretRef[\"scope\"],\n scopeId: string,\n): Promise<string | undefined> {\n try {\n return (await readAppSecret({ key, scope, scopeId }))?.value;\n } catch {\n // Older databases may not have app_secrets yet. Keep the legacy\n // credential store available while the table bootstraps.\n return undefined;\n }\n}\n\n/**\n * Resolve a credential from one explicit legacy SQL credential scope.\n *\n * Prefer `resolveCredential()` for normal app-local credential lookup. This\n * helper exists for workspace connection refs, where a ref can explicitly say\n * \"use the org-scoped key\" and must not accidentally read a user override.\n */\nexport async function resolveCredentialForScope(\n key: string,\n ctx: CredentialContext & { scope: CredentialStorageScope },\n): Promise<string | undefined> {\n if (!ctx?.userEmail) return undefined;\n if (ctx.scope === \"org\") {\n if (!ctx.orgId) return undefined;\n return readCredentialSetting(orgCredentialSettingKey(ctx.orgId, key));\n }\n return readCredentialSetting(userCredentialSettingKey(ctx.userEmail, key));\n}\n\n/**\n * Resolve a credential across the encrypted app_secrets store and the legacy\n * settings-backed credential store. User overrides win, followed by the\n * active org/workspace shared value.\n *\n * SECURITY: NEVER reads from process.env. Env vars are global to the\n * deployment and would leak across users in a multi-tenant app.\n *\n * Read order:\n * 1. user-scoped app_secrets\n * 2. user-scoped legacy settings credential\n * 3. org-scoped app_secrets\n * 4. legacy workspace-scoped app_secrets for the org\n * 5. org-scoped legacy settings credential\n *\n * Without an active org, step 3 is replaced by the solo workspace scope.\n */\nexport async function resolveCredential(\n key: string,\n ctx: CredentialContext,\n): Promise<string | undefined> {\n if (!ctx?.userEmail) return undefined;\n\n const userSecret = await readScopedAppSecret(key, \"user\", ctx.userEmail);\n if (userSecret) return userSecret;\n\n const userSetting = await resolveCredentialForScope(key, {\n ...ctx,\n scope: \"user\",\n });\n if (userSetting) return userSetting;\n\n if (ctx.orgId) {\n const orgSecret = await readScopedAppSecret(key, \"org\", ctx.orgId);\n if (orgSecret) return orgSecret;\n\n const workspaceSecret = await readScopedAppSecret(\n key,\n \"workspace\",\n ctx.orgId,\n );\n if (workspaceSecret) return workspaceSecret;\n\n return resolveCredentialForScope(key, { ...ctx, scope: \"org\" });\n }\n\n return readScopedAppSecret(key, \"workspace\", `solo:${ctx.userEmail}`);\n}\n\n/**\n * Check if a credential is available for the given context.\n */\nexport async function hasCredential(\n key: string,\n ctx: CredentialContext,\n): Promise<boolean> {\n return (await resolveCredential(key, ctx)) !== undefined;\n}\n\n/**\n * Save a credential. By default writes to the per-user store; pass\n * `scope: \"org\"` to write to the active org's shared credentials.\n */\nexport async function saveCredential(\n key: string,\n value: string,\n ctx: CredentialContext & { scope?: \"user\" | \"org\" },\n): Promise<void> {\n if (!ctx?.userEmail) {\n throw new Error(\"saveCredential requires CredentialContext with userEmail\");\n }\n // Encrypt at rest (AES-256-GCM) so a leaked DB backup / pg_dump / read\n // replica doesn't expose plaintext keys. resolveCredential decrypts\n // transparently on read.\n const encrypted = encryptSecretValue(value);\n if (ctx.scope === \"org\") {\n if (!ctx.orgId) {\n throw new Error(\"saveCredential scope='org' requires orgId\");\n }\n await putSetting(orgCredentialSettingKey(ctx.orgId, key), {\n value: encrypted,\n });\n return;\n }\n await putSetting(userCredentialSettingKey(ctx.userEmail, key), {\n value: encrypted,\n });\n}\n\n/**\n * Delete a credential from the per-user (default) or per-org store.\n */\nexport async function deleteCredential(\n key: string,\n ctx: CredentialContext & { scope?: \"user\" | \"org\" },\n): Promise<void> {\n if (!ctx?.userEmail) {\n throw new Error(\n \"deleteCredential requires CredentialContext with userEmail\",\n );\n }\n if (ctx.scope === \"org\") {\n if (!ctx.orgId) {\n throw new Error(\"deleteCredential scope='org' requires orgId\");\n }\n await deleteSetting(orgCredentialSettingKey(ctx.orgId, key));\n return;\n }\n await deleteSetting(userCredentialSettingKey(ctx.userEmail, key));\n}\n"]}
|
|
@@ -10,15 +10,27 @@
|
|
|
10
10
|
* the raw secret never enters the model's context.
|
|
11
11
|
*/
|
|
12
12
|
import type { ActionEntry } from "../agent/production-agent.js";
|
|
13
|
+
import type { ResolvedKeyReference } from "../secrets/substitution.js";
|
|
13
14
|
export interface FetchToolOptions {
|
|
14
15
|
/** Resolve ${keys.NAME} references. Injected by the plugin at setup time. */
|
|
15
16
|
resolveKeys?: (text: string) => Promise<{
|
|
16
17
|
resolved: string;
|
|
17
18
|
usedKeys: string[];
|
|
18
19
|
secretValues?: string[];
|
|
20
|
+
/**
|
|
21
|
+
* Optional: which scope (user/org/workspace) each used key actually
|
|
22
|
+
* resolved at. Populated by resolvers like
|
|
23
|
+
* `resolveKeyReferencesWithRequestScopes` so `validateUrl` can look up
|
|
24
|
+
* the allowlist at the scope the value came from rather than assuming
|
|
25
|
+
* user scope. Backwards compatible — callers that omit this (or a
|
|
26
|
+
* resolver that doesn't report it) are unaffected.
|
|
27
|
+
*/
|
|
28
|
+
resolvedKeys?: ResolvedKeyReference[];
|
|
19
29
|
}>;
|
|
20
30
|
/** Validate URL against per-key allowlists. */
|
|
21
|
-
validateUrl?: (url: string, usedKeys: string[]
|
|
31
|
+
validateUrl?: (url: string, usedKeys: string[],
|
|
32
|
+
/** Accumulated `resolvedKeys` from every `resolveKeys` call this request made, when the resolver reported them. */
|
|
33
|
+
resolvedKeys?: ResolvedKeyReference[]) => Promise<boolean>;
|
|
22
34
|
}
|
|
23
35
|
/**
|
|
24
36
|
* Create the fetch tool entry for the agent tool registry.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-tool.d.ts","sourceRoot":"","sources":["../../src/extensions/fetch-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch-tool.d.ts","sourceRoot":"","sources":["../../src/extensions/fetch-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AA6DvE,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QACtC,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB;;;;;;;WAOG;QACH,YAAY,CAAC,EAAE,oBAAoB,EAAE,CAAC;KACvC,CAAC,CAAC;IACH,+CAA+C;IAC/C,WAAW,CAAC,EAAE,CACZ,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,EAAE;IAClB,mHAAmH;IACnH,YAAY,CAAC,EAAE,oBAAoB,EAAE,KAClC,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,GAAE,gBAAqB,GAC1B,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CA8V7B"}
|
|
@@ -153,21 +153,25 @@ export function createFetchToolEntry(opts = {}) {
|
|
|
153
153
|
let resolvedBody = rawBody;
|
|
154
154
|
const allUsedKeys = [];
|
|
155
155
|
const allSecretValues = [];
|
|
156
|
+
const allResolvedKeys = [];
|
|
156
157
|
if (opts.resolveKeys) {
|
|
157
158
|
try {
|
|
158
159
|
const urlResult = await opts.resolveKeys(rawUrl);
|
|
159
160
|
resolvedUrl = urlResult.resolved;
|
|
160
161
|
allUsedKeys.push(...urlResult.usedKeys);
|
|
161
162
|
allSecretValues.push(...(urlResult.secretValues ?? []));
|
|
163
|
+
allResolvedKeys.push(...(urlResult.resolvedKeys ?? []));
|
|
162
164
|
const headerResult = await opts.resolveKeys(rawHeaders);
|
|
163
165
|
resolvedHeaders = headerResult.resolved;
|
|
164
166
|
allUsedKeys.push(...headerResult.usedKeys);
|
|
165
167
|
allSecretValues.push(...(headerResult.secretValues ?? []));
|
|
168
|
+
allResolvedKeys.push(...(headerResult.resolvedKeys ?? []));
|
|
166
169
|
if (rawBody) {
|
|
167
170
|
const bodyResult = await opts.resolveKeys(rawBody);
|
|
168
171
|
resolvedBody = bodyResult.resolved;
|
|
169
172
|
allUsedKeys.push(...bodyResult.usedKeys);
|
|
170
173
|
allSecretValues.push(...(bodyResult.secretValues ?? []));
|
|
174
|
+
allResolvedKeys.push(...(bodyResult.resolvedKeys ?? []));
|
|
171
175
|
}
|
|
172
176
|
}
|
|
173
177
|
catch (err) {
|
|
@@ -175,6 +179,9 @@ export function createFetchToolEntry(opts = {}) {
|
|
|
175
179
|
}
|
|
176
180
|
}
|
|
177
181
|
const secretValues = collectSecretValues(allSecretValues);
|
|
182
|
+
const resolvedKeys = allResolvedKeys.length
|
|
183
|
+
? allResolvedKeys
|
|
184
|
+
: undefined;
|
|
178
185
|
// Block SSRF targets regardless of key usage
|
|
179
186
|
if (await isBlockedExtensionUrlWithDns(resolvedUrl)) {
|
|
180
187
|
return `Requests to private/internal addresses are not allowed: "${rawUrl}".`;
|
|
@@ -182,7 +189,7 @@ export function createFetchToolEntry(opts = {}) {
|
|
|
182
189
|
// Validate URL against per-key allowlists
|
|
183
190
|
if (opts.validateUrl && allUsedKeys.length > 0) {
|
|
184
191
|
try {
|
|
185
|
-
const allowed = await opts.validateUrl(resolvedUrl, allUsedKeys);
|
|
192
|
+
const allowed = await opts.validateUrl(resolvedUrl, allUsedKeys, resolvedKeys);
|
|
186
193
|
if (!allowed) {
|
|
187
194
|
return `URL "${rawUrl}" is not in the allowlist for the referenced keys. Check your key settings.`;
|
|
188
195
|
}
|
|
@@ -236,7 +243,7 @@ export function createFetchToolEntry(opts = {}) {
|
|
|
236
243
|
return "Redirect to private/internal address blocked.";
|
|
237
244
|
}
|
|
238
245
|
if (redirectUrl && opts.validateUrl && allUsedKeys.length > 0) {
|
|
239
|
-
const allowed = await opts.validateUrl(redirectUrl, allUsedKeys);
|
|
246
|
+
const allowed = await opts.validateUrl(redirectUrl, allUsedKeys, resolvedKeys);
|
|
240
247
|
if (!allowed) {
|
|
241
248
|
return "Redirect URL is not in the allowlist for the referenced keys.";
|
|
242
249
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-tool.js","sourceRoot":"","sources":["../../src/extensions/fetch-tool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Fetch tool — outbound HTTP for automations and agent use.\n *\n * NOTE: this is an *agent* tool (LLM function call), not an *extension* (the\n * sandboxed Alpine.js mini-app primitive). It lives in this directory because\n * it shares SSRF-safe URL/proxy helpers with the extension iframe proxy.\n *\n * Supports ${keys.NAME} reference substitution in URL, headers, and body.\n * Values are resolved server-side AFTER the model emits the tool call —\n * the raw secret never enters the model's context.\n */\n\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport {\n collectSecretValues,\n MAX_EXTENSION_PROXY_RESPONSE_SIZE,\n normalizeExtensionProxyMethod,\n readResponseTextWithLimit,\n redactSecrets,\n redactString,\n sanitizeOutboundHeaders,\n} from \"./proxy-security.js\";\nimport {\n createSsrfSafeDispatcher,\n isBlockedExtensionUrlWithDns,\n} from \"./url-safety.js\";\nimport {\n formatWebContentResult,\n parseWebContentSearchOptions,\n processWebContent,\n} from \"./web-content.js\";\n\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/**\n * Headers that mimic a current Chrome on macOS so anti-bot middleware (Cloudflare,\n * PerimeterX, Akamai) treats the request as a real user. We only fill in fields\n * the caller hasn't supplied — explicit headers (e.g. an `Authorization` header\n * for an API call) always win.\n *\n * `Accept-Encoding` deliberately omits `zstd` because Node's undici fetch only\n * decompresses `gzip`, `deflate`, and `br`. Advertising `zstd` would let some\n * servers send bytes we can't decode.\n */\nconst BROWSER_DEFAULT_HEADERS: Record<string, string> = {\n \"User-Agent\":\n \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\n Accept:\n \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\n \"Accept-Language\": \"en-US,en;q=0.9\",\n \"Accept-Encoding\": \"gzip, deflate, br\",\n \"Sec-Ch-Ua\":\n '\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"',\n \"Sec-Ch-Ua-Mobile\": \"?0\",\n \"Sec-Ch-Ua-Platform\": '\"macOS\"',\n \"Sec-Fetch-Dest\": \"document\",\n \"Sec-Fetch-Mode\": \"navigate\",\n \"Sec-Fetch-Site\": \"none\",\n \"Sec-Fetch-User\": \"?1\",\n \"Upgrade-Insecure-Requests\": \"1\",\n};\n\nfunction applyBrowserDefaults(\n headers: Record<string, string>,\n): Record<string, string> {\n const seen = new Set(Object.keys(headers).map((k) => k.toLowerCase()));\n const merged = { ...headers };\n for (const [name, value] of Object.entries(BROWSER_DEFAULT_HEADERS)) {\n if (!seen.has(name.toLowerCase())) merged[name] = value;\n }\n return merged;\n}\n\nexport interface FetchToolOptions {\n /** Resolve ${keys.NAME} references. Injected by the plugin at setup time. */\n resolveKeys?: (text: string) => Promise<{\n resolved: string;\n usedKeys: string[];\n secretValues?: string[];\n }>;\n /** Validate URL against per-key allowlists. */\n validateUrl?: (url: string, usedKeys: string[]) => Promise<boolean>;\n}\n\n/**\n * Create the fetch tool entry for the agent tool registry.\n */\nexport function createFetchToolEntry(\n opts: FetchToolOptions = {},\n): Record<string, ActionEntry> {\n return {\n \"web-request\": {\n tool: {\n description: `Make an outbound HTTP request to any EXTERNAL URL — APIs, webhooks, and arbitrary web pages (HTML, RSS, JSON, etc.). Use this to fetch the contents of a URL the user pastes in chat. Sends realistic Chrome-on-macOS headers by default (User-Agent, Accept, Sec-Fetch-*) so most sites that block obvious bots will respond normally; pass an explicit header to override any default. Supports \\${keys.NAME} placeholders in url, headers, and body — these are resolved server-side from the user's saved keys (the raw value never enters your context). Example: \\${keys.SLACK_WEBHOOK} in the url field. IMPORTANT: Never use this to call internal /_agent-native/ endpoints or localhost action URLs — use the registered actions directly (e.g. \\`search-records\\`, \\`provider-api-request\\`, \\`update-resource\\`). Actions are already available as native tools; calling them via HTTP is slower and bypasses validation.`,\n parameters: {\n type: \"object\" as const,\n properties: {\n url: {\n type: \"string\",\n description:\n 'Full URL. May contain ${keys.NAME} references, e.g. \"${keys.SLACK_WEBHOOK}\".',\n },\n method: {\n type: \"string\",\n description: \"HTTP method. Default: GET.\",\n enum: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\"],\n },\n headers: {\n type: \"string\",\n description:\n 'JSON object of headers. May contain ${keys.NAME} references. Example: \\'{\"Authorization\": \"Bearer ${keys.API_TOKEN}\"}\\'.',\n },\n body: {\n type: \"string\",\n description:\n \"Request body (for POST/PUT/PATCH). May contain ${keys.NAME} references.\",\n },\n timeout_ms: {\n type: \"number\",\n description: `Timeout in milliseconds. Default: ${DEFAULT_TIMEOUT_MS}. Max: 30000.`,\n },\n maxChars: {\n type: \"number\",\n description:\n \"Maximum response body characters to return. Default: 32000. Max: 200000. Increase when you need to read a large document, API response, or dataset.\",\n },\n responseMode: {\n type: \"string\",\n description:\n \"How to return the response. Default: auto (HTML pages become clean markdown; JSON/text stays raw). Use raw for exact bytes, markdown/text for extracted readable content, links for just links, metadata for page metadata, or matches with search.\",\n enum: [\n \"auto\",\n \"raw\",\n \"text\",\n \"markdown\",\n \"links\",\n \"metadata\",\n \"matches\",\n ],\n },\n extract: {\n type: \"string\",\n description:\n \"HTML extraction strategy. Default: readability. Use all-visible for visible body text/markdown, or none to convert the full HTML document.\",\n enum: [\"readability\", \"all-visible\", \"none\"],\n },\n includeLinks: {\n type: \"boolean\",\n description:\n \"Whether extracted HTML responses should include a compact links list. Default: true for extracted pages.\",\n },\n search: {\n type: \"object\",\n description:\n \"Optional post-fetch search over extracted content by default. Supports {query, queries, terms, regex, regexFlags, source:'extracted'|'raw', maxMatches, contextChars, caseSensitive}. Regex is safety-checked and bounded; prefer query/terms for simple grep-like searches.\",\n properties: {\n query: { type: \"string\" },\n queries: { type: \"array\", items: { type: \"string\" } },\n terms: { type: \"array\", items: { type: \"string\" } },\n regex: { type: \"string\" },\n regexFlags: { type: \"string\" },\n source: { type: \"string\", enum: [\"extracted\", \"raw\"] },\n maxMatches: { type: \"number\" },\n contextChars: { type: \"number\" },\n caseSensitive: { type: \"boolean\" },\n },\n } as any,\n saveToFile: {\n type: \"string\",\n description:\n \"Workspace file path to save the full response body to instead of returning it in context (e.g. 'analysis/page.html'). When set, returns only a compact summary {savedTo, status, bytes, preview}. Useful for large web pages or API responses that would overflow context.\",\n },\n },\n required: [\"url\"],\n },\n },\n run: async (args: Record<string, unknown>) => {\n const startTime = Date.now();\n const rawUrl = String(args.url ?? \"\");\n const method = normalizeExtensionProxyMethod(args.method || \"GET\");\n if (!method) {\n return \"Unsupported HTTP method. Allowed methods: GET, POST, PUT, PATCH, DELETE, HEAD.\";\n }\n const rawHeaders =\n typeof args.headers === \"string\"\n ? args.headers\n : JSON.stringify(args.headers ?? {});\n const rawBody =\n typeof args.body === \"string\"\n ? args.body\n : args.body === undefined || args.body === null\n ? undefined\n : JSON.stringify(args.body);\n const timeoutMs = Math.min(\n Number(args.timeout_ms) || DEFAULT_TIMEOUT_MS,\n 30_000,\n );\n const requestedMaxChars = Number(args.maxChars);\n const maxChars =\n Number.isFinite(requestedMaxChars) && requestedMaxChars > 0\n ? Math.min(requestedMaxChars, 200_000)\n : 32_000;\n\n // Resolve key references\n let resolvedUrl = rawUrl;\n let resolvedHeaders = rawHeaders;\n let resolvedBody = rawBody;\n const allUsedKeys: string[] = [];\n const allSecretValues: string[] = [];\n\n if (opts.resolveKeys) {\n try {\n const urlResult = await opts.resolveKeys(rawUrl);\n resolvedUrl = urlResult.resolved;\n allUsedKeys.push(...urlResult.usedKeys);\n allSecretValues.push(...(urlResult.secretValues ?? []));\n\n const headerResult = await opts.resolveKeys(rawHeaders);\n resolvedHeaders = headerResult.resolved;\n allUsedKeys.push(...headerResult.usedKeys);\n allSecretValues.push(...(headerResult.secretValues ?? []));\n\n if (rawBody) {\n const bodyResult = await opts.resolveKeys(rawBody);\n resolvedBody = bodyResult.resolved;\n allUsedKeys.push(...bodyResult.usedKeys);\n allSecretValues.push(...(bodyResult.secretValues ?? []));\n }\n } catch (err: any) {\n return `Error resolving key references: ${err?.message ?? err}`;\n }\n }\n const secretValues = collectSecretValues(allSecretValues);\n\n // Block SSRF targets regardless of key usage\n if (await isBlockedExtensionUrlWithDns(resolvedUrl)) {\n return `Requests to private/internal addresses are not allowed: \"${rawUrl}\".`;\n }\n\n // Validate URL against per-key allowlists\n if (opts.validateUrl && allUsedKeys.length > 0) {\n try {\n const allowed = await opts.validateUrl(resolvedUrl, allUsedKeys);\n if (!allowed) {\n return `URL \"${rawUrl}\" is not in the allowlist for the referenced keys. Check your key settings.`;\n }\n } catch (err: any) {\n return `URL validation error: ${err?.message ?? err}`;\n }\n }\n\n // Parse headers, then merge in browser-like defaults for any header the\n // caller didn't already specify. Real-browser headers (User-Agent,\n // Accept, Sec-Fetch-*) are what gets you past Cloudflare / PerimeterX /\n // generic UA-sniffing middleware on sites the user pastes in chat;\n // explicit caller headers always win so API calls keep their auth\n // headers untouched.\n let headers: Record<string, string>;\n try {\n headers = sanitizeOutboundHeaders(JSON.parse(resolvedHeaders));\n } catch {\n return `Invalid headers JSON: ${rawHeaders}`;\n }\n headers = applyBrowserDefaults(headers);\n\n // Make the request\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const dispatcher = (await createSsrfSafeDispatcher()) ?? undefined;\n const fetchOpts: RequestInit & { dispatcher?: unknown } = {\n method,\n headers,\n signal: controller.signal,\n redirect: \"manual\",\n };\n if (dispatcher) fetchOpts.dispatcher = dispatcher;\n if (resolvedBody && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n fetchOpts.body = resolvedBody;\n if (!headers[\"content-type\"] && !headers[\"Content-Type\"]) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n }\n\n const response = await fetch(resolvedUrl, fetchOpts);\n const elapsed = Date.now() - startTime;\n\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"location\");\n const redirectUrl = location\n ? new URL(location, resolvedUrl).href\n : null;\n if (\n redirectUrl &&\n (await isBlockedExtensionUrlWithDns(redirectUrl))\n ) {\n return \"Redirect to private/internal address blocked.\";\n }\n if (redirectUrl && opts.validateUrl && allUsedKeys.length > 0) {\n const allowed = await opts.validateUrl(redirectUrl, allUsedKeys);\n if (!allowed) {\n return \"Redirect URL is not in the allowlist for the referenced keys.\";\n }\n }\n return `HTTP ${response.status} ${response.statusText}\\n\\nRedirect: ${\n redirectUrl ? redactString(redirectUrl, secretValues) : \"(none)\"\n }`;\n }\n\n // Check if caller wants to save to workspace file (before truncation).\n const saveToFilePath =\n typeof (args as Record<string, unknown>).saveToFile === \"string\"\n ? ((args as Record<string, unknown>).saveToFile as string).trim()\n : \"\";\n\n let body: string;\n try {\n // When saving to file allow larger reads (20MB), otherwise cap at proxy limit.\n const readLimit = saveToFilePath\n ? 20 * 1024 * 1024\n : MAX_EXTENSION_PROXY_RESPONSE_SIZE;\n const result = await readResponseTextWithLimit(response, readLimit);\n body = result.text;\n } catch {\n body = \"(could not read response body)\";\n }\n body = redactString(body, secretValues);\n const contentType =\n response.headers.get(\"content-type\")?.split(\";\")[0].trim() ??\n \"text/plain\";\n let displayBody: string;\n let processedMode = \"raw\";\n try {\n const processed = processWebContent({\n url: resolvedUrl,\n body,\n contentType,\n responseMode: String(args.responseMode ?? \"auto\"),\n extract: String(args.extract ?? \"readability\"),\n includeLinks:\n args.includeLinks === undefined\n ? true\n : parseBooleanArg(args.includeLinks),\n search: parseWebContentSearchOptions(args.search),\n maxChars,\n });\n processedMode = processed.mode;\n displayBody = formatWebContentResult(processed);\n } catch (err: any) {\n return `web-request post-processing error: ${err?.message ?? String(err)}`;\n }\n\n // Audit log\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → ${response.status} (${elapsed}ms, keys: ${allUsedKeys.join(\",\") || \"none\"})`,\n );\n\n // saveToFile: write full body to workspace and return compact summary.\n if (saveToFilePath) {\n try {\n const { writeWorkspaceFile, SAVE_TO_FILE_MAX_BYTES } =\n await import(\"../workspace-files/store.js\");\n const { getRequestOrgId, getRequestUserEmail } =\n await import(\"../server/request-context.js\");\n const orgId = getRequestOrgId();\n const email = getRequestUserEmail();\n const scope = orgId\n ? { scope: \"org\" as const, scopeId: orgId }\n : email\n ? { scope: \"user\" as const, scopeId: email }\n : null;\n if (!scope)\n throw new Error(\"No authenticated context for saveToFile\");\n await writeWorkspaceFile(\n scope,\n saveToFilePath,\n body,\n contentType,\n {\n maxFileBytes: SAVE_TO_FILE_MAX_BYTES,\n },\n );\n const bytes = Buffer.byteLength(body, \"utf8\");\n const preview = displayBody.slice(0, 2000);\n return JSON.stringify({\n savedToFile: true,\n savedTo: saveToFilePath,\n status: response.status,\n bytes,\n contentType,\n responseMode: processedMode,\n preview:\n preview.length < displayBody.length ? `${preview}…` : preview,\n });\n } catch (saveErr: any) {\n return `saveToFile error: ${saveErr?.message ?? String(saveErr)}\\n\\nHTTP ${response.status} ${response.statusText}\\n\\n${body.slice(0, maxChars)}`;\n }\n }\n\n return `HTTP ${response.status} ${response.statusText}\\n\\n${displayBody}`;\n } catch (err: any) {\n const elapsed = Date.now() - startTime;\n if (err?.name === \"AbortError\") {\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → TIMEOUT (${elapsed}ms)`,\n );\n return `Request timed out after ${timeoutMs}ms.`;\n }\n const message = redactSecrets(\n err?.message ?? String(err),\n secretValues,\n );\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → ERROR: ${message} (${elapsed}ms)`,\n );\n return `Request failed: ${message}`;\n } finally {\n clearTimeout(timeout);\n }\n },\n readOnly: true,\n },\n };\n}\n\nfunction parseBooleanArg(value: unknown): boolean {\n if (typeof value === \"boolean\") return value;\n const normalized = String(value).trim().toLowerCase();\n return normalized === \"true\" || normalized === \"1\" || normalized === \"yes\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fetch-tool.js","sourceRoot":"","sources":["../../src/extensions/fetch-tool.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Fetch tool — outbound HTTP for automations and agent use.\n *\n * NOTE: this is an *agent* tool (LLM function call), not an *extension* (the\n * sandboxed Alpine.js mini-app primitive). It lives in this directory because\n * it shares SSRF-safe URL/proxy helpers with the extension iframe proxy.\n *\n * Supports ${keys.NAME} reference substitution in URL, headers, and body.\n * Values are resolved server-side AFTER the model emits the tool call —\n * the raw secret never enters the model's context.\n */\n\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport type { ResolvedKeyReference } from \"../secrets/substitution.js\";\nimport {\n collectSecretValues,\n MAX_EXTENSION_PROXY_RESPONSE_SIZE,\n normalizeExtensionProxyMethod,\n readResponseTextWithLimit,\n redactSecrets,\n redactString,\n sanitizeOutboundHeaders,\n} from \"./proxy-security.js\";\nimport {\n createSsrfSafeDispatcher,\n isBlockedExtensionUrlWithDns,\n} from \"./url-safety.js\";\nimport {\n formatWebContentResult,\n parseWebContentSearchOptions,\n processWebContent,\n} from \"./web-content.js\";\n\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/**\n * Headers that mimic a current Chrome on macOS so anti-bot middleware (Cloudflare,\n * PerimeterX, Akamai) treats the request as a real user. We only fill in fields\n * the caller hasn't supplied — explicit headers (e.g. an `Authorization` header\n * for an API call) always win.\n *\n * `Accept-Encoding` deliberately omits `zstd` because Node's undici fetch only\n * decompresses `gzip`, `deflate`, and `br`. Advertising `zstd` would let some\n * servers send bytes we can't decode.\n */\nconst BROWSER_DEFAULT_HEADERS: Record<string, string> = {\n \"User-Agent\":\n \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36\",\n Accept:\n \"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8\",\n \"Accept-Language\": \"en-US,en;q=0.9\",\n \"Accept-Encoding\": \"gzip, deflate, br\",\n \"Sec-Ch-Ua\":\n '\"Google Chrome\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"',\n \"Sec-Ch-Ua-Mobile\": \"?0\",\n \"Sec-Ch-Ua-Platform\": '\"macOS\"',\n \"Sec-Fetch-Dest\": \"document\",\n \"Sec-Fetch-Mode\": \"navigate\",\n \"Sec-Fetch-Site\": \"none\",\n \"Sec-Fetch-User\": \"?1\",\n \"Upgrade-Insecure-Requests\": \"1\",\n};\n\nfunction applyBrowserDefaults(\n headers: Record<string, string>,\n): Record<string, string> {\n const seen = new Set(Object.keys(headers).map((k) => k.toLowerCase()));\n const merged = { ...headers };\n for (const [name, value] of Object.entries(BROWSER_DEFAULT_HEADERS)) {\n if (!seen.has(name.toLowerCase())) merged[name] = value;\n }\n return merged;\n}\n\nexport interface FetchToolOptions {\n /** Resolve ${keys.NAME} references. Injected by the plugin at setup time. */\n resolveKeys?: (text: string) => Promise<{\n resolved: string;\n usedKeys: string[];\n secretValues?: string[];\n /**\n * Optional: which scope (user/org/workspace) each used key actually\n * resolved at. Populated by resolvers like\n * `resolveKeyReferencesWithRequestScopes` so `validateUrl` can look up\n * the allowlist at the scope the value came from rather than assuming\n * user scope. Backwards compatible — callers that omit this (or a\n * resolver that doesn't report it) are unaffected.\n */\n resolvedKeys?: ResolvedKeyReference[];\n }>;\n /** Validate URL against per-key allowlists. */\n validateUrl?: (\n url: string,\n usedKeys: string[],\n /** Accumulated `resolvedKeys` from every `resolveKeys` call this request made, when the resolver reported them. */\n resolvedKeys?: ResolvedKeyReference[],\n ) => Promise<boolean>;\n}\n\n/**\n * Create the fetch tool entry for the agent tool registry.\n */\nexport function createFetchToolEntry(\n opts: FetchToolOptions = {},\n): Record<string, ActionEntry> {\n return {\n \"web-request\": {\n tool: {\n description: `Make an outbound HTTP request to any EXTERNAL URL — APIs, webhooks, and arbitrary web pages (HTML, RSS, JSON, etc.). Use this to fetch the contents of a URL the user pastes in chat. Sends realistic Chrome-on-macOS headers by default (User-Agent, Accept, Sec-Fetch-*) so most sites that block obvious bots will respond normally; pass an explicit header to override any default. Supports \\${keys.NAME} placeholders in url, headers, and body — these are resolved server-side from the user's saved keys (the raw value never enters your context). Example: \\${keys.SLACK_WEBHOOK} in the url field. IMPORTANT: Never use this to call internal /_agent-native/ endpoints or localhost action URLs — use the registered actions directly (e.g. \\`search-records\\`, \\`provider-api-request\\`, \\`update-resource\\`). Actions are already available as native tools; calling them via HTTP is slower and bypasses validation.`,\n parameters: {\n type: \"object\" as const,\n properties: {\n url: {\n type: \"string\",\n description:\n 'Full URL. May contain ${keys.NAME} references, e.g. \"${keys.SLACK_WEBHOOK}\".',\n },\n method: {\n type: \"string\",\n description: \"HTTP method. Default: GET.\",\n enum: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"HEAD\"],\n },\n headers: {\n type: \"string\",\n description:\n 'JSON object of headers. May contain ${keys.NAME} references. Example: \\'{\"Authorization\": \"Bearer ${keys.API_TOKEN}\"}\\'.',\n },\n body: {\n type: \"string\",\n description:\n \"Request body (for POST/PUT/PATCH). May contain ${keys.NAME} references.\",\n },\n timeout_ms: {\n type: \"number\",\n description: `Timeout in milliseconds. Default: ${DEFAULT_TIMEOUT_MS}. Max: 30000.`,\n },\n maxChars: {\n type: \"number\",\n description:\n \"Maximum response body characters to return. Default: 32000. Max: 200000. Increase when you need to read a large document, API response, or dataset.\",\n },\n responseMode: {\n type: \"string\",\n description:\n \"How to return the response. Default: auto (HTML pages become clean markdown; JSON/text stays raw). Use raw for exact bytes, markdown/text for extracted readable content, links for just links, metadata for page metadata, or matches with search.\",\n enum: [\n \"auto\",\n \"raw\",\n \"text\",\n \"markdown\",\n \"links\",\n \"metadata\",\n \"matches\",\n ],\n },\n extract: {\n type: \"string\",\n description:\n \"HTML extraction strategy. Default: readability. Use all-visible for visible body text/markdown, or none to convert the full HTML document.\",\n enum: [\"readability\", \"all-visible\", \"none\"],\n },\n includeLinks: {\n type: \"boolean\",\n description:\n \"Whether extracted HTML responses should include a compact links list. Default: true for extracted pages.\",\n },\n search: {\n type: \"object\",\n description:\n \"Optional post-fetch search over extracted content by default. Supports {query, queries, terms, regex, regexFlags, source:'extracted'|'raw', maxMatches, contextChars, caseSensitive}. Regex is safety-checked and bounded; prefer query/terms for simple grep-like searches.\",\n properties: {\n query: { type: \"string\" },\n queries: { type: \"array\", items: { type: \"string\" } },\n terms: { type: \"array\", items: { type: \"string\" } },\n regex: { type: \"string\" },\n regexFlags: { type: \"string\" },\n source: { type: \"string\", enum: [\"extracted\", \"raw\"] },\n maxMatches: { type: \"number\" },\n contextChars: { type: \"number\" },\n caseSensitive: { type: \"boolean\" },\n },\n } as any,\n saveToFile: {\n type: \"string\",\n description:\n \"Workspace file path to save the full response body to instead of returning it in context (e.g. 'analysis/page.html'). When set, returns only a compact summary {savedTo, status, bytes, preview}. Useful for large web pages or API responses that would overflow context.\",\n },\n },\n required: [\"url\"],\n },\n },\n run: async (args: Record<string, unknown>) => {\n const startTime = Date.now();\n const rawUrl = String(args.url ?? \"\");\n const method = normalizeExtensionProxyMethod(args.method || \"GET\");\n if (!method) {\n return \"Unsupported HTTP method. Allowed methods: GET, POST, PUT, PATCH, DELETE, HEAD.\";\n }\n const rawHeaders =\n typeof args.headers === \"string\"\n ? args.headers\n : JSON.stringify(args.headers ?? {});\n const rawBody =\n typeof args.body === \"string\"\n ? args.body\n : args.body === undefined || args.body === null\n ? undefined\n : JSON.stringify(args.body);\n const timeoutMs = Math.min(\n Number(args.timeout_ms) || DEFAULT_TIMEOUT_MS,\n 30_000,\n );\n const requestedMaxChars = Number(args.maxChars);\n const maxChars =\n Number.isFinite(requestedMaxChars) && requestedMaxChars > 0\n ? Math.min(requestedMaxChars, 200_000)\n : 32_000;\n\n // Resolve key references\n let resolvedUrl = rawUrl;\n let resolvedHeaders = rawHeaders;\n let resolvedBody = rawBody;\n const allUsedKeys: string[] = [];\n const allSecretValues: string[] = [];\n const allResolvedKeys: ResolvedKeyReference[] = [];\n\n if (opts.resolveKeys) {\n try {\n const urlResult = await opts.resolveKeys(rawUrl);\n resolvedUrl = urlResult.resolved;\n allUsedKeys.push(...urlResult.usedKeys);\n allSecretValues.push(...(urlResult.secretValues ?? []));\n allResolvedKeys.push(...(urlResult.resolvedKeys ?? []));\n\n const headerResult = await opts.resolveKeys(rawHeaders);\n resolvedHeaders = headerResult.resolved;\n allUsedKeys.push(...headerResult.usedKeys);\n allSecretValues.push(...(headerResult.secretValues ?? []));\n allResolvedKeys.push(...(headerResult.resolvedKeys ?? []));\n\n if (rawBody) {\n const bodyResult = await opts.resolveKeys(rawBody);\n resolvedBody = bodyResult.resolved;\n allUsedKeys.push(...bodyResult.usedKeys);\n allSecretValues.push(...(bodyResult.secretValues ?? []));\n allResolvedKeys.push(...(bodyResult.resolvedKeys ?? []));\n }\n } catch (err: any) {\n return `Error resolving key references: ${err?.message ?? err}`;\n }\n }\n const secretValues = collectSecretValues(allSecretValues);\n const resolvedKeys = allResolvedKeys.length\n ? allResolvedKeys\n : undefined;\n\n // Block SSRF targets regardless of key usage\n if (await isBlockedExtensionUrlWithDns(resolvedUrl)) {\n return `Requests to private/internal addresses are not allowed: \"${rawUrl}\".`;\n }\n\n // Validate URL against per-key allowlists\n if (opts.validateUrl && allUsedKeys.length > 0) {\n try {\n const allowed = await opts.validateUrl(\n resolvedUrl,\n allUsedKeys,\n resolvedKeys,\n );\n if (!allowed) {\n return `URL \"${rawUrl}\" is not in the allowlist for the referenced keys. Check your key settings.`;\n }\n } catch (err: any) {\n return `URL validation error: ${err?.message ?? err}`;\n }\n }\n\n // Parse headers, then merge in browser-like defaults for any header the\n // caller didn't already specify. Real-browser headers (User-Agent,\n // Accept, Sec-Fetch-*) are what gets you past Cloudflare / PerimeterX /\n // generic UA-sniffing middleware on sites the user pastes in chat;\n // explicit caller headers always win so API calls keep their auth\n // headers untouched.\n let headers: Record<string, string>;\n try {\n headers = sanitizeOutboundHeaders(JSON.parse(resolvedHeaders));\n } catch {\n return `Invalid headers JSON: ${rawHeaders}`;\n }\n headers = applyBrowserDefaults(headers);\n\n // Make the request\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const dispatcher = (await createSsrfSafeDispatcher()) ?? undefined;\n const fetchOpts: RequestInit & { dispatcher?: unknown } = {\n method,\n headers,\n signal: controller.signal,\n redirect: \"manual\",\n };\n if (dispatcher) fetchOpts.dispatcher = dispatcher;\n if (resolvedBody && [\"POST\", \"PUT\", \"PATCH\"].includes(method)) {\n fetchOpts.body = resolvedBody;\n if (!headers[\"content-type\"] && !headers[\"Content-Type\"]) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n }\n\n const response = await fetch(resolvedUrl, fetchOpts);\n const elapsed = Date.now() - startTime;\n\n if (response.status >= 300 && response.status < 400) {\n const location = response.headers.get(\"location\");\n const redirectUrl = location\n ? new URL(location, resolvedUrl).href\n : null;\n if (\n redirectUrl &&\n (await isBlockedExtensionUrlWithDns(redirectUrl))\n ) {\n return \"Redirect to private/internal address blocked.\";\n }\n if (redirectUrl && opts.validateUrl && allUsedKeys.length > 0) {\n const allowed = await opts.validateUrl(\n redirectUrl,\n allUsedKeys,\n resolvedKeys,\n );\n if (!allowed) {\n return \"Redirect URL is not in the allowlist for the referenced keys.\";\n }\n }\n return `HTTP ${response.status} ${response.statusText}\\n\\nRedirect: ${\n redirectUrl ? redactString(redirectUrl, secretValues) : \"(none)\"\n }`;\n }\n\n // Check if caller wants to save to workspace file (before truncation).\n const saveToFilePath =\n typeof (args as Record<string, unknown>).saveToFile === \"string\"\n ? ((args as Record<string, unknown>).saveToFile as string).trim()\n : \"\";\n\n let body: string;\n try {\n // When saving to file allow larger reads (20MB), otherwise cap at proxy limit.\n const readLimit = saveToFilePath\n ? 20 * 1024 * 1024\n : MAX_EXTENSION_PROXY_RESPONSE_SIZE;\n const result = await readResponseTextWithLimit(response, readLimit);\n body = result.text;\n } catch {\n body = \"(could not read response body)\";\n }\n body = redactString(body, secretValues);\n const contentType =\n response.headers.get(\"content-type\")?.split(\";\")[0].trim() ??\n \"text/plain\";\n let displayBody: string;\n let processedMode = \"raw\";\n try {\n const processed = processWebContent({\n url: resolvedUrl,\n body,\n contentType,\n responseMode: String(args.responseMode ?? \"auto\"),\n extract: String(args.extract ?? \"readability\"),\n includeLinks:\n args.includeLinks === undefined\n ? true\n : parseBooleanArg(args.includeLinks),\n search: parseWebContentSearchOptions(args.search),\n maxChars,\n });\n processedMode = processed.mode;\n displayBody = formatWebContentResult(processed);\n } catch (err: any) {\n return `web-request post-processing error: ${err?.message ?? String(err)}`;\n }\n\n // Audit log\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → ${response.status} (${elapsed}ms, keys: ${allUsedKeys.join(\",\") || \"none\"})`,\n );\n\n // saveToFile: write full body to workspace and return compact summary.\n if (saveToFilePath) {\n try {\n const { writeWorkspaceFile, SAVE_TO_FILE_MAX_BYTES } =\n await import(\"../workspace-files/store.js\");\n const { getRequestOrgId, getRequestUserEmail } =\n await import(\"../server/request-context.js\");\n const orgId = getRequestOrgId();\n const email = getRequestUserEmail();\n const scope = orgId\n ? { scope: \"org\" as const, scopeId: orgId }\n : email\n ? { scope: \"user\" as const, scopeId: email }\n : null;\n if (!scope)\n throw new Error(\"No authenticated context for saveToFile\");\n await writeWorkspaceFile(\n scope,\n saveToFilePath,\n body,\n contentType,\n {\n maxFileBytes: SAVE_TO_FILE_MAX_BYTES,\n },\n );\n const bytes = Buffer.byteLength(body, \"utf8\");\n const preview = displayBody.slice(0, 2000);\n return JSON.stringify({\n savedToFile: true,\n savedTo: saveToFilePath,\n status: response.status,\n bytes,\n contentType,\n responseMode: processedMode,\n preview:\n preview.length < displayBody.length ? `${preview}…` : preview,\n });\n } catch (saveErr: any) {\n return `saveToFile error: ${saveErr?.message ?? String(saveErr)}\\n\\nHTTP ${response.status} ${response.statusText}\\n\\n${body.slice(0, maxChars)}`;\n }\n }\n\n return `HTTP ${response.status} ${response.statusText}\\n\\n${displayBody}`;\n } catch (err: any) {\n const elapsed = Date.now() - startTime;\n if (err?.name === \"AbortError\") {\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → TIMEOUT (${elapsed}ms)`,\n );\n return `Request timed out after ${timeoutMs}ms.`;\n }\n const message = redactSecrets(\n err?.message ?? String(err),\n secretValues,\n );\n console.log(\n `[fetch-tool] ${method} ${rawUrl} → ERROR: ${message} (${elapsed}ms)`,\n );\n return `Request failed: ${message}`;\n } finally {\n clearTimeout(timeout);\n }\n },\n readOnly: true,\n },\n };\n}\n\nfunction parseBooleanArg(value: unknown): boolean {\n if (typeof value === \"boolean\") return value;\n const normalized = String(value).trim().toLowerCase();\n return normalized === \"true\" || normalized === \"1\" || normalized === \"yes\";\n}\n"]}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* `metadata.emailRecipients`.
|
|
28
28
|
*/
|
|
29
29
|
import { ssrfSafeFetch } from "../extensions/url-safety.js";
|
|
30
|
-
import {
|
|
30
|
+
import { resolveKeyReferencesWithRequestScopes, validateUrlAllowlist, getKeyAllowlist, } from "../secrets/substitution.js";
|
|
31
31
|
import { sendEmail } from "../server/email.js";
|
|
32
32
|
import { registerNotificationChannel } from "./registry.js";
|
|
33
33
|
let _registered = false;
|
|
@@ -153,19 +153,35 @@ function createEmailChannel() {
|
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
async function resolveWebhookRequest(urlTemplate, authTemplate, owner, label) {
|
|
156
|
-
// Resolve `${keys.NAME}` references
|
|
156
|
+
// Resolve `${keys.NAME}` references through the same request-scope
|
|
157
|
+
// cascade already used by extension fetches (extensions/routes.ts) and
|
|
158
|
+
// automation connector headers (automation/index.ts): user scope first
|
|
159
|
+
// (a personal override always wins), then the active org scope — where
|
|
160
|
+
// the Dispatch vault syncs workspace secrets — then workspace scope.
|
|
161
|
+
// Org/workspace vault rows are write-gated (org-admin + Dispatch vault
|
|
162
|
+
// UI), so reading them here is safe by default; this is unrelated to the
|
|
163
|
+
// opt-in-only user→workspace fallback in resolveKeyReferences (audit 05
|
|
164
|
+
// H2 in secrets/substitution.ts), which stays off. In headless contexts
|
|
165
|
+
// (e.g. a scheduled monitor check) getRequestOrgId() may be unset, in
|
|
166
|
+
// which case the cascade checks user + solo-workspace scopes only — a
|
|
167
|
+
// strict superset of the previous user-scope-only behavior.
|
|
157
168
|
// Missing keys throw — the error surfaces in logs and the channel is marked
|
|
158
169
|
// un-delivered, but other channels still run.
|
|
159
|
-
const { resolved: url } = await
|
|
170
|
+
const { resolved: url } = await resolveKeyReferencesWithRequestScopes(urlTemplate, owner);
|
|
160
171
|
const headers = {
|
|
161
172
|
"Content-Type": "application/json",
|
|
162
173
|
};
|
|
163
174
|
if (authTemplate) {
|
|
164
|
-
const { resolved: auth } = await
|
|
175
|
+
const { resolved: auth } = await resolveKeyReferencesWithRequestScopes(authTemplate, owner);
|
|
165
176
|
headers.Authorization = auth;
|
|
166
177
|
}
|
|
167
178
|
// If the user set an allowlist on a referenced key, enforce it here —
|
|
168
179
|
// origin-level check, same rule the automations fetch-tool applies.
|
|
180
|
+
// NOTE: this still checks the allowlist at user scope only (unlike the
|
|
181
|
+
// fetch tool's validateUrl, which now consults getResolvedKeyAllowlist at
|
|
182
|
+
// the scope the key actually resolved at — see agent-chat-plugin.ts). A
|
|
183
|
+
// key that resolves at org/workspace scope with an allowlist configured
|
|
184
|
+
// on that org/workspace row will not have it enforced here.
|
|
169
185
|
const keyNames = Array.from(new Set(Array.from(urlTemplate.matchAll(/\$\{keys\.([A-Za-z0-9_-]+)\}/g), (m) => String(m[1]))));
|
|
170
186
|
const allowlists = await Promise.all(keyNames.map((name) => getKeyAllowlist(name, "user", owner)));
|
|
171
187
|
keyNames.forEach((name, i) => {
|