@agent-native/core 0.131.4 → 0.131.6
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 +3 -3
- package/corpus/core/CHANGELOG.md +69 -0
- package/corpus/core/docs/content/agent-native-toolkit.mdx +13 -6
- package/corpus/core/docs/content/cloneable-saas.mdx +5 -5
- package/corpus/core/docs/content/creating-templates.mdx +9 -0
- package/corpus/core/docs/content/deployment.mdx +14 -10
- package/corpus/core/docs/content/docs-components.mdx +491 -0
- package/corpus/core/docs/content/doctor.mdx +8 -1
- package/corpus/core/docs/content/drop-in-agent.mdx +1 -1
- package/corpus/core/docs/content/extensions.mdx +18 -18
- package/corpus/core/docs/content/faq.mdx +4 -4
- package/corpus/core/docs/content/key-concepts.mdx +30 -21
- package/corpus/core/docs/content/template-brain-developers.mdx +4 -1
- package/corpus/core/docs/content/template-mail-developers.mdx +4 -1
- package/corpus/core/docs/content/what-is-agent-native.mdx +6 -6
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/client.ts +449 -66
- package/corpus/core/src/a2a/correlation.ts +31 -0
- package/corpus/core/src/a2a/types.ts +4 -0
- package/corpus/core/src/action.ts +3 -0
- package/corpus/core/src/agent/engine/registry.ts +45 -5
- package/corpus/core/src/agent/production-agent.ts +151 -18
- package/corpus/core/src/agent/run-loop-with-resume.ts +190 -3
- package/corpus/core/src/agent/run-manager.ts +117 -41
- package/corpus/core/src/agent/thread-debug-history.ts +86 -0
- package/corpus/core/src/agent/types.ts +3 -1
- package/corpus/core/src/cli/create.ts +11 -1
- package/corpus/core/src/cli/templates-meta.ts +2 -2
- package/corpus/core/src/client/agent-chat-adapter.ts +91 -13
- package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +11 -9
- package/corpus/core/src/client/sse-event-processor.ts +17 -3
- package/corpus/core/src/db/client.ts +57 -17
- package/corpus/core/src/integrations/adapters/slack.ts +8 -3
- package/corpus/core/src/localization/default-messages.ts +44 -0
- package/corpus/core/src/observability/traces.ts +78 -5
- package/corpus/core/src/scripts/call-agent.ts +290 -9
- package/corpus/core/src/scripts/describe-workspace-apps.ts +2 -2
- package/corpus/core/src/secrets/crypto.ts +126 -34
- package/corpus/core/src/secrets/storage.ts +61 -25
- package/corpus/core/src/server/agent-capabilities.ts +1 -1
- package/corpus/core/src/server/agent-chat/action-filters-a2a.ts +111 -7
- package/corpus/core/src/server/agent-chat/context-tools.ts +2 -0
- package/corpus/core/src/server/agent-chat/plugin-options.ts +34 -1
- package/corpus/core/src/server/agent-chat/prompt-resources.ts +1 -1
- package/corpus/core/src/server/agent-chat-plugin.ts +91 -59
- package/corpus/core/src/templates/default/.agents/skills/secrets/SKILL.md +14 -7
- package/corpus/core/src/templates/headless/.agents/skills/secrets/SKILL.md +14 -7
- package/corpus/core/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
- package/corpus/core/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
- package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +14 -7
- package/corpus/core/src/vite/client.ts +1 -0
- package/corpus/templates/analytics/.agents/skills/dashboard-ops/SKILL.md +5 -4
- package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +6 -4
- package/corpus/templates/analytics/AGENTS.md +6 -4
- package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +4 -3
- package/corpus/templates/analytics/changelog/2026-07-30-scheduled-dashboard-emails-complete-every-panel-without-pool.md +6 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +28 -1
- package/corpus/templates/analytics/server/jobs/dashboard-report.ts +18 -11
- package/corpus/templates/analytics/server/lib/analytics-connector-catalog.ts +4 -4
- package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -5
- package/corpus/templates/analytics/server/lib/dashboard-panel-query.ts +6 -2
- package/corpus/templates/analytics/server/lib/dashboard-panel-source-resolver.ts +11 -4
- package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +49 -31
- package/corpus/templates/analytics/server/lib/first-party-analytics.ts +16 -8
- package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +9 -3
- package/corpus/templates/analytics/server/lib/production-serverless-runtime.ts +11 -0
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +3 -2
- package/corpus/templates/analytics/server/plugins/analytics-alert-jobs.ts +2 -1
- package/corpus/templates/analytics/server/plugins/dashboard-report-jobs.ts +2 -1
- package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +1 -12
- package/corpus/templates/brain/.agents/skills/brain/RUNBOOK.md +54 -6
- package/corpus/templates/brain/.agents/skills/brain/SKILL.md +20 -9
- package/corpus/templates/brain/.agents/skills/ingestion-and-connectors/SKILL.md +33 -1
- package/corpus/templates/brain/AGENTS.md +3 -1
- package/corpus/templates/brain/README.md +4 -1
- package/corpus/templates/brain/actions/_schemas.ts +30 -8
- package/corpus/templates/brain/actions/ask-brain.ts +124 -14
- package/corpus/templates/brain/actions/create-source.ts +18 -2
- package/corpus/templates/brain/actions/enqueue-captures-distillation.ts +8 -123
- package/corpus/templates/brain/actions/enqueue-distillation.ts +6 -127
- package/corpus/templates/brain/actions/import-capture.ts +17 -5
- package/corpus/templates/brain/actions/import-transcript.ts +17 -5
- package/corpus/templates/brain/actions/update-source.ts +25 -5
- package/corpus/templates/brain/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/brain/changelog/2026-07-30-blessed-resources-can-feed-cited-company-answers.md +6 -0
- package/corpus/templates/brain/changelog/2026-07-30-the-left-sidebar-no-longer-shows-a-blank-organization-placeh.md +6 -0
- package/corpus/templates/brain/server/lib/brain.ts +49 -19
- package/corpus/templates/brain/server/lib/distillation-queue.ts +147 -0
- package/corpus/templates/brain/server/lib/source-policy.ts +264 -0
- package/corpus/templates/brain/server/plugins/agent-chat.ts +3 -2
- package/corpus/templates/brain/server/routes/api/_agent-native/brain/ingest.post.ts +116 -19
- package/corpus/templates/clips/app/components/meetings/google-oauth-identity-notice.tsx +51 -0
- package/corpus/templates/clips/app/i18n/en-US.ts +4 -0
- package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +3 -1
- package/corpus/templates/clips/changelog/2026-07-30-google-calendar-warning.md +6 -0
- package/corpus/templates/content/.agents/skills/content-product-development/SKILL.md +47 -0
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +72 -8
- package/corpus/templates/content/changelog/2026-07-30-the-slash-command-hint-now-stays-on-only-the-active-empty-li.md +6 -0
- package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +506 -11
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +148 -17
- package/corpus/templates/design/app/components/design/canvas-interactions/design-canvas-interactions.ts +357 -0
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +7 -466
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +15 -311
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +19 -997
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +8 -1216
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +31 -38
- package/corpus/templates/design/package.json +0 -1
- package/corpus/templates/dispatch/AGENTS.md +8 -0
- package/corpus/templates/dispatch/DEVELOPING.md +29 -0
- package/corpus/templates/dispatch/actions/view-screen.ts +34 -2
- package/corpus/templates/dispatch/app/i18n-data.ts +457 -0
- package/corpus/templates/dispatch/changelog/2026-07-30-delegated-dispatch-work-continues-reliably-in-the-background.md +6 -0
- package/corpus/templates/dispatch/changelog/2026-07-30-find-failed-runs-across-apps.md +6 -0
- package/corpus/templates/dispatch/netlify.toml +3 -0
- package/corpus/templates/slides/.agents/skills/analytics-data-for-decks/SKILL.md +8 -12
- package/corpus/templates/slides/AGENTS.md +4 -4
- package/corpus/templates/slides/actions/duplicate-deck.ts +19 -4
- package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -10
- package/corpus/templates/slides/app/components/deck/MermaidRenderer.tsx +12 -1
- package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +30 -13
- package/corpus/templates/slides/app/components/editor/DeckEditorSkeleton.tsx +35 -0
- package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +63 -56
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +21 -76
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1110 -292
- package/corpus/templates/slides/app/components/editor/SlideOverflowWarning.tsx +53 -0
- package/corpus/templates/slides/app/components/editor/SlideStyleInspector.tsx +235 -18
- package/corpus/templates/slides/app/components/editor/SpeakerNotesPanel.tsx +4 -1
- package/corpus/templates/slides/app/components/editor/canvas-interactions/index.ts +11 -0
- package/corpus/templates/slides/app/components/editor/canvas-interactions/slides-canvas-adapter.ts +129 -0
- package/corpus/templates/slides/app/components/editor/selection-overlay-measurement.ts +70 -0
- package/corpus/templates/slides/app/components/editor/slide-object-interactions.ts +519 -65
- package/corpus/templates/slides/app/components/visual-editor/CanvasCommentPins.tsx +36 -426
- package/corpus/templates/slides/app/components/visual-editor/DrawOverlay.tsx +19 -458
- package/corpus/templates/slides/app/context/DeckContext.tsx +179 -72
- package/corpus/templates/slides/app/global.css +105 -0
- package/corpus/templates/slides/app/i18n/en-US.ts +8 -0
- package/corpus/templates/slides/app/lib/deck-editor-loading.ts +26 -0
- package/corpus/templates/slides/app/lib/mermaid-blocks.ts +27 -0
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +71 -38
- package/corpus/templates/slides/app/pages/Index.tsx +98 -41
- package/corpus/templates/slides/changelog/2026-07-30-deck-loading-no-longer-flashes-an-unavailable-message.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-duplicating-a-deck-from-the-deck-list-now-opens-the-copy-imm.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-layout-overflow-warning-is-readable-and-dismissible.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-shared-canvas-annotations.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-slide-objects-can-be-moved-as-a-group-copied-and-pasted-with.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-slide-text-editing-and-object-controls-now-match-google-slides.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-style-panel-now-scrolls-with-speaker-notes-open-shows-the-sl.md +6 -0
- package/corpus/templates/slides/changelog/2026-07-30-top-toolbar-controls-now-use-consistent-sizing-with-undo-and.md +6 -0
- package/corpus/templates/slides/netlify.toml +3 -1
- package/corpus/templates/slides/package.json +0 -1
- package/corpus/templates/slides/server/lib/chat-attachments.ts +18 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +5 -0
- package/corpus/templates/slides/vite.config.ts +0 -1
- package/corpus/toolkit/CHANGELOG.md +13 -0
- package/corpus/toolkit/README.md +19 -0
- package/corpus/toolkit/agent-native.eject.json +38 -0
- package/corpus/toolkit/package.json +29 -1
- package/corpus/toolkit/src/canvas-annotations/CanvasCommentPins.tsx +861 -0
- package/corpus/toolkit/src/canvas-annotations/DrawOverlay.tsx +1233 -0
- package/corpus/toolkit/src/canvas-annotations/index.ts +15 -0
- package/corpus/toolkit/src/canvas-annotations/types.ts +14 -0
- package/corpus/toolkit/src/canvas-interactions/canvas-interactions.ts +933 -0
- package/corpus/toolkit/src/canvas-interactions/index.ts +67 -0
- package/corpus/toolkit/src/design-tweaks/scrub-input-utils.ts +311 -0
- package/corpus/toolkit/src/design-tweaks/scrub-input.tsx +491 -0
- package/corpus/toolkit/src/design-tweaks/visual-style-controls.tsx +26 -171
- package/corpus/toolkit/src/index.ts +1 -0
- package/dist/a2a/client.d.ts +23 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +309 -44
- package/dist/a2a/client.js.map +1 -1
- package/dist/a2a/correlation.d.ts +1 -0
- package/dist/a2a/correlation.d.ts.map +1 -1
- package/dist/a2a/correlation.js +27 -0
- package/dist/a2a/correlation.js.map +1 -1
- package/dist/a2a/types.d.ts +4 -0
- package/dist/a2a/types.d.ts.map +1 -1
- package/dist/a2a/types.js.map +1 -1
- package/dist/action.d.ts +3 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js.map +1 -1
- package/dist/agent/engine/registry.d.ts.map +1 -1
- package/dist/agent/engine/registry.js +34 -7
- package/dist/agent/engine/registry.js.map +1 -1
- package/dist/agent/production-agent.d.ts +40 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +113 -15
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +21 -0
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +163 -4
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/agent/run-manager.d.ts +16 -9
- package/dist/agent/run-manager.d.ts.map +1 -1
- package/dist/agent/run-manager.js +93 -42
- package/dist/agent/run-manager.js.map +1 -1
- package/dist/agent/thread-debug-history.d.ts +10 -0
- package/dist/agent/thread-debug-history.d.ts.map +1 -0
- package/dist/agent/thread-debug-history.js +68 -0
- package/dist/agent/thread-debug-history.js.map +1 -0
- package/dist/agent/types.d.ts +3 -1
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +6 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/templates-meta.js +2 -2
- package/dist/cli/templates-meta.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +76 -13
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
- package/dist/client/resources/McpIntegrationDialog.js +1 -1
- package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +12 -3
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +48 -17
- package/dist/db/client.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/integrations/adapters/slack.js +7 -3
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/localization/default-messages.d.ts +43 -0
- package/dist/localization/default-messages.d.ts.map +1 -1
- package/dist/localization/default-messages.js +43 -0
- package/dist/localization/default-messages.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/traces.d.ts +3 -1
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +73 -4
- package/dist/observability/traces.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
- package/dist/provider-api/actions/provider-api.d.ts +13 -13
- package/dist/provider-api/corpus-jobs.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +233 -10
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/scripts/describe-workspace-apps.js +2 -2
- package/dist/scripts/describe-workspace-apps.js.map +1 -1
- package/dist/secrets/crypto.d.ts +31 -21
- package/dist/secrets/crypto.d.ts.map +1 -1
- package/dist/secrets/crypto.js +108 -33
- package/dist/secrets/crypto.js.map +1 -1
- package/dist/secrets/storage.d.ts +3 -5
- package/dist/secrets/storage.d.ts.map +1 -1
- package/dist/secrets/storage.js +40 -25
- package/dist/secrets/storage.js.map +1 -1
- package/dist/server/agent-capabilities.js +1 -1
- package/dist/server/agent-capabilities.js.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.d.ts +17 -3
- package/dist/server/agent-chat/action-filters-a2a.d.ts.map +1 -1
- package/dist/server/agent-chat/action-filters-a2a.js +92 -3
- package/dist/server/agent-chat/action-filters-a2a.js.map +1 -1
- package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
- package/dist/server/agent-chat/context-tools.js +2 -0
- package/dist/server/agent-chat/context-tools.js.map +1 -1
- package/dist/server/agent-chat/plugin-options.d.ts +23 -0
- package/dist/server/agent-chat/plugin-options.d.ts.map +1 -1
- package/dist/server/agent-chat/plugin-options.js +4 -1
- package/dist/server/agent-chat/plugin-options.js.map +1 -1
- package/dist/server/agent-chat/prompt-resources.js +1 -1
- package/dist/server/agent-chat/prompt-resources.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +2 -1
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +79 -56
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/templates/default/.agents/skills/secrets/SKILL.md +14 -7
- package/dist/templates/headless/.agents/skills/secrets/SKILL.md +14 -7
- package/dist/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
- package/dist/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
- package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +14 -7
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +1 -0
- package/dist/vite/client.js.map +1 -1
- package/docs/content/agent-native-toolkit.mdx +13 -6
- package/docs/content/cloneable-saas.mdx +5 -5
- package/docs/content/creating-templates.mdx +9 -0
- package/docs/content/deployment.mdx +14 -10
- package/docs/content/docs-components.mdx +491 -0
- package/docs/content/doctor.mdx +8 -1
- package/docs/content/drop-in-agent.mdx +1 -1
- package/docs/content/extensions.mdx +18 -18
- package/docs/content/faq.mdx +4 -4
- package/docs/content/key-concepts.mdx +30 -21
- package/docs/content/template-brain-developers.mdx +4 -1
- package/docs/content/template-mail-developers.mdx +4 -1
- package/docs/content/what-is-agent-native.mdx +6 -6
- package/package.json +2 -2
- package/src/a2a/client.ts +449 -66
- package/src/a2a/correlation.ts +31 -0
- package/src/a2a/types.ts +4 -0
- package/src/action.ts +3 -0
- package/src/agent/engine/registry.ts +45 -5
- package/src/agent/production-agent.ts +151 -18
- package/src/agent/run-loop-with-resume.ts +190 -3
- package/src/agent/run-manager.ts +117 -41
- package/src/agent/thread-debug-history.ts +86 -0
- package/src/agent/types.ts +3 -1
- package/src/cli/create.ts +11 -1
- package/src/cli/templates-meta.ts +2 -2
- package/src/client/agent-chat-adapter.ts +91 -13
- package/src/client/resources/McpIntegrationDialog.tsx +11 -9
- package/src/client/sse-event-processor.ts +17 -3
- package/src/db/client.ts +57 -17
- package/src/integrations/adapters/slack.ts +8 -3
- package/src/localization/default-messages.ts +44 -0
- package/src/observability/traces.ts +78 -5
- package/src/scripts/call-agent.ts +290 -9
- package/src/scripts/describe-workspace-apps.ts +2 -2
- package/src/secrets/crypto.ts +126 -34
- package/src/secrets/storage.ts +61 -25
- package/src/server/agent-capabilities.ts +1 -1
- package/src/server/agent-chat/action-filters-a2a.ts +111 -7
- package/src/server/agent-chat/context-tools.ts +2 -0
- package/src/server/agent-chat/plugin-options.ts +34 -1
- package/src/server/agent-chat/prompt-resources.ts +1 -1
- package/src/server/agent-chat-plugin.ts +91 -59
- package/src/templates/default/.agents/skills/secrets/SKILL.md +14 -7
- package/src/templates/headless/.agents/skills/secrets/SKILL.md +14 -7
- package/src/templates/workspace-core/.agents/skills/a2a-protocol/SKILL.md +18 -7
- package/src/templates/workspace-core/.agents/skills/composable-mini-apps/SKILL.md +15 -14
- package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +14 -7
- package/src/vite/client.ts +1 -0
package/src/secrets/crypto.ts
CHANGED
|
@@ -10,25 +10,26 @@
|
|
|
10
10
|
* existing single-app deployments can keep reading and writing secrets while
|
|
11
11
|
* they migrate to the shared key.
|
|
12
12
|
*
|
|
13
|
-
* The
|
|
14
|
-
* `<APP_NAME>_SECRETS_ENCRYPTION_KEY` when set, then
|
|
15
|
-
* then `BETTER_AUTH_SECRET`.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* `/var/task` on Lambda), so anyone with read access to the DB could decrypt
|
|
25
|
-
* every secret.
|
|
13
|
+
* The generic encryption key is derived from
|
|
14
|
+
* `<APP_NAME>_SECRETS_ENCRYPTION_KEY` when set, then
|
|
15
|
+
* `SECRETS_ENCRYPTION_KEY`, then `BETTER_AUTH_SECRET`. Workspace-shared vault
|
|
16
|
+
* rows use a different precedence: `SECRETS_ENCRYPTION_KEY`, then material
|
|
17
|
+
* derived from the workspace-wide `A2A_SECRET`, then the app-local auth/key
|
|
18
|
+
* fallbacks retained for backward-compatible reads. This keeps sibling apps
|
|
19
|
+
* on one key even when each app correctly has a different auth secret.
|
|
20
|
+
*
|
|
21
|
+
* In production we refuse to start without configured key material — a
|
|
22
|
+
* CWD-derived fallback would be effectively static (e.g. `/var/task` on
|
|
23
|
+
* Lambda), so anyone with read access to the DB could decrypt every secret.
|
|
26
24
|
*
|
|
27
25
|
* Encrypted values are tagged `v1:<iv-hex>:<ct-hex>:<tag-hex>`. The `v1:` prefix
|
|
28
26
|
* lets readers distinguish ciphertext from legacy plaintext during migration.
|
|
29
27
|
*/
|
|
30
28
|
|
|
31
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
deriveServerSecret,
|
|
31
|
+
getWorkspaceA2ADerivedSecret,
|
|
32
|
+
} from "../server/derived-secret.js";
|
|
32
33
|
|
|
33
34
|
type NodeCryptoModule = typeof import("node:crypto");
|
|
34
35
|
|
|
@@ -80,7 +81,12 @@ function appScopedEncryptionKey(): string | undefined {
|
|
|
80
81
|
: undefined;
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Preserve the generic/app-local key precedence. Generic encrypted values are
|
|
86
|
+
* not a cross-app boundary, and changing their preferred key would strand
|
|
87
|
+
* existing OAuth tokens and credentials.
|
|
88
|
+
*/
|
|
89
|
+
function genericEncryptionKeyMaterial(): string | undefined {
|
|
84
90
|
if (typeof process === "undefined") return undefined;
|
|
85
91
|
return (
|
|
86
92
|
process.env.SECRETS_ENCRYPTION_KEY ||
|
|
@@ -89,13 +95,55 @@ function sharedEncryptionKeyMaterial(): string | undefined {
|
|
|
89
95
|
);
|
|
90
96
|
}
|
|
91
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Vault sharing follows configured A2A trust even on independently deployed
|
|
100
|
+
* sibling apps that are not launched by the workspace wrapper. The wrapper's
|
|
101
|
+
* `AGENT_NATIVE_WORKSPACE` flag remains required for auth/OAuth-derived
|
|
102
|
+
* secrets, but it must not be a hidden prerequisite for decrypting vault rows
|
|
103
|
+
* when every app already has the same explicit `A2A_SECRET`.
|
|
104
|
+
*/
|
|
105
|
+
function a2aSharedEncryptionKeyMaterial(): string | undefined {
|
|
106
|
+
const workspaceDerived = getWorkspaceA2ADerivedSecret("secrets-encryption");
|
|
107
|
+
if (workspaceDerived) return workspaceDerived;
|
|
108
|
+
if (typeof process === "undefined") return undefined;
|
|
109
|
+
const rootSecret = process.env.A2A_SECRET?.trim();
|
|
110
|
+
return rootSecret
|
|
111
|
+
? deriveServerSecret(rootSecret, "secrets-encryption")
|
|
112
|
+
: undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Stable materials that may be used for workspace-shared vault rows, ordered
|
|
117
|
+
* from current preference to legacy compatibility fallbacks.
|
|
118
|
+
*
|
|
119
|
+
* `BETTER_AUTH_SECRET` deliberately comes after the A2A-derived material:
|
|
120
|
+
* sibling apps normally have different auth secrets but share A2A trust.
|
|
121
|
+
*/
|
|
122
|
+
function sharedEncryptionKeyMaterials(): string[] {
|
|
123
|
+
if (typeof process === "undefined") return [];
|
|
124
|
+
const candidates = [
|
|
125
|
+
process.env.SECRETS_ENCRYPTION_KEY,
|
|
126
|
+
a2aSharedEncryptionKeyMaterial(),
|
|
127
|
+
process.env.BETTER_AUTH_SECRET,
|
|
128
|
+
appScopedEncryptionKey(),
|
|
129
|
+
].filter((value): value is string => Boolean(value));
|
|
130
|
+
return [...new Set(candidates)];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function preferredSharedEncryptionKeyMaterial(): string | undefined {
|
|
134
|
+
return sharedEncryptionKeyMaterials()[0];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function hashSecretEncryptionKey(material: string): Buffer {
|
|
138
|
+
const { createHash } = requireNodeCrypto();
|
|
139
|
+
return createHash("sha256").update(material).digest();
|
|
140
|
+
}
|
|
141
|
+
|
|
92
142
|
function deriveSecretEncryptionKey(
|
|
93
143
|
explicit: string | undefined,
|
|
94
144
|
errorMessage: string,
|
|
95
145
|
warningMessage: string,
|
|
96
146
|
): Buffer {
|
|
97
|
-
const { createHash } = requireNodeCrypto();
|
|
98
|
-
|
|
99
147
|
if (!explicit) {
|
|
100
148
|
if (processNodeEnv() === "production") {
|
|
101
149
|
throw new Error(errorMessage);
|
|
@@ -108,7 +156,7 @@ function deriveSecretEncryptionKey(
|
|
|
108
156
|
}
|
|
109
157
|
|
|
110
158
|
const material = explicit || `agent-native-secrets:${processCwd()}`;
|
|
111
|
-
return
|
|
159
|
+
return hashSecretEncryptionKey(material);
|
|
112
160
|
}
|
|
113
161
|
|
|
114
162
|
/**
|
|
@@ -125,7 +173,7 @@ export function getSecretEncryptionKey(): Buffer {
|
|
|
125
173
|
.replace(/[^A-Z0-9]+/g, "_")
|
|
126
174
|
.replace(/^_+|_+$/g, "");
|
|
127
175
|
return deriveSecretEncryptionKey(
|
|
128
|
-
appScopedEncryptionKey() ||
|
|
176
|
+
appScopedEncryptionKey() || genericEncryptionKeyMaterial(),
|
|
129
177
|
"[agent-native/secrets] Refusing to start in production without an encryption key. " +
|
|
130
178
|
`Set ${appName ? `${appName}_SECRETS_ENCRYPTION_KEY, ` : ""}SECRETS_ENCRYPTION_KEY, or BETTER_AUTH_SECRET in the deploy environment. ` +
|
|
131
179
|
"The previous CWD-derived fallback was effectively static (e.g. `/var/task` on Lambda), " +
|
|
@@ -138,30 +186,32 @@ export function getSecretEncryptionKey(): Buffer {
|
|
|
138
186
|
|
|
139
187
|
/**
|
|
140
188
|
* Derive the preferred workspace-shared key used by `app_secrets` rows.
|
|
141
|
-
* Unlike generic column-level encryption, workspace vault data
|
|
142
|
-
* in sibling apps
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
* shared material yet; once the shared key is configured, writes use it and
|
|
148
|
-
* reads still fall back to old rows.
|
|
189
|
+
* Unlike generic column-level encryption, workspace vault data must decrypt
|
|
190
|
+
* in sibling apps. An explicit `SECRETS_ENCRYPTION_KEY` wins; hosted
|
|
191
|
+
* workspaces otherwise derive stable material from their shared `A2A_SECRET`
|
|
192
|
+
* before considering app-local `BETTER_AUTH_SECRET` or app-scoped key
|
|
193
|
+
* fallbacks. Reads try every configured legacy candidate and report when the
|
|
194
|
+
* ciphertext should be refreshed under the preferred key.
|
|
149
195
|
*/
|
|
150
196
|
export function getSharedSecretEncryptionKey(): Buffer {
|
|
151
197
|
return deriveSecretEncryptionKey(
|
|
152
|
-
|
|
198
|
+
preferredSharedEncryptionKeyMaterial(),
|
|
153
199
|
"[agent-native/secrets] Refusing to start in production without encryption key material for workspace secrets. " +
|
|
154
|
-
"Set SECRETS_ENCRYPTION_KEY,
|
|
155
|
-
"
|
|
200
|
+
"Set SECRETS_ENCRYPTION_KEY, ensure a hosted workspace has A2A_SECRET, or set BETTER_AUTH_SECRET / an app-scoped " +
|
|
201
|
+
"*_SECRETS_ENCRYPTION_KEY compatibility fallback in the deploy environment.",
|
|
156
202
|
"[agent-native/secrets] SECRETS_ENCRYPTION_KEY not set — using app-scoped or machine-local fallback for workspace secrets. " +
|
|
157
|
-
"Set SECRETS_ENCRYPTION_KEY or
|
|
158
|
-
"or rely on A2A_SECRET-derived material on hosted workspace deploys.",
|
|
203
|
+
"Set SECRETS_ENCRYPTION_KEY or rely on A2A_SECRET-derived material on hosted workspace deploys so sibling apps share vault rows.",
|
|
159
204
|
);
|
|
160
205
|
}
|
|
161
206
|
|
|
162
207
|
/** Whether this deployment has stable workspace-shared key material. */
|
|
163
208
|
export function hasSharedSecretEncryptionKeyMaterial(): boolean {
|
|
164
|
-
|
|
209
|
+
if (typeof process === "undefined") return false;
|
|
210
|
+
return Boolean(
|
|
211
|
+
process.env.SECRETS_ENCRYPTION_KEY ||
|
|
212
|
+
a2aSharedEncryptionKeyMaterial() ||
|
|
213
|
+
process.env.BETTER_AUTH_SECRET,
|
|
214
|
+
);
|
|
165
215
|
}
|
|
166
216
|
|
|
167
217
|
function encryptWithKey(plaintext: string, key: Buffer): string {
|
|
@@ -212,7 +262,49 @@ export function encryptSharedSecretValue(plaintext: string): string {
|
|
|
212
262
|
|
|
213
263
|
/** Decrypt a workspace-shared `app_secrets` value. */
|
|
214
264
|
export function decryptSharedSecretValue(encrypted: string): string {
|
|
215
|
-
return
|
|
265
|
+
return decryptSharedSecretValueDetailed(encrypted).value;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export interface DecryptedSharedSecretValue {
|
|
269
|
+
value: string;
|
|
270
|
+
/**
|
|
271
|
+
* True when a legacy app-local candidate decrypted the value. Callers that
|
|
272
|
+
* own the row should opportunistically re-encrypt it with the preferred
|
|
273
|
+
* workspace key.
|
|
274
|
+
*/
|
|
275
|
+
needsReencrypt: boolean;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Decrypt workspace ciphertext with the preferred key first, then configured
|
|
280
|
+
* legacy candidates. The result never exposes which secret material matched.
|
|
281
|
+
*/
|
|
282
|
+
export function decryptSharedSecretValueDetailed(
|
|
283
|
+
encrypted: string,
|
|
284
|
+
): DecryptedSharedSecretValue {
|
|
285
|
+
const preferredKey = getSharedSecretEncryptionKey();
|
|
286
|
+
const candidateKeys = [preferredKey];
|
|
287
|
+
for (const material of sharedEncryptionKeyMaterials()) {
|
|
288
|
+
const candidate = hashSecretEncryptionKey(material);
|
|
289
|
+
if (!candidateKeys.some((existing) => existing.equals(candidate))) {
|
|
290
|
+
candidateKeys.push(candidate);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
let lastError: unknown;
|
|
295
|
+
for (const candidate of candidateKeys) {
|
|
296
|
+
try {
|
|
297
|
+
return {
|
|
298
|
+
value: decryptWithKey(encrypted, candidate),
|
|
299
|
+
needsReencrypt: !candidate.equals(preferredKey),
|
|
300
|
+
};
|
|
301
|
+
} catch (error) {
|
|
302
|
+
lastError = error;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
throw lastError instanceof Error
|
|
306
|
+
? lastError
|
|
307
|
+
: new Error("Unable to decrypt workspace secret");
|
|
216
308
|
}
|
|
217
309
|
|
|
218
310
|
/**
|
package/src/secrets/storage.ts
CHANGED
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Values are encrypted at rest with AES-256-GCM. The workspace-shared
|
|
5
5
|
* encryption key is derived from `SECRETS_ENCRYPTION_KEY` (preferred) or the
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* once configured, new rows use the shared key and reads retain a legacy-key
|
|
10
|
-
* fallback for existing rows.
|
|
6
|
+
* workspace-wide `A2A_SECRET`. `BETTER_AUTH_SECRET` and app-scoped keys remain
|
|
7
|
+
* read fallbacks for rows written before sibling apps converged on the shared
|
|
8
|
+
* key. Successful fallback reads race-safely refresh the shared ciphertext.
|
|
11
9
|
*
|
|
12
10
|
* Secret values are NEVER logged and NEVER returned from any route handler.
|
|
13
11
|
*/
|
|
@@ -19,7 +17,7 @@ import { ensureColumnExists, ensureTableExists } from "../db/ddl-guard.js";
|
|
|
19
17
|
import {
|
|
20
18
|
encryptSecretValue as encryptLegacyValue,
|
|
21
19
|
encryptSharedSecretValue as encryptValue,
|
|
22
|
-
|
|
20
|
+
decryptSharedSecretValueDetailed as decryptValue,
|
|
23
21
|
decryptSecretValue as decryptLegacyValue,
|
|
24
22
|
hasSharedSecretEncryptionKeyMaterial,
|
|
25
23
|
} from "./crypto.js";
|
|
@@ -193,8 +191,9 @@ export async function writeAppSecret(args: WriteSecretArgs): Promise<string> {
|
|
|
193
191
|
// pointing at data that's no longer current. Siblings then get an honest
|
|
194
192
|
// cache miss (falling back to the legacy column or reporting missing) until
|
|
195
193
|
// the owning app's next read repopulates shared_encrypted_value via
|
|
196
|
-
// `populateSharedAppSecret`, which
|
|
197
|
-
//
|
|
194
|
+
// `populateSharedAppSecret`, which fills a NULL column or compare-and-swap
|
|
195
|
+
// replaces the exact legacy ciphertext it just decrypted. A temporary miss
|
|
196
|
+
// is safer than serving rotated-away plaintext.
|
|
198
197
|
const upsertSql = `INSERT INTO app_secrets (id, scope, scope_id, key, encrypted_value, shared_encrypted_value, description, url_allowlist, created_at, updated_at)
|
|
199
198
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
200
199
|
ON CONFLICT (scope, scope_id, key) DO UPDATE SET
|
|
@@ -253,8 +252,13 @@ interface DecryptedAppSecretValue {
|
|
|
253
252
|
value: string;
|
|
254
253
|
/** True when the app-scoped fallback decrypted encrypted_value. */
|
|
255
254
|
usedLegacyKey: boolean;
|
|
256
|
-
/** True when shared_encrypted_value
|
|
255
|
+
/** True when shared_encrypted_value needs to be created or refreshed. */
|
|
257
256
|
needsSharedCiphertext: boolean;
|
|
257
|
+
/**
|
|
258
|
+
* Existing ciphertext that must still match before a refresh. Null means the
|
|
259
|
+
* migration may only fill an empty shared column.
|
|
260
|
+
*/
|
|
261
|
+
sharedCiphertextToReplace?: string | null;
|
|
258
262
|
}
|
|
259
263
|
|
|
260
264
|
function decryptAppSecretValue(
|
|
@@ -263,10 +267,14 @@ function decryptAppSecretValue(
|
|
|
263
267
|
): DecryptedAppSecretValue {
|
|
264
268
|
if (sharedEncrypted) {
|
|
265
269
|
try {
|
|
270
|
+
const decrypted = decryptValue(sharedEncrypted);
|
|
266
271
|
return {
|
|
267
|
-
value:
|
|
272
|
+
value: decrypted.value,
|
|
268
273
|
usedLegacyKey: false,
|
|
269
|
-
needsSharedCiphertext:
|
|
274
|
+
needsSharedCiphertext: decrypted.needsReencrypt,
|
|
275
|
+
sharedCiphertextToReplace: decrypted.needsReencrypt
|
|
276
|
+
? sharedEncrypted
|
|
277
|
+
: undefined,
|
|
270
278
|
};
|
|
271
279
|
} catch {
|
|
272
280
|
// Fall through to the legacy column. A partially migrated row may have
|
|
@@ -274,30 +282,34 @@ function decryptAppSecretValue(
|
|
|
274
282
|
}
|
|
275
283
|
}
|
|
276
284
|
try {
|
|
285
|
+
const decrypted = decryptValue(encrypted);
|
|
277
286
|
return {
|
|
278
|
-
value:
|
|
287
|
+
value: decrypted.value,
|
|
279
288
|
usedLegacyKey: false,
|
|
280
289
|
needsSharedCiphertext: true,
|
|
290
|
+
sharedCiphertextToReplace: sharedEncrypted ?? null,
|
|
281
291
|
};
|
|
282
292
|
} catch {
|
|
283
293
|
return {
|
|
284
294
|
value: decryptLegacyValue(encrypted),
|
|
285
295
|
usedLegacyKey: true,
|
|
286
296
|
needsSharedCiphertext: true,
|
|
297
|
+
sharedCiphertextToReplace: sharedEncrypted ?? null,
|
|
287
298
|
};
|
|
288
299
|
}
|
|
289
300
|
}
|
|
290
301
|
|
|
291
302
|
/**
|
|
292
|
-
*
|
|
293
|
-
* intentionally best-effort: a read must still succeed if a deployment's
|
|
294
|
-
* role cannot update the row. The
|
|
295
|
-
*
|
|
296
|
-
*
|
|
303
|
+
* Create or refresh the shared column without touching encrypted_value. This
|
|
304
|
+
* is intentionally best-effort: a read must still succeed if a deployment's
|
|
305
|
+
* DB role cannot update the row. The compare-and-swap predicate prevents a
|
|
306
|
+
* concurrent writer from being overwritten, and leaving updated_at untouched
|
|
307
|
+
* preserves the row's user-visible ordering/metadata.
|
|
297
308
|
*/
|
|
298
309
|
async function populateSharedAppSecret(
|
|
299
310
|
id: unknown,
|
|
300
311
|
value: string,
|
|
312
|
+
sharedCiphertextToReplace: string | null,
|
|
301
313
|
): Promise<void> {
|
|
302
314
|
if (
|
|
303
315
|
id === undefined ||
|
|
@@ -307,10 +319,18 @@ async function populateSharedAppSecret(
|
|
|
307
319
|
return;
|
|
308
320
|
}
|
|
309
321
|
try {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
322
|
+
const encrypted = encryptValue(value);
|
|
323
|
+
if (sharedCiphertextToReplace === null) {
|
|
324
|
+
await getDbExec().execute({
|
|
325
|
+
sql: `UPDATE app_secrets SET shared_encrypted_value = ? WHERE id = ? AND shared_encrypted_value IS NULL`,
|
|
326
|
+
args: [encrypted, id],
|
|
327
|
+
});
|
|
328
|
+
} else {
|
|
329
|
+
await getDbExec().execute({
|
|
330
|
+
sql: `UPDATE app_secrets SET shared_encrypted_value = ? WHERE id = ? AND shared_encrypted_value = ?`,
|
|
331
|
+
args: [encrypted, id, sharedCiphertextToReplace],
|
|
332
|
+
});
|
|
333
|
+
}
|
|
314
334
|
} catch {
|
|
315
335
|
// Migration is opportunistic. Preserve the successful read if the update
|
|
316
336
|
// is unavailable due to a read-only role, transient DB failure, or race.
|
|
@@ -371,7 +391,11 @@ export async function readAppSecret(
|
|
|
371
391
|
rows[0].shared_encrypted_value as string | null,
|
|
372
392
|
);
|
|
373
393
|
if (decrypted.needsSharedCiphertext) {
|
|
374
|
-
await populateSharedAppSecret(
|
|
394
|
+
await populateSharedAppSecret(
|
|
395
|
+
rows[0].id,
|
|
396
|
+
decrypted.value,
|
|
397
|
+
decrypted.sharedCiphertextToReplace ?? null,
|
|
398
|
+
);
|
|
375
399
|
}
|
|
376
400
|
return {
|
|
377
401
|
value: decrypted.value,
|
|
@@ -410,7 +434,11 @@ export async function readAppSecrets(args: {
|
|
|
410
434
|
row.shared_encrypted_value as string | null,
|
|
411
435
|
);
|
|
412
436
|
if (decrypted.needsSharedCiphertext) {
|
|
413
|
-
await populateSharedAppSecret(
|
|
437
|
+
await populateSharedAppSecret(
|
|
438
|
+
row.id,
|
|
439
|
+
decrypted.value,
|
|
440
|
+
decrypted.sharedCiphertextToReplace ?? null,
|
|
441
|
+
);
|
|
414
442
|
}
|
|
415
443
|
results.set(key, {
|
|
416
444
|
value: decrypted.value,
|
|
@@ -473,7 +501,11 @@ export async function readAppSecretMeta(
|
|
|
473
501
|
row.shared_encrypted_value as string | null,
|
|
474
502
|
);
|
|
475
503
|
if (decrypted.needsSharedCiphertext) {
|
|
476
|
-
await populateSharedAppSecret(
|
|
504
|
+
await populateSharedAppSecret(
|
|
505
|
+
row.id,
|
|
506
|
+
decrypted.value,
|
|
507
|
+
decrypted.sharedCiphertextToReplace ?? null,
|
|
508
|
+
);
|
|
477
509
|
}
|
|
478
510
|
last4Value = last4(decrypted.value);
|
|
479
511
|
} catch {
|
|
@@ -516,7 +548,11 @@ export async function listAppSecretsForScope(
|
|
|
516
548
|
row.shared_encrypted_value as string | null,
|
|
517
549
|
);
|
|
518
550
|
if (decrypted.needsSharedCiphertext) {
|
|
519
|
-
await populateSharedAppSecret(
|
|
551
|
+
await populateSharedAppSecret(
|
|
552
|
+
row.id,
|
|
553
|
+
decrypted.value,
|
|
554
|
+
decrypted.sharedCiphertextToReplace ?? null,
|
|
555
|
+
);
|
|
520
556
|
}
|
|
521
557
|
last4Value = last4(decrypted.value);
|
|
522
558
|
} catch {
|
|
@@ -130,7 +130,7 @@ export function formatCapabilitySummary(
|
|
|
130
130
|
"",
|
|
131
131
|
lines.join("\n\n"),
|
|
132
132
|
"",
|
|
133
|
-
"Delegate with call-agent
|
|
133
|
+
"Delegate with call-agent using a natural-language message by default. The receiving app owns interpretation and its local tools. Use action + input only for an exact bounded read with a fully known schema, never as a workaround for failed delegation. Prefer the app that owns the data over rebuilding its capability here.",
|
|
134
134
|
]
|
|
135
135
|
.filter(Boolean)
|
|
136
136
|
.join("\n");
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
filterInitialEngineTools,
|
|
13
13
|
resolveAgentRequestReasoningEffort,
|
|
14
14
|
type ActionEntry,
|
|
15
|
+
type AgentLoopOutcome,
|
|
15
16
|
} from "../../agent/production-agent.js";
|
|
16
17
|
import { runAgentLoopDirectWithSoftTimeout } from "../../agent/run-loop-with-resume.js";
|
|
17
18
|
import type { AgentChatEvent } from "../../agent/types.js";
|
|
@@ -194,6 +195,10 @@ export function buildAuthenticatedAgentA2ASkills(
|
|
|
194
195
|
name,
|
|
195
196
|
description: entry.tool.description,
|
|
196
197
|
publicAgent: entry.publicAgent,
|
|
198
|
+
// Every action in this set is read-only by construction. Omitting the
|
|
199
|
+
// flag made discovery label all of them "(mutating)", which pushes a
|
|
200
|
+
// caller away from invoking them and back into open-ended delegation.
|
|
201
|
+
readOnly: true,
|
|
197
202
|
// Naming an action without its parameters is what makes a caller invoke
|
|
198
203
|
// it with `{}` and fail on a required field.
|
|
199
204
|
...(entry.tool.parameters
|
|
@@ -230,9 +235,14 @@ export function resolveArtifactBaseUrl(
|
|
|
230
235
|
export function assembleA2AFinalResponse(
|
|
231
236
|
events: readonly AgentChatEvent[],
|
|
232
237
|
toolResults: readonly A2AToolResultSummary[],
|
|
233
|
-
options: A2AArtifactResponseOptions & {
|
|
238
|
+
options: A2AArtifactResponseOptions & {
|
|
239
|
+
event?: any;
|
|
240
|
+
outcome?: AgentLoopOutcome;
|
|
241
|
+
} = {},
|
|
234
242
|
): { responseText: string; finalText: string } {
|
|
235
|
-
const terminalError =
|
|
243
|
+
const terminalError = options.outcome
|
|
244
|
+
? terminalErrorFromOutcome(options.outcome)
|
|
245
|
+
: getA2ATerminalErrorEvent(events);
|
|
236
246
|
const responseText = collectFinalResponseTextFromAgentEvents(events, {
|
|
237
247
|
fallbackToPreToolText: !terminalError,
|
|
238
248
|
});
|
|
@@ -241,12 +251,48 @@ export function assembleA2AFinalResponse(
|
|
|
241
251
|
includeReferencedArtifacts: true,
|
|
242
252
|
includePersistedArtifactMarker: true,
|
|
243
253
|
});
|
|
244
|
-
if (terminalError
|
|
245
|
-
|
|
254
|
+
if (terminalError) {
|
|
255
|
+
const partialResult = finalText.trim()
|
|
256
|
+
? `\n\nPartial verified results before the failure:\n${finalText.trim()}`
|
|
257
|
+
: "";
|
|
258
|
+
throw new Error(formatA2ATerminalError(terminalError) + partialResult);
|
|
259
|
+
}
|
|
260
|
+
if (!finalText.trim()) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
"Agent completed without a response or verified artifact.\ncode: empty_agent_response",
|
|
263
|
+
);
|
|
246
264
|
}
|
|
247
265
|
return { responseText, finalText };
|
|
248
266
|
}
|
|
249
267
|
|
|
268
|
+
function terminalErrorFromOutcome(
|
|
269
|
+
outcome: AgentLoopOutcome,
|
|
270
|
+
): Extract<AgentChatEvent, { type: "error" }> | null {
|
|
271
|
+
if (outcome.state === "completed") return null;
|
|
272
|
+
if (outcome.state === "failed") {
|
|
273
|
+
return {
|
|
274
|
+
type: "error",
|
|
275
|
+
error: outcome.message,
|
|
276
|
+
errorCode: outcome.code,
|
|
277
|
+
recoverable: outcome.retryable,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
if (outcome.state === "input_required") {
|
|
281
|
+
return {
|
|
282
|
+
type: "error",
|
|
283
|
+
error: outcome.message,
|
|
284
|
+
errorCode: outcome.code,
|
|
285
|
+
recoverable: true,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
type: "error",
|
|
290
|
+
error: outcome.message ?? "Agent run was canceled.",
|
|
291
|
+
errorCode: "canceled",
|
|
292
|
+
recoverable: false,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
250
296
|
function getA2ATerminalErrorEvent(
|
|
251
297
|
events: readonly AgentChatEvent[],
|
|
252
298
|
): Extract<AgentChatEvent, { type: "error" }> | null {
|
|
@@ -255,6 +301,23 @@ function getA2ATerminalErrorEvent(
|
|
|
255
301
|
if (event.type === "clear") continue;
|
|
256
302
|
if (event.type === "done") return null;
|
|
257
303
|
if (event.type === "error") return event;
|
|
304
|
+
if (event.type === "tripwire") {
|
|
305
|
+
return {
|
|
306
|
+
type: "error",
|
|
307
|
+
error: event.reason || "Agent stopped at a delegated-run guardrail.",
|
|
308
|
+
errorCode: event.processor ? `tripwire:${event.processor}` : "tripwire",
|
|
309
|
+
recoverable: true,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
if (event.type === "loop_limit") {
|
|
313
|
+
return {
|
|
314
|
+
type: "error",
|
|
315
|
+
error:
|
|
316
|
+
"Agent stopped before finishing at the delegated-run step limit.",
|
|
317
|
+
errorCode: "loop_limit",
|
|
318
|
+
recoverable: true,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
258
321
|
if (event.type === "auto_continue") {
|
|
259
322
|
return {
|
|
260
323
|
type: "error",
|
|
@@ -280,6 +343,27 @@ function formatA2ATerminalError(
|
|
|
280
343
|
|
|
281
344
|
type A2AAgentLoopRunner = typeof runAgentLoopDirectWithSoftTimeout;
|
|
282
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Delegated turns should do specialist work with the receiver's own tools,
|
|
348
|
+
* but must return a bounded caller-ready result. Interactive defaults allow
|
|
349
|
+
* extremely deep work (400 iterations / 20M cumulative input tokens), which
|
|
350
|
+
* made healthy Slides -> Analytics calls take 4-13 minutes and consume up to
|
|
351
|
+
* millions of input tokens as tool results accumulated. These defaults leave
|
|
352
|
+
* ample room for multi-source analysis while failing before a delegated turn
|
|
353
|
+
* can silently become an unbounded research session.
|
|
354
|
+
*/
|
|
355
|
+
export const DEFAULT_DELEGATED_MAX_ITERATIONS = 80;
|
|
356
|
+
export const DEFAULT_DELEGATED_MAX_RUN_INPUT_TOKENS = 750_000;
|
|
357
|
+
export const DEFAULT_DELEGATED_MAX_TOOL_RESULT_CHARS = 20_000;
|
|
358
|
+
|
|
359
|
+
export const DELEGATED_AGENT_EXECUTION_CONTRACT = `
|
|
360
|
+
<delegated-agent-contract>
|
|
361
|
+
This request was delegated by another app. You are the specialist owner of the work.
|
|
362
|
+
- Interpret the caller's natural-language objective using your own instructions, skills, data dictionary, credentials, and tools. Choose providers, schemas, queries, and joins here; never ask the caller to invent SQL or source-specific implementation details for you.
|
|
363
|
+
- Minimize round trips. Filter, join, aggregate, paginate, stage, and reduce large datasets inside your own tools rather than returning raw records or transcripts.
|
|
364
|
+
- Return a concise caller-ready result with the answer, source and coverage details, relevant counts or IDs, caveats/partial gaps, and exact artifact URLs. Do not return tool transcripts or large raw payloads.
|
|
365
|
+
</delegated-agent-contract>`;
|
|
366
|
+
|
|
283
367
|
export interface DelegatedAgentLoopTelemetry {
|
|
284
368
|
runId: string;
|
|
285
369
|
threadId: string | null;
|
|
@@ -302,14 +386,19 @@ async function runDelegatedAgentLoop(
|
|
|
302
386
|
runOptions: Parameters<A2AAgentLoopRunner>[0],
|
|
303
387
|
pluginOptions: Pick<
|
|
304
388
|
AgentChatPluginOptions,
|
|
305
|
-
"finalResponseGuard" | "runSoftTimeoutMs"
|
|
389
|
+
"delegatedRunPolicy" | "finalResponseGuard" | "runSoftTimeoutMs"
|
|
306
390
|
>,
|
|
307
391
|
timeoutOptions: Parameters<A2AAgentLoopRunner>[2],
|
|
308
392
|
options: DelegatedAgentLoopOptions,
|
|
309
393
|
) {
|
|
310
394
|
const runner = options.runner ?? runAgentLoopDirectWithSoftTimeout;
|
|
395
|
+
const policy = pluginOptions.delegatedRunPolicy;
|
|
396
|
+
const configuredHardToolResultCap = runOptions.toolLimits?.hardMaxResultChars;
|
|
397
|
+
const delegatedHardToolResultCap =
|
|
398
|
+
policy?.maxToolResultChars ?? DEFAULT_DELEGATED_MAX_TOOL_RESULT_CHARS;
|
|
311
399
|
const resolvedRunOptions = {
|
|
312
400
|
...runOptions,
|
|
401
|
+
systemPrompt: runOptions.systemPrompt + DELEGATED_AGENT_EXECUTION_CONTRACT,
|
|
313
402
|
// Delegated runs resolve their own model and do not pass through the
|
|
314
403
|
// interactive request handler's output-token setup. Use the same
|
|
315
404
|
// model-aware headroom here so reasoning models (notably GPT-5.x) do
|
|
@@ -321,6 +410,21 @@ async function runDelegatedAgentLoop(
|
|
|
321
410
|
reasoningEffort:
|
|
322
411
|
runOptions.reasoningEffort ??
|
|
323
412
|
resolveAgentRequestReasoningEffort({ model: runOptions.model }),
|
|
413
|
+
maxIterations:
|
|
414
|
+
runOptions.maxIterations ??
|
|
415
|
+
policy?.maxIterations ??
|
|
416
|
+
DEFAULT_DELEGATED_MAX_ITERATIONS,
|
|
417
|
+
maxRunInputTokens:
|
|
418
|
+
runOptions.maxRunInputTokens ??
|
|
419
|
+
policy?.maxRunInputTokens ??
|
|
420
|
+
DEFAULT_DELEGATED_MAX_RUN_INPUT_TOKENS,
|
|
421
|
+
toolLimits: {
|
|
422
|
+
...(runOptions.toolLimits ?? {}),
|
|
423
|
+
hardMaxResultChars:
|
|
424
|
+
typeof configuredHardToolResultCap === "number"
|
|
425
|
+
? Math.min(configuredHardToolResultCap, delegatedHardToolResultCap)
|
|
426
|
+
: delegatedHardToolResultCap,
|
|
427
|
+
},
|
|
324
428
|
finalResponseGuard: pluginOptions.finalResponseGuard,
|
|
325
429
|
};
|
|
326
430
|
const execute = (loopOptions = resolvedRunOptions) =>
|
|
@@ -369,7 +473,7 @@ export function runA2AAgentLoop(
|
|
|
369
473
|
runOptions: Parameters<A2AAgentLoopRunner>[0],
|
|
370
474
|
pluginOptions: Pick<
|
|
371
475
|
AgentChatPluginOptions,
|
|
372
|
-
"finalResponseGuard" | "runSoftTimeoutMs"
|
|
476
|
+
"delegatedRunPolicy" | "finalResponseGuard" | "runSoftTimeoutMs"
|
|
373
477
|
>,
|
|
374
478
|
timeoutOptions: Parameters<A2AAgentLoopRunner>[2],
|
|
375
479
|
options: DelegatedAgentLoopOptions = {},
|
|
@@ -391,7 +495,7 @@ export function runMCPAgentLoop(
|
|
|
391
495
|
runOptions: Parameters<A2AAgentLoopRunner>[0],
|
|
392
496
|
pluginOptions: Pick<
|
|
393
497
|
AgentChatPluginOptions,
|
|
394
|
-
"finalResponseGuard" | "runSoftTimeoutMs"
|
|
498
|
+
"delegatedRunPolicy" | "finalResponseGuard" | "runSoftTimeoutMs"
|
|
395
499
|
>,
|
|
396
500
|
timeoutOptions: Parameters<A2AAgentLoopRunner>[2],
|
|
397
501
|
options: DelegatedAgentLoopOptions = {},
|
|
@@ -152,6 +152,8 @@ Prefer \`web-request\` for simple API calls and static pages. Use browser automa
|
|
|
152
152
|
|
|
153
153
|
The \`call-agent\` tool sends a message to a DIFFERENT, separately-deployed app's agent (A2A protocol). It is **not** for calling actions within the current app.
|
|
154
154
|
|
|
155
|
+
Use a natural-language \`message\` by default. The receiving app owns interpretation and runs with its own instructions, skills, connected sources, data dictionary, and tools. Do not choose its provider, schema, query, joins, or SQL for it. A direct \`action\` + \`input\` call is only for an exact bounded read whose complete contract is already known; it is never a workaround for unreliable delegation.
|
|
156
|
+
|
|
155
157
|
**NEVER use \`call-agent\` to:**
|
|
156
158
|
- Call your own app by name
|
|
157
159
|
- Perform tasks you can accomplish with your own registered tools
|