@agent-native/core 0.84.20 → 0.84.22
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 +1 -1
- package/corpus/core/CHANGELOG.md +16 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/a2a/caller-auth.ts +37 -8
- package/corpus/core/src/a2a/client.ts +178 -67
- package/corpus/core/src/agent/production-agent.ts +19 -3
- package/corpus/core/src/agent/types.ts +1 -1
- package/corpus/core/src/cli/pr-visual-recap-workflow.ts +1 -1
- package/corpus/core/src/cli/recap.ts +43 -34
- package/corpus/core/src/client/AssistantChat.tsx +27 -4
- package/corpus/core/src/client/PoweredByBadge.tsx +70 -17
- package/corpus/core/src/client/agent-chat-adapter.ts +5 -1
- package/corpus/core/src/client/guided-questions.tsx +11 -6
- package/corpus/core/src/client/i18n.tsx +10 -5
- package/corpus/core/src/client/sse-event-processor.ts +147 -5
- package/corpus/core/src/client/tool-display.ts +8 -0
- package/corpus/core/src/file-upload/builder.ts +40 -14
- package/corpus/core/src/file-upload/types.ts +3 -0
- package/corpus/core/src/integrations/a2a-continuation-processor.ts +49 -24
- package/corpus/core/src/mcp/builtin-tools.ts +3 -0
- package/corpus/core/src/mcp/org-directory.ts +66 -20
- package/corpus/core/src/notifications/channels.ts +235 -38
- package/corpus/core/src/scripts/call-agent.ts +7 -2
- package/corpus/core/src/server/analytics.ts +32 -6
- package/corpus/core/src/server/ssr-handler.ts +31 -12
- package/corpus/templates/analytics/AGENTS.md +21 -0
- package/corpus/templates/analytics/README.md +35 -0
- package/corpus/templates/analytics/actions/delete-analytics-alert-rule.ts +23 -0
- package/corpus/templates/analytics/actions/list-analytics-alert-rules.ts +20 -0
- package/corpus/templates/analytics/actions/run-analytics-alerts.ts +23 -0
- package/corpus/templates/analytics/actions/save-analytics-alert-rule.ts +69 -0
- package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +161 -12
- package/corpus/templates/analytics/changelog/2026-07-01-analytics-can-alert-when-first-party-events-spike.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-01-session-replays-load-large-production-recordings-from-scoped.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-01-session-replays-play-back-in-production-instead-of-showing.md +6 -0
- package/corpus/templates/analytics/netlify.toml +3 -0
- package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +109 -0
- package/corpus/templates/analytics/server/db/schema.ts +55 -0
- package/corpus/templates/analytics/server/handlers/session-replay.ts +5 -2
- package/corpus/templates/analytics/server/jobs/analytics-alerts.ts +71 -0
- package/corpus/templates/analytics/server/lib/analytics-alerts.ts +734 -0
- package/corpus/templates/analytics/server/lib/session-replay.ts +10 -3
- package/corpus/templates/analytics/server/plugins/analytics-alert-jobs.ts +54 -0
- package/corpus/templates/analytics/server/plugins/db.ts +100 -0
- package/corpus/templates/analytics/server/routes/api/analytics-alerts/run.post.ts +60 -0
- package/corpus/templates/assets/README.md +30 -8
- package/corpus/templates/brain/README.md +20 -552
- package/corpus/templates/calendar/README.md +22 -28
- package/corpus/templates/calendar/app/pages/BookingPage.tsx +14 -8
- package/corpus/templates/calendar/changelog/2026-07-01-meeting-invite-pages-have-cleaner-branding-a-black-dark-mo.md +6 -0
- package/corpus/templates/chat/README.md +32 -0
- package/corpus/templates/clips/README.md +37 -0
- package/corpus/templates/clips/actions/create-recording.ts +7 -2
- package/corpus/templates/clips/actions/finalize-recording.ts +11 -0
- package/corpus/templates/clips/actions/get-recording-player-data.ts +4 -2
- package/corpus/templates/clips/actions/import-loom-recording.ts +1 -0
- package/corpus/templates/clips/actions/lib/loom-video.ts +16 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +18 -7
- package/corpus/templates/clips/app/components/library/recording-card.tsx +42 -5
- package/corpus/templates/clips/app/components/player/scrubber.tsx +2 -2
- package/corpus/templates/clips/app/components/recorder/countdown-overlay.tsx +4 -3
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +39 -12
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +106 -2
- package/corpus/templates/clips/app/components/recorder/recording-toolbar.tsx +1 -22
- package/corpus/templates/clips/app/routes/record.tsx +48 -8
- package/corpus/templates/clips/changelog/2026-07-01-clip-uploads-and-loom-imports-finish-sooner-by-skipping-the-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-clip-uploads-and-playback-recover-more-reliably-from-storage.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-desktop-recording-avoids-unsupported-browser-tab-capture.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-embedded-player-progress-is-easier-to-see-on-dark-videos.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-loom-imports-show-clear-guidance-when-loom-does-not-provide-a-downloadable-video.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-move-to-folder-now-opens-folder-choices-directly-instead-of.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-preserve-in-flight-streaming-uploads-when-the-streaming-kill.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-recording-controls-are-cleaner-and-the-countdown-uses-shadow.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-01-recordings-now-recover-when-final-upload-status-gets-lost.md +6 -0
- package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -1
- package/corpus/templates/clips/chrome-extension/src/finalize-recovery.ts +7 -224
- package/corpus/templates/clips/chrome-extension/src/offscreen.ts +64 -6
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +87 -5
- package/corpus/templates/clips/server/lib/streaming-upload-mode.ts +7 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +51 -1
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/status.get.ts +4 -1
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +96 -1
- package/corpus/templates/clips/shared/finalize-recovery.ts +242 -0
- package/corpus/templates/content/AGENTS.md +7 -3
- package/corpus/templates/content/README.md +22 -34
- package/corpus/templates/content/actions/list-trashed-content-databases.ts +4 -0
- package/corpus/templates/content/app/components/editor/SlashCommandMenu.tsx +58 -6
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +257 -24
- package/corpus/templates/content/app/components/sidebar/DocumentTreeItem.tsx +41 -15
- package/corpus/templates/content/app/i18n/zh-TW.ts +25 -7
- package/corpus/templates/content/app/i18n-data.ts +278 -40
- package/corpus/templates/content/changelog/2026-06-30-database-once-again-creates-an-inline-database-in-the-curren.md +6 -0
- package/corpus/templates/content/shared/api.ts +1 -0
- package/corpus/templates/design/AGENTS.md +7 -6
- package/corpus/templates/design/README.md +33 -0
- package/corpus/templates/design/actions/delete-file.ts +86 -10
- package/corpus/templates/design/actions/get-design-snapshot.ts +13 -0
- package/corpus/templates/design/actions/present-design-variants.ts +11 -13
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/pages/DesignEditor.tsx +2 -2
- package/corpus/templates/design/changelog/2026-07-01-design-recovers-sooner-when-an-agent-gets-stuck-preparing-a-.md +6 -0
- package/corpus/templates/dispatch/README.md +34 -0
- package/corpus/templates/forms/README.md +34 -0
- package/corpus/templates/macros/README.md +28 -0
- package/corpus/templates/mail/README.md +35 -0
- package/corpus/templates/plan/README.md +30 -118
- package/corpus/templates/plan/app/global.css +1 -1
- package/corpus/templates/slides/README.md +34 -0
- package/corpus/templates/videos/README.md +22 -201
- package/dist/a2a/caller-auth.d.ts +1 -0
- package/dist/a2a/caller-auth.d.ts.map +1 -1
- package/dist/a2a/caller-auth.js +34 -8
- package/dist/a2a/caller-auth.js.map +1 -1
- package/dist/a2a/client.d.ts +4 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +148 -58
- package/dist/a2a/client.js.map +1 -1
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +19 -4
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/types.d.ts +1 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/agent/types.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts +3 -3
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +41 -34
- package/dist/cli/recap.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +28 -4
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/PoweredByBadge.d.ts +9 -3
- package/dist/client/PoweredByBadge.d.ts.map +1 -1
- package/dist/client/PoweredByBadge.js +47 -15
- package/dist/client/PoweredByBadge.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +5 -1
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/guided-questions.d.ts.map +1 -1
- package/dist/client/guided-questions.js +10 -6
- package/dist/client/guided-questions.js.map +1 -1
- package/dist/client/i18n.d.ts +1 -1
- package/dist/client/i18n.d.ts.map +1 -1
- package/dist/client/i18n.js +6 -2
- package/dist/client/i18n.js.map +1 -1
- package/dist/client/sse-event-processor.d.ts +2 -0
- package/dist/client/sse-event-processor.d.ts.map +1 -1
- package/dist/client/sse-event-processor.js +121 -5
- package/dist/client/sse-event-processor.js.map +1 -1
- package/dist/client/tool-display.d.ts.map +1 -1
- package/dist/client/tool-display.js +8 -0
- package/dist/client/tool-display.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/builder.d.ts.map +1 -1
- package/dist/file-upload/builder.js +26 -8
- package/dist/file-upload/builder.js.map +1 -1
- package/dist/file-upload/types.d.ts +5 -1
- package/dist/file-upload/types.d.ts.map +1 -1
- package/dist/file-upload/types.js.map +1 -1
- package/dist/integrations/a2a-continuation-processor.js +43 -18
- package/dist/integrations/a2a-continuation-processor.js.map +1 -1
- package/dist/mcp/builtin-tools.d.ts.map +1 -1
- package/dist/mcp/builtin-tools.js +3 -0
- package/dist/mcp/builtin-tools.js.map +1 -1
- package/dist/mcp/org-directory.d.ts.map +1 -1
- package/dist/mcp/org-directory.js +58 -20
- package/dist/mcp/org-directory.js.map +1 -1
- package/dist/notifications/channels.d.ts +10 -0
- package/dist/notifications/channels.d.ts.map +1 -1
- package/dist/notifications/channels.js +172 -20
- package/dist/notifications/channels.js.map +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/scripts/call-agent.d.ts.map +1 -1
- package/dist/scripts/call-agent.js +2 -2
- package/dist/scripts/call-agent.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/analytics.d.ts +16 -0
- package/dist/server/analytics.d.ts.map +1 -1
- package/dist/server/analytics.js +30 -7
- package/dist/server/analytics.js.map +1 -1
- package/dist/server/ssr-handler.d.ts.map +1 -1
- package/dist/server/ssr-handler.js +28 -12
- package/dist/server/ssr-handler.js.map +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,212 +1,33 @@
|
|
|
1
|
-
# Video
|
|
1
|
+
# Video
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An agent-native, Remotion-based video composition studio. Describe a video in
|
|
4
|
+
plain English and the agent generates animated React compositions with camera
|
|
5
|
+
moves, interactive cursor animations, and a full timeline editor.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
**Live app: [videos.agent-native.com](https://videos.agent-native.com)**
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
Open http://localhost:8080 to access the studio.
|
|
13
|
-
|
|
14
|
-
## ✨ Features
|
|
15
|
-
|
|
16
|
-
### 🎬 AI-Powered Generation
|
|
17
|
-
|
|
18
|
-
- Describe videos in natural language
|
|
19
|
-
- Attach reference images, logos, and assets
|
|
20
|
-
- Generates React components and animation tracks automatically
|
|
21
|
-
|
|
22
|
-
### 🎥 Camera System
|
|
23
|
-
|
|
24
|
-
- 6 animatable properties: translate X/Y, scale, rotate X/Y, perspective
|
|
25
|
-
- Multi-keyframe selection with box-select and shift-click
|
|
26
|
-
- Visual camera controls with real-time preview
|
|
27
|
-
|
|
28
|
-
### 🖱️ Interactive Cursor
|
|
29
|
-
|
|
30
|
-
- Position tracking with smooth animations
|
|
31
|
-
- Hover and click detection
|
|
32
|
-
- Component-type based interactions
|
|
33
|
-
- Visual cursor with multiple states
|
|
34
|
-
|
|
35
|
-
### 🎭 Cursor Interactions
|
|
36
|
-
|
|
37
|
-
- Add hover animations to any component type
|
|
38
|
-
- Click effects with timing control
|
|
39
|
-
- Scale, translate, and rotate on interaction
|
|
40
|
-
- Duration and easing customization
|
|
41
|
-
|
|
42
|
-
### ⚡ Advanced Timeline
|
|
43
|
-
|
|
44
|
-
- Multi-keyframe selection and editing
|
|
45
|
-
- Visual easing selector (20+ curves)
|
|
46
|
-
- View range control for focused editing
|
|
47
|
-
- Track properties panel with live preview
|
|
48
|
-
- Expression-controlled animations (programmatic)
|
|
49
|
-
|
|
50
|
-
### 🎨 Built-in Components
|
|
51
|
-
|
|
52
|
-
- Kinetic Text (typing reveal, drift, explode)
|
|
53
|
-
- Logo Reveal (particle burst)
|
|
54
|
-
- Logo Explode (SVG scatter)
|
|
55
|
-
- Interactive Demo (hover buttons)
|
|
56
|
-
- Interactive Card Grid (cursor-reactive cards)
|
|
57
|
-
- Slideshow (multi-slide transitions)
|
|
58
|
-
|
|
59
|
-
## 🛠️ Helper Functions
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
import { createBlankComposition, addComposition } from "@/remotion/registry";
|
|
63
|
-
|
|
64
|
-
import {
|
|
65
|
-
createCameraTrack,
|
|
66
|
-
createCursorTrack,
|
|
67
|
-
createAnimationTrack,
|
|
68
|
-
createFadeInTrack,
|
|
69
|
-
createSlideInTrack,
|
|
70
|
-
createCursorPath,
|
|
71
|
-
createClickEvents,
|
|
72
|
-
validateComposition,
|
|
73
|
-
} from "@/utils/compositionHelpers";
|
|
74
|
-
|
|
75
|
-
// Create a new composition
|
|
76
|
-
const comp = createBlankComposition("My Video");
|
|
77
|
-
addComposition(comp);
|
|
78
|
-
|
|
79
|
-
// Or build manually with full control
|
|
80
|
-
const tracks = [
|
|
81
|
-
createCameraTrack(240),
|
|
82
|
-
createCursorTrack(240),
|
|
83
|
-
createFadeInTrack("title", "Title Entrance", 0, 30),
|
|
84
|
-
];
|
|
85
|
-
|
|
86
|
-
validateComposition(tracks); // Ensure it has required tracks
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## 📖 Key Concepts
|
|
90
|
-
|
|
91
|
-
### Compositions
|
|
92
|
-
|
|
93
|
-
- **ID**: URL-friendly slug (auto-generated from title)
|
|
94
|
-
- **Tracks**: Animation timelines (camera, cursor, custom)
|
|
95
|
-
- **Props**: Component configuration (colors, text, etc.)
|
|
96
|
-
- **Dimensions**: Default 1920×1080 @ 30fps
|
|
97
|
-
|
|
98
|
-
### Tracks
|
|
99
|
-
|
|
100
|
-
- **Camera**: Controls viewport (required)
|
|
101
|
-
- **Cursor**: Tracks pointer position (required for interactions)
|
|
102
|
-
- **Custom**: Element-specific animations (optional)
|
|
9
|
+
Compositions are code-driven and yours to own. Generate with AI or build manually
|
|
10
|
+
with the same track system, then refine keyframes, easing, and cursor
|
|
11
|
+
interactions on the timeline.
|
|
103
12
|
|
|
104
|
-
|
|
13
|
+
## Features
|
|
105
14
|
|
|
106
|
-
-
|
|
107
|
-
-
|
|
108
|
-
-
|
|
15
|
+
- Describe videos in natural language and get React components + animation tracks.
|
|
16
|
+
- Camera system with animatable translate, scale, rotate, and perspective.
|
|
17
|
+
- Interactive cursor with hover/click detection and component-type interactions.
|
|
18
|
+
- Advanced timeline: multi-keyframe editing, easing curves, and view ranges.
|
|
19
|
+
- Built-in components: kinetic text, logo reveal/explode, slideshows, and more.
|
|
20
|
+
- Standardized 1920×1080 @ 30fps compositions.
|
|
109
21
|
|
|
110
|
-
|
|
22
|
+
## Develop locally
|
|
111
23
|
|
|
112
|
-
|
|
113
|
-
- **Click**: Trigger on cursor click events
|
|
114
|
-
- **Component-Type Based**: Apply to all instances globally
|
|
115
|
-
|
|
116
|
-
## 🎯 Common Workflows
|
|
117
|
-
|
|
118
|
-
### Create with AI
|
|
119
|
-
|
|
120
|
-
1. Click "+ New Composition"
|
|
121
|
-
2. Describe your video
|
|
122
|
-
3. Attach references (optional)
|
|
123
|
-
4. Press Enter
|
|
124
|
-
5. Edit and customize
|
|
125
|
-
|
|
126
|
-
### Create Manually
|
|
127
|
-
|
|
128
|
-
1. Copy `BlankComposition.tsx` as template
|
|
129
|
-
2. Modify component code
|
|
130
|
-
3. Register in `registry.ts`
|
|
131
|
-
4. Export in `compositions/index.ts`
|
|
132
|
-
5. Navigate to `/c/your-comp-id`
|
|
133
|
-
|
|
134
|
-
### Add Cursor Interactions
|
|
135
|
-
|
|
136
|
-
1. Ensure cursor track exists
|
|
137
|
-
2. Add `<Cursor>` to component
|
|
138
|
-
3. Use `useHoverAnimation()` hook
|
|
139
|
-
4. Configure in Properties → Cursor Interactions
|
|
140
|
-
|
|
141
|
-
### Multi-Keyframe Editing
|
|
142
|
-
|
|
143
|
-
1. Box-select: Click+drag in timeline
|
|
144
|
-
2. Shift-click: Add individual keyframes
|
|
145
|
-
3. Drag selection: Move all together
|
|
146
|
-
4. Maintains relative timing
|
|
147
|
-
|
|
148
|
-
## 📁 Project Structure
|
|
149
|
-
|
|
150
|
-
```
|
|
151
|
-
app/
|
|
152
|
-
├── remotion/
|
|
153
|
-
│ ├── compositions/ # Video components
|
|
154
|
-
│ ├── ui-components/ # Reusable elements (Cursor, etc.)
|
|
155
|
-
│ ├── hooks/ # Animation hooks
|
|
156
|
-
│ ├── CameraHost.tsx # Camera wrapper
|
|
157
|
-
│ ├── registry.ts # Composition registry
|
|
158
|
-
│ └── trackAnimation.ts # Track utilities
|
|
159
|
-
├── components/ # Studio UI components
|
|
160
|
-
├── pages/ # Routes (Index, CompositionView)
|
|
161
|
-
├── utils/ # Helpers and utilities
|
|
162
|
-
└── contexts/ # React context providers
|
|
163
|
-
|
|
164
|
-
server/
|
|
165
|
-
├── routes/ # API endpoints
|
|
166
|
-
└── index.ts # Express server
|
|
167
|
-
|
|
168
|
-
docs/examples/
|
|
169
|
-
└── create-composition.example.ts # Example creation script
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
## 🔧 Development
|
|
173
|
-
|
|
174
|
-
### Commands
|
|
24
|
+
Scaffold your own copy and run it:
|
|
175
25
|
|
|
176
26
|
```bash
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
pnpm run build # Build client and server
|
|
182
|
-
|
|
183
|
-
# Production
|
|
184
|
-
pnpm start # Run production server
|
|
185
|
-
|
|
186
|
-
# Testing
|
|
187
|
-
pnpm test # Run tests
|
|
188
|
-
pnpm typecheck # Type checking
|
|
189
|
-
pnpm format.fix # Format code
|
|
27
|
+
npx @agent-native/core@latest create my-videos --standalone --template videos
|
|
28
|
+
cd my-videos
|
|
29
|
+
pnpm install
|
|
30
|
+
pnpm dev
|
|
190
31
|
```
|
|
191
32
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
All compositions run at:
|
|
195
|
-
|
|
196
|
-
- **30fps** (standardized)
|
|
197
|
-
- **1920×1080** (Wide format default)
|
|
198
|
-
- **240 frames** (8 seconds default duration)
|
|
199
|
-
|
|
200
|
-
## 🎓 Learn More
|
|
201
|
-
|
|
202
|
-
- **Remotion Docs**: https://remotion.dev/docs
|
|
203
|
-
- **Builder.io Docs**: https://www.builder.io/c/docs/projects
|
|
204
|
-
- **Example Compositions**: Check `app/remotion/compositions/` folder
|
|
205
|
-
|
|
206
|
-
## 📝 License
|
|
207
|
-
|
|
208
|
-
Private project.
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
|
-
**Ready to create?** Click "+ New Composition" and describe your first video! 🎬
|
|
33
|
+
Full docs: [agent-native.com/docs/template-videos](https://agent-native.com/docs/template-videos).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caller-auth.d.ts","sourceRoot":"","sources":["../../src/a2a/caller-auth.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,wBAAsB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IACnD,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,GAAG,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"caller-auth.d.ts","sourceRoot":"","sources":["../../src/a2a/caller-auth.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,wBAAsB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IACnD,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,GAAG,OAAO,CAAC,aAAa,CAAC,CA+DzB"}
|
package/dist/a2a/caller-auth.js
CHANGED
|
@@ -23,20 +23,46 @@ export async function resolveA2ACallerAuth(options) {
|
|
|
23
23
|
}
|
|
24
24
|
catch { }
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
const apiKeyAttempts = [];
|
|
27
|
+
const addApiKeyAttempt = (token) => {
|
|
28
|
+
if (!token || apiKeyAttempts.includes(token))
|
|
29
|
+
return;
|
|
30
|
+
apiKeyAttempts.push(token);
|
|
31
|
+
};
|
|
27
32
|
if (userEmail && (orgSecret || process.env.A2A_SECRET)) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
if (process.env.A2A_SECRET?.trim()) {
|
|
34
|
+
try {
|
|
35
|
+
addApiKeyAttempt(await signA2AToken(userEmail, orgDomain, orgSecret, {
|
|
36
|
+
expiresIn: options?.expiresIn ?? DEFAULT_A2A_CALLER_TOKEN_TTL,
|
|
37
|
+
preferGlobalSecret: true,
|
|
38
|
+
}));
|
|
39
|
+
}
|
|
40
|
+
catch { }
|
|
41
|
+
}
|
|
42
|
+
if (orgSecret) {
|
|
43
|
+
try {
|
|
44
|
+
addApiKeyAttempt(await signA2AToken(userEmail, orgDomain, orgSecret, {
|
|
45
|
+
expiresIn: options?.expiresIn ?? DEFAULT_A2A_CALLER_TOKEN_TTL,
|
|
46
|
+
preferGlobalSecret: false,
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
catch { }
|
|
33
50
|
}
|
|
34
|
-
catch { }
|
|
35
51
|
}
|
|
36
52
|
if (options?.includeGoogleToken) {
|
|
37
53
|
await attachGoogleTokenMetadata(metadata, userEmail);
|
|
38
54
|
}
|
|
39
|
-
return {
|
|
55
|
+
return {
|
|
56
|
+
apiKey: apiKeyAttempts[0],
|
|
57
|
+
...(apiKeyAttempts.length > 1
|
|
58
|
+
? { apiKeyFallbacks: apiKeyAttempts.slice(1) }
|
|
59
|
+
: {}),
|
|
60
|
+
userEmail,
|
|
61
|
+
orgId,
|
|
62
|
+
orgDomain,
|
|
63
|
+
orgSecret,
|
|
64
|
+
metadata,
|
|
65
|
+
};
|
|
40
66
|
}
|
|
41
67
|
async function attachGoogleTokenMetadata(metadata, userEmail) {
|
|
42
68
|
if (process.env.NODE_ENV !== "production" || !userEmail)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caller-auth.js","sourceRoot":"","sources":["../../src/a2a/caller-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,4BAA4B,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"caller-auth.js","sourceRoot":"","sources":["../../src/a2a/caller-auth.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"../server/request-context.js\";\nimport { signA2AToken } from \"./client.js\";\n\nconst DEFAULT_A2A_CALLER_TOKEN_TTL = \"30m\";\n\nexport interface A2ACallerAuth {\n apiKey?: string;\n apiKeyFallbacks?: string[];\n userEmail?: string;\n orgId?: string;\n orgDomain?: string;\n orgSecret?: string;\n metadata: Record<string, unknown>;\n}\n\nexport async function resolveA2ACallerAuth(options?: {\n expiresIn?: string | number;\n includeGoogleToken?: boolean;\n}): Promise<A2ACallerAuth> {\n const userEmail = getRequestUserEmail();\n const metadata: Record<string, unknown> = {};\n if (userEmail) metadata.userEmail = userEmail;\n\n let orgDomain: string | undefined;\n let orgSecret: string | undefined;\n const orgId = getRequestOrgId();\n if (orgId) {\n try {\n const { getOrgDomain } = await import(\"../org/context.js\");\n orgDomain = (await getOrgDomain(orgId)) ?? undefined;\n if (orgDomain) metadata.orgDomain = orgDomain;\n } catch {}\n try {\n const { getOrgA2ASecret } = await import(\"../org/context.js\");\n orgSecret = (await getOrgA2ASecret(orgId)) ?? undefined;\n } catch {}\n }\n\n const apiKeyAttempts: string[] = [];\n const addApiKeyAttempt = (token: string | undefined) => {\n if (!token || apiKeyAttempts.includes(token)) return;\n apiKeyAttempts.push(token);\n };\n if (userEmail && (orgSecret || process.env.A2A_SECRET)) {\n if (process.env.A2A_SECRET?.trim()) {\n try {\n addApiKeyAttempt(\n await signA2AToken(userEmail, orgDomain, orgSecret, {\n expiresIn: options?.expiresIn ?? DEFAULT_A2A_CALLER_TOKEN_TTL,\n preferGlobalSecret: true,\n }),\n );\n } catch {}\n }\n if (orgSecret) {\n try {\n addApiKeyAttempt(\n await signA2AToken(userEmail, orgDomain, orgSecret, {\n expiresIn: options?.expiresIn ?? DEFAULT_A2A_CALLER_TOKEN_TTL,\n preferGlobalSecret: false,\n }),\n );\n } catch {}\n }\n }\n\n if (options?.includeGoogleToken) {\n await attachGoogleTokenMetadata(metadata, userEmail);\n }\n\n return {\n apiKey: apiKeyAttempts[0],\n ...(apiKeyAttempts.length > 1\n ? { apiKeyFallbacks: apiKeyAttempts.slice(1) }\n : {}),\n userEmail,\n orgId,\n orgDomain,\n orgSecret,\n metadata,\n };\n}\n\nasync function attachGoogleTokenMetadata(\n metadata: Record<string, unknown>,\n userEmail: string | undefined,\n): Promise<void> {\n if (process.env.NODE_ENV !== \"production\" || !userEmail) return;\n\n try {\n const { listOAuthAccountsByOwner } =\n await import(\"../oauth-tokens/store.js\");\n const accounts = await listOAuthAccountsByOwner(\"google\", userEmail);\n const tokens = accounts[0]?.tokens;\n if (tokens?.access_token) {\n metadata.googleToken = tokens.access_token;\n }\n } catch {}\n}\n"]}
|
package/dist/a2a/client.d.ts
CHANGED
|
@@ -27,14 +27,17 @@ export declare function signA2AToken(email: string, orgDomain?: string, orgSecre
|
|
|
27
27
|
*/
|
|
28
28
|
extraClaims?: Record<string, unknown>;
|
|
29
29
|
}): Promise<string>;
|
|
30
|
+
export declare function shouldPreferGlobalA2ASecret(orgSecret?: string): boolean;
|
|
30
31
|
export declare class A2AClient {
|
|
31
32
|
private baseUrl;
|
|
32
33
|
private apiKey?;
|
|
34
|
+
private apiKeyAttempts;
|
|
33
35
|
private endpointCandidates;
|
|
34
36
|
private endpointResolved;
|
|
35
37
|
private requestTimeoutMs?;
|
|
36
38
|
constructor(baseUrl: string, apiKey?: string, options?: {
|
|
37
39
|
requestTimeoutMs?: number;
|
|
40
|
+
fallbackApiKeys?: string[];
|
|
38
41
|
});
|
|
39
42
|
/**
|
|
40
43
|
* Detect which A2A path the target agent uses.
|
|
@@ -42,6 +45,7 @@ export declare class A2AClient {
|
|
|
42
45
|
*/
|
|
43
46
|
resolveEndpoint(): Promise<void>;
|
|
44
47
|
private headers;
|
|
48
|
+
private markApiKeySucceeded;
|
|
45
49
|
private rpc;
|
|
46
50
|
getAgentCard(): Promise<AgentCard>;
|
|
47
51
|
send(message: Message, opts?: {
|
package/dist/a2a/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/a2a/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,SAAS,EAGT,OAAO,EACP,IAAI,EACL,MAAM,YAAY,CAAC;AAEpB,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,YAAY,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAU5D;CACF;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC,GACA,OAAO,CAAC,MAAM,CAAC,CA+BjB;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,kBAAkB,CAAgB;IAC1C,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,gBAAgB,CAAC,CAAS;IAElC,YACE,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/a2a/client.ts"],"names":[],"mappings":"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"}
|
package/dist/a2a/client.js
CHANGED
|
@@ -50,9 +50,13 @@ export async function signA2AToken(email, orgDomain, orgSecret, options) {
|
|
|
50
50
|
jwt.setAudience(options.audience);
|
|
51
51
|
return jwt.sign(new TextEncoder().encode(secret));
|
|
52
52
|
}
|
|
53
|
+
export function shouldPreferGlobalA2ASecret(orgSecret) {
|
|
54
|
+
return !!process.env.A2A_SECRET?.trim() || !orgSecret;
|
|
55
|
+
}
|
|
53
56
|
export class A2AClient {
|
|
54
57
|
baseUrl;
|
|
55
58
|
apiKey;
|
|
59
|
+
apiKeyAttempts;
|
|
56
60
|
endpointCandidates = [];
|
|
57
61
|
endpointResolved = false;
|
|
58
62
|
requestTimeoutMs;
|
|
@@ -65,6 +69,10 @@ export class A2AClient {
|
|
|
65
69
|
this.endpointResolved = true;
|
|
66
70
|
}
|
|
67
71
|
this.apiKey = apiKey;
|
|
72
|
+
this.apiKeyAttempts = uniqueAuthTokens([
|
|
73
|
+
apiKey,
|
|
74
|
+
...(options?.fallbackApiKeys ?? []),
|
|
75
|
+
]);
|
|
68
76
|
this.requestTimeoutMs = options?.requestTimeoutMs;
|
|
69
77
|
}
|
|
70
78
|
/**
|
|
@@ -92,13 +100,20 @@ export class A2AClient {
|
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
}
|
|
95
|
-
headers() {
|
|
103
|
+
headers(apiKey = this.apiKey) {
|
|
96
104
|
const h = { "Content-Type": "application/json" };
|
|
97
|
-
if (
|
|
98
|
-
h["Authorization"] = `Bearer ${
|
|
105
|
+
if (apiKey) {
|
|
106
|
+
h["Authorization"] = `Bearer ${apiKey}`;
|
|
99
107
|
}
|
|
100
108
|
return h;
|
|
101
109
|
}
|
|
110
|
+
markApiKeySucceeded(apiKey) {
|
|
111
|
+
this.apiKey = apiKey;
|
|
112
|
+
this.apiKeyAttempts = uniqueAuthTokens([
|
|
113
|
+
apiKey,
|
|
114
|
+
...this.apiKeyAttempts.filter((token) => token !== apiKey),
|
|
115
|
+
]);
|
|
116
|
+
}
|
|
102
117
|
async rpc(method, params) {
|
|
103
118
|
const body = {
|
|
104
119
|
jsonrpc: "2.0",
|
|
@@ -109,18 +124,26 @@ export class A2AClient {
|
|
|
109
124
|
await this.ensureEndpointCandidates();
|
|
110
125
|
let lastError = null;
|
|
111
126
|
for (const url of this.endpointCandidates) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
for (let i = 0; i < this.apiKeyAttempts.length; i++) {
|
|
128
|
+
console.log(`[A2A Client] POST ${url} method=${method}`);
|
|
129
|
+
const startTime = Date.now();
|
|
130
|
+
const res = await this.postJson(url, body, this.apiKeyAttempts[i]);
|
|
131
|
+
console.log(`[A2A Client] Response: ${res.status} in ${Date.now() - startTime}ms`);
|
|
132
|
+
if (res.ok) {
|
|
133
|
+
this.endpointCandidates = [url];
|
|
134
|
+
this.markApiKeySucceeded(this.apiKeyAttempts[i]);
|
|
135
|
+
return res.json();
|
|
136
|
+
}
|
|
137
|
+
const text = await res.text();
|
|
138
|
+
lastError = new Error(`A2A request failed (${res.status}): ${text}`);
|
|
139
|
+
if (i < this.apiKeyAttempts.length - 1 &&
|
|
140
|
+
isA2AAuthRejectionResponse(res.status, text)) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!shouldTryNextEndpoint(res.status)) {
|
|
144
|
+
throw lastError;
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
124
147
|
}
|
|
125
148
|
}
|
|
126
149
|
throw lastError ?? new Error("No A2A endpoint candidates available");
|
|
@@ -206,15 +229,25 @@ export class A2AClient {
|
|
|
206
229
|
let res = null;
|
|
207
230
|
let lastError = null;
|
|
208
231
|
for (const candidate of this.endpointCandidates) {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
232
|
+
for (let i = 0; i < this.apiKeyAttempts.length; i++) {
|
|
233
|
+
res = await this.postJson(candidate, body, this.apiKeyAttempts[i]);
|
|
234
|
+
if (res.ok) {
|
|
235
|
+
this.endpointCandidates = [candidate];
|
|
236
|
+
this.markApiKeySucceeded(this.apiKeyAttempts[i]);
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
const text = await res.text();
|
|
240
|
+
lastError = new Error(`A2A stream failed (${res.status}): ${text}`);
|
|
241
|
+
if (i < this.apiKeyAttempts.length - 1 &&
|
|
242
|
+
isA2AAuthRejectionResponse(res.status, text)) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
if (!shouldTryNextEndpoint(res.status))
|
|
246
|
+
throw lastError;
|
|
212
247
|
break;
|
|
213
248
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (!shouldTryNextEndpoint(res.status))
|
|
217
|
-
throw lastError;
|
|
249
|
+
if (res?.ok)
|
|
250
|
+
break;
|
|
218
251
|
}
|
|
219
252
|
if (!res?.ok) {
|
|
220
253
|
throw lastError ?? new Error("No A2A endpoint candidates available");
|
|
@@ -271,7 +304,7 @@ export class A2AClient {
|
|
|
271
304
|
}
|
|
272
305
|
this.endpointCandidates = unique(candidates);
|
|
273
306
|
}
|
|
274
|
-
async postJson(url, body) {
|
|
307
|
+
async postJson(url, body, apiKey = this.apiKey) {
|
|
275
308
|
const controller = this.requestTimeoutMs
|
|
276
309
|
? new AbortController()
|
|
277
310
|
: undefined;
|
|
@@ -281,7 +314,7 @@ export class A2AClient {
|
|
|
281
314
|
try {
|
|
282
315
|
return await ssrfSafeFetch(url, {
|
|
283
316
|
method: "POST",
|
|
284
|
-
headers: this.headers(),
|
|
317
|
+
headers: this.headers(apiKey),
|
|
285
318
|
body: JSON.stringify(body),
|
|
286
319
|
signal: controller?.signal,
|
|
287
320
|
}, { maxRedirects: 3 });
|
|
@@ -342,6 +375,21 @@ function shouldTryNextEndpoint(status) {
|
|
|
342
375
|
function unique(values) {
|
|
343
376
|
return Array.from(new Set(values));
|
|
344
377
|
}
|
|
378
|
+
function uniqueAuthTokens(values) {
|
|
379
|
+
const result = [];
|
|
380
|
+
for (const value of values) {
|
|
381
|
+
if (result.includes(value))
|
|
382
|
+
continue;
|
|
383
|
+
result.push(value);
|
|
384
|
+
}
|
|
385
|
+
if (result.length === 0)
|
|
386
|
+
result.push(undefined);
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
function isA2AAuthRejectionResponse(status, text) {
|
|
390
|
+
return (status === 401 ||
|
|
391
|
+
/A2A error \(-32001\): (?:Invalid or expired A2A token|Invalid API key|Authentication required)|Invalid or expired A2A token|Invalid API key|Authentication required/i.test(text));
|
|
392
|
+
}
|
|
345
393
|
/**
|
|
346
394
|
* One-shot convenience function: send a text message and get a text response.
|
|
347
395
|
*
|
|
@@ -350,19 +398,6 @@ function unique(values) {
|
|
|
350
398
|
* identity (instead of blindly trusting metadata).
|
|
351
399
|
*/
|
|
352
400
|
export async function callAgent(url, text, opts) {
|
|
353
|
-
let apiKey = opts?.apiKey;
|
|
354
|
-
// Auto-sign with JWT when an A2A secret (org or global) is available and we have a user email
|
|
355
|
-
if (!apiKey &&
|
|
356
|
-
opts?.userEmail &&
|
|
357
|
-
(opts?.orgSecret || process.env.A2A_SECRET)) {
|
|
358
|
-
try {
|
|
359
|
-
apiKey = await signA2AToken(opts.userEmail, opts.orgDomain, opts.orgSecret, { preferGlobalSecret: !opts.orgSecret });
|
|
360
|
-
}
|
|
361
|
-
catch {
|
|
362
|
-
// Fall back to unsigned call
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
const client = new A2AClient(url, apiKey);
|
|
366
401
|
const metadata = {};
|
|
367
402
|
if (opts?.userEmail)
|
|
368
403
|
metadata.userEmail = opts.userEmail;
|
|
@@ -378,15 +413,40 @@ export async function callAgent(url, text, opts) {
|
|
|
378
413
|
role: "user",
|
|
379
414
|
parts: [{ type: "text", text }],
|
|
380
415
|
};
|
|
381
|
-
|
|
382
|
-
|
|
416
|
+
const apiKeyAttempts = await buildA2AApiKeyAttempts(opts);
|
|
417
|
+
let lastAuthError;
|
|
418
|
+
for (let i = 0; i < apiKeyAttempts.length; i++) {
|
|
383
419
|
try {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
420
|
+
const fallbackApiKeys = apiKeyAttempts
|
|
421
|
+
.slice(i + 1)
|
|
422
|
+
.filter((token) => token !== undefined);
|
|
423
|
+
const client = new A2AClient(url, apiKeyAttempts[i], {
|
|
424
|
+
fallbackApiKeys,
|
|
389
425
|
});
|
|
426
|
+
let task;
|
|
427
|
+
if (useAsync) {
|
|
428
|
+
task = await client.sendAndWait(message, {
|
|
429
|
+
contextId: opts?.contextId,
|
|
430
|
+
metadata,
|
|
431
|
+
timeoutMs: opts?.timeoutMs,
|
|
432
|
+
pollIntervalMs: opts?.pollIntervalMs,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
task = await client.send(message, {
|
|
437
|
+
contextId: opts?.contextId,
|
|
438
|
+
metadata,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
// Extract text from the response
|
|
442
|
+
const responseMessage = task.status.message;
|
|
443
|
+
if (responseMessage) {
|
|
444
|
+
const textParts = responseMessage.parts
|
|
445
|
+
.filter((p) => p.type === "text")
|
|
446
|
+
.map((p) => p.text);
|
|
447
|
+
return textParts.join("\n");
|
|
448
|
+
}
|
|
449
|
+
return "";
|
|
390
450
|
}
|
|
391
451
|
catch (err) {
|
|
392
452
|
if (err instanceof A2ATaskTimeoutError) {
|
|
@@ -394,25 +454,55 @@ export async function callAgent(url, text, opts) {
|
|
|
394
454
|
if (recoverableText)
|
|
395
455
|
return recoverableText;
|
|
396
456
|
}
|
|
457
|
+
if (i < apiKeyAttempts.length - 1 && isA2AAuthRejection(err)) {
|
|
458
|
+
lastAuthError = err;
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
397
461
|
throw err;
|
|
398
462
|
}
|
|
399
463
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
contextId: opts?.contextId,
|
|
403
|
-
metadata,
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
// Extract text from the response
|
|
407
|
-
const responseMessage = task.status.message;
|
|
408
|
-
if (responseMessage) {
|
|
409
|
-
const textParts = responseMessage.parts
|
|
410
|
-
.filter((p) => p.type === "text")
|
|
411
|
-
.map((p) => p.text);
|
|
412
|
-
return textParts.join("\n");
|
|
413
|
-
}
|
|
464
|
+
if (lastAuthError)
|
|
465
|
+
throw lastAuthError;
|
|
414
466
|
return "";
|
|
415
467
|
}
|
|
468
|
+
async function buildA2AApiKeyAttempts(opts) {
|
|
469
|
+
const attempts = [];
|
|
470
|
+
const add = (token) => {
|
|
471
|
+
if (token === undefined || attempts.includes(token))
|
|
472
|
+
return;
|
|
473
|
+
attempts.push(token);
|
|
474
|
+
};
|
|
475
|
+
add(opts?.apiKey);
|
|
476
|
+
if (opts?.userEmail && (opts.orgSecret || process.env.A2A_SECRET)) {
|
|
477
|
+
if (process.env.A2A_SECRET?.trim()) {
|
|
478
|
+
try {
|
|
479
|
+
add(await signA2AToken(opts.userEmail, opts.orgDomain, opts.orgSecret, {
|
|
480
|
+
preferGlobalSecret: true,
|
|
481
|
+
}));
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
// Keep any explicit token attempt, then fall back below.
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (opts.orgSecret) {
|
|
488
|
+
try {
|
|
489
|
+
add(await signA2AToken(opts.userEmail, opts.orgDomain, opts.orgSecret, {
|
|
490
|
+
preferGlobalSecret: false,
|
|
491
|
+
}));
|
|
492
|
+
}
|
|
493
|
+
catch {
|
|
494
|
+
// Fall through to the attempts we already have.
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
if (attempts.length === 0)
|
|
499
|
+
attempts.push(undefined);
|
|
500
|
+
return attempts;
|
|
501
|
+
}
|
|
502
|
+
function isA2AAuthRejection(err) {
|
|
503
|
+
const message = err instanceof Error ? err.message : String(err ?? "");
|
|
504
|
+
return /A2A request failed \(401\)|A2A error \(-32001\): (?:Invalid or expired A2A token|Invalid API key|Authentication required)|Invalid or expired A2A token|Invalid API key|Authentication required/i.test(message);
|
|
505
|
+
}
|
|
416
506
|
function extractRecoverableArtifactText(task) {
|
|
417
507
|
if (!task.status.message?.metadata?.agentNativeRecoverableArtifacts) {
|
|
418
508
|
return "";
|