@amplitude/ai 0.2.0 → 0.3.0

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.
Files changed (106) hide show
  1. package/.claude/commands/instrument-with-amplitude-ai.md +12 -0
  2. package/.cursor/skills/instrument-with-amplitude-ai/SKILL.md +4 -42
  3. package/AGENTS.md +86 -28
  4. package/README.md +190 -111
  5. package/amplitude-ai.md +463 -0
  6. package/bin/amplitude-ai-doctor.mjs +0 -0
  7. package/bin/amplitude-ai-instrument.mjs +0 -0
  8. package/bin/amplitude-ai-mcp.mjs +0 -0
  9. package/bin/amplitude-ai-register-catalog.mjs +0 -0
  10. package/bin/amplitude-ai-status.mjs +0 -0
  11. package/bin/amplitude-ai.mjs +14 -5
  12. package/data/agent_event_catalog.json +52 -2
  13. package/dist/bound-agent.d.ts +18 -14
  14. package/dist/bound-agent.d.ts.map +1 -1
  15. package/dist/bound-agent.js +4 -1
  16. package/dist/bound-agent.js.map +1 -1
  17. package/dist/cli/status.d.ts.map +1 -1
  18. package/dist/cli/status.js +31 -19
  19. package/dist/cli/status.js.map +1 -1
  20. package/dist/client.d.ts +14 -14
  21. package/dist/client.d.ts.map +1 -1
  22. package/dist/client.js +38 -0
  23. package/dist/client.js.map +1 -1
  24. package/dist/context.d.ts +5 -0
  25. package/dist/context.d.ts.map +1 -1
  26. package/dist/context.js +4 -0
  27. package/dist/context.js.map +1 -1
  28. package/dist/core/constants.d.ts +3 -1
  29. package/dist/core/constants.d.ts.map +1 -1
  30. package/dist/core/constants.js +3 -1
  31. package/dist/core/constants.js.map +1 -1
  32. package/dist/core/tracking.d.ts +12 -2
  33. package/dist/core/tracking.d.ts.map +1 -1
  34. package/dist/core/tracking.js +13 -2
  35. package/dist/core/tracking.js.map +1 -1
  36. package/dist/decorators.d.ts +1 -1
  37. package/dist/decorators.d.ts.map +1 -1
  38. package/dist/decorators.js +4 -3
  39. package/dist/decorators.js.map +1 -1
  40. package/dist/index.d.ts +7 -4
  41. package/dist/index.js +5 -2
  42. package/dist/mcp/contract.d.ts +4 -0
  43. package/dist/mcp/contract.d.ts.map +1 -1
  44. package/dist/mcp/contract.js +6 -2
  45. package/dist/mcp/contract.js.map +1 -1
  46. package/dist/mcp/generate-verify-test.d.ts +7 -0
  47. package/dist/mcp/generate-verify-test.d.ts.map +1 -0
  48. package/dist/mcp/generate-verify-test.js +25 -0
  49. package/dist/mcp/generate-verify-test.js.map +1 -0
  50. package/dist/mcp/index.d.ts +2 -1
  51. package/dist/mcp/index.js +2 -1
  52. package/dist/mcp/instrument-file.d.ts +14 -0
  53. package/dist/mcp/instrument-file.d.ts.map +1 -0
  54. package/dist/mcp/instrument-file.js +139 -0
  55. package/dist/mcp/instrument-file.js.map +1 -0
  56. package/dist/mcp/scan-project.d.ts +52 -0
  57. package/dist/mcp/scan-project.d.ts.map +1 -0
  58. package/dist/mcp/scan-project.js +309 -0
  59. package/dist/mcp/scan-project.js.map +1 -0
  60. package/dist/mcp/server.d.ts.map +1 -1
  61. package/dist/mcp/server.js +79 -4
  62. package/dist/mcp/server.js.map +1 -1
  63. package/dist/mcp/validate-file.d.ts +4 -0
  64. package/dist/mcp/validate-file.d.ts.map +1 -1
  65. package/dist/mcp/validate-file.js +559 -11
  66. package/dist/mcp/validate-file.js.map +1 -1
  67. package/dist/middleware.js +2 -1
  68. package/dist/middleware.js.map +1 -1
  69. package/dist/node_modules/.pnpm/acorn-typescript@1.4.13_acorn@8.16.0/node_modules/acorn-typescript/lib/index.js +2389 -0
  70. package/dist/node_modules/.pnpm/acorn-typescript@1.4.13_acorn@8.16.0/node_modules/acorn-typescript/lib/index.js.map +1 -0
  71. package/dist/node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.js +5128 -0
  72. package/dist/node_modules/.pnpm/acorn@8.16.0/node_modules/acorn/dist/acorn.js.map +1 -0
  73. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js +1 -1
  74. package/dist/providers/anthropic.d.ts.map +1 -1
  75. package/dist/providers/anthropic.js +1 -0
  76. package/dist/providers/anthropic.js.map +1 -1
  77. package/dist/providers/base.d.ts +2 -1
  78. package/dist/providers/base.d.ts.map +1 -1
  79. package/dist/providers/base.js +4 -0
  80. package/dist/providers/base.js.map +1 -1
  81. package/dist/providers/openai.d.ts.map +1 -1
  82. package/dist/providers/openai.js +2 -0
  83. package/dist/providers/openai.js.map +1 -1
  84. package/dist/serverless.d.ts +19 -0
  85. package/dist/serverless.d.ts.map +1 -0
  86. package/dist/serverless.js +35 -0
  87. package/dist/serverless.js.map +1 -0
  88. package/dist/session.d.ts +24 -8
  89. package/dist/session.d.ts.map +1 -1
  90. package/dist/session.js +20 -1
  91. package/dist/session.js.map +1 -1
  92. package/dist/types.d.ts +1 -0
  93. package/dist/types.d.ts.map +1 -1
  94. package/dist/types.js.map +1 -1
  95. package/dist/utils/costs.d.ts.map +1 -1
  96. package/dist/utils/costs.js +5 -1
  97. package/dist/utils/costs.js.map +1 -1
  98. package/llms-full.txt +353 -69
  99. package/llms.txt +6 -2
  100. package/mcp.schema.json +7 -3
  101. package/package.json +10 -5
  102. package/bin/amplitude-ai-init.mjs +0 -27
  103. package/dist/cli/init.d.ts +0 -14
  104. package/dist/cli/init.d.ts.map +0 -1
  105. package/dist/cli/init.js +0 -40
  106. package/dist/cli/init.js.map +0 -1
package/llms-full.txt CHANGED
@@ -1,73 +1,260 @@
1
1
  # llms-full.txt
2
- # @amplitude/ai 0.2.0 — Detailed API Reference for LLM Agents
3
- # Use this file for instrumentation guidance. See llms.txt for discovery.
2
+ # @amplitude/ai 0.3.0 — Complete API Reference for AI Coding Agents
3
+ #
4
+ # This file is the definitive guide for any coding agent instrumenting
5
+ # a JavaScript/TypeScript AI application with @amplitude/ai.
6
+ # For tool discovery only, see llms.txt.
7
+
8
+ ## Quick Setup for AI Coding Agents
9
+
10
+ Install the SDK, then configure your coding agent's MCP server for
11
+ project analysis tools (scan_project, validate_file):
12
+
13
+ ```bash
14
+ pnpm add @amplitude/ai
15
+ ```
16
+
17
+ MCP config (one line — works with any MCP-compatible agent):
18
+ ```json
19
+ { "amplitude-ai": { "command": "npx", "args": ["amplitude-ai", "mcp"] } }
20
+ ```
21
+
22
+ Agent-specific setup:
23
+
24
+ Cursor: Add to .cursor/mcp.json:
25
+ { "mcpServers": { "amplitude-ai": { "command": "npx", "args": ["amplitude-ai", "mcp"] } } }
26
+ Then point the agent at: node_modules/@amplitude/ai/amplitude-ai.md
27
+
28
+ Claude Code:
29
+ claude mcp add amplitude-ai -- npx amplitude-ai mcp
30
+ Then point the agent at: node_modules/@amplitude/ai/amplitude-ai.md
31
+
32
+ Codex CLI: Add to ~/.codex/config.toml:
33
+ [mcp_servers.amplitude-ai]
34
+ command = "npx"
35
+ args = ["amplitude-ai", "mcp"]
36
+ AGENTS.md in the npm package is auto-read by Codex.
37
+
38
+ Without MCP: Read amplitude-ai.md for the complete guided workflow, or this file for the API reference.
39
+
40
+ ## Instrumentation Workflow (4 Phases)
41
+
42
+ Phase 1 — Detect: Run scan_project MCP tool (or read package.json + grep for providers manually).
43
+ Phase 2 — Discover: Identify agents, call sites, delegation patterns. Read source files.
44
+ Phase 3 — Instrument: Create bootstrap file, swap providers, add session wrapping, tool tracking.
45
+ Phase 4 — Verify: Run tsc --noEmit, validate_file on changed files, amplitude-ai doctor.
46
+
47
+ For the full guided workflow with code examples for every phase,
48
+ read the MCP resource: amplitude-ai://instrument-guide
49
+
50
+ ---
4
51
 
5
52
  ## Core API
6
53
 
7
- ### AmplitudeAI(config)
54
+ ### AmplitudeAI(options)
8
55
  Initialize the SDK. Required entry point.
9
56
  ```
10
- new AmplitudeAI({ apiKey: string, contentMode?: 'full' | 'metadata_only' | 'customer_enriched' })
57
+ import { AmplitudeAI, AIConfig } from '@amplitude/ai';
58
+ const ai = new AmplitudeAI({
59
+ apiKey: string,
60
+ config?: new AIConfig({
61
+ contentMode?: 'full' | 'metadata_only' | 'customer_enriched',
62
+ redactPii?: boolean,
63
+ debug?: boolean,
64
+ }),
65
+ });
11
66
  ```
12
67
 
13
- ### patch(options)
68
+ ### patch(options) / unpatch()
14
69
  Zero-code instrumentation. Monkey-patches all detected provider SDKs.
15
70
  ```
16
- patch({ amplitudeAI: AmplitudeAI })
17
- unpatch()
71
+ import { patch, unpatch } from '@amplitude/ai';
72
+ patch({ amplitudeAI: ai });
73
+ // All provider calls are now tracked. Call unpatch() to restore.
74
+ ```
75
+
76
+ ### wrap(client, ai, opts?)
77
+ Wrap an existing provider client without changing its construction.
78
+ ```
79
+ import { wrap } from '@amplitude/ai';
80
+ const instrumented = wrap(existingOpenAIClient, ai);
81
+ ```
82
+
83
+ ### ai.agent(agentId, opts?) → BoundAgent
84
+ Create a bound agent for identity and session lineage.
85
+ ```
86
+ const agent = ai.agent('shopping-agent', {
87
+ description?: string, // human-readable, appears in event streams
88
+ userId?: string,
89
+ parentAgentId?: string,
90
+ customerOrgId?: string,
91
+ agentVersion?: string,
92
+ env?: string,
93
+ context?: Record<string, unknown>,
94
+ sessionId?: string,
95
+ groups?: Record<string, unknown>,
96
+ deviceId?: string,
97
+ browserSessionId?: string,
98
+ });
99
+ ```
100
+
101
+ ### agent.child(agentId, overrides?) → BoundAgent
102
+ Create a child agent that inherits parent identity.
103
+ ```
104
+ const recipeAgent = shoppingAgent.child('recipe-agent', {
105
+ description: 'Finds recipes and checks ingredient availability',
106
+ });
107
+ ```
108
+
109
+ ### agent.session(opts?) → Session
110
+ Create a session for multi-turn tracking.
111
+ ```
112
+ const session = agent.session({
113
+ sessionId?: string, // defaults to random UUID
114
+ userId?: string,
115
+ deviceId?: string,
116
+ browserSessionId?: string, // links to Amplitude browser session
117
+ autoFlush?: boolean, // auto-flush on completion (default: auto-detect serverless)
118
+ });
119
+ ```
120
+
121
+ ### session.run(fn) / session.runSync(fn)
122
+ Execute code within session context. Auto-ends session when fn completes.
123
+ Provider calls inside run() are automatically tagged with session/agent identity.
124
+ ```
125
+ await session.run(async (s) => {
126
+ s.trackUserMessage(content);
127
+ // ... LLM calls are auto-tracked by provider wrappers ...
128
+ s.trackAiMessage(response, model, provider, latencyMs);
129
+ });
130
+ // Session automatically ends here, [Agent] Session End emitted.
131
+ ```
132
+
133
+ ### s.runAs(childAgent, fn) / s.runAsSync(childAgent, fn)
134
+ Delegate to a child agent within the same session.
135
+ Shares sessionId, traceId, turn counter. Does NOT emit Session End for child.
136
+ ```
137
+ await s.runAs(recipeAgent, async (cs) => {
138
+ cs.trackUserMessage('Find me a pancake recipe');
139
+ // LLM calls here are tagged with recipe-agent's agentId
140
+ // parentAgentId is automatically set to shopping-agent
141
+ });
142
+ ```
143
+
144
+ ### s.trackUserMessage(content, opts?) → eventId
145
+ Track a user message. Returns the event ID.
146
+ ```
147
+ s.trackUserMessage('What recipes do you have for pancakes?');
148
+ ```
149
+
150
+ ### s.trackAiMessage(content, model, provider, latencyMs, opts?) → eventId
151
+ Track an AI response. Use when provider wrappers can't auto-capture (e.g. Assistants API).
152
+ ```
153
+ s.trackAiMessage('Here are some pancake recipes...', 'gpt-4o-mini', 'openai', 1250, {
154
+ inputTokens: 150, outputTokens: 400, totalTokens: 550,
155
+ });
156
+ ```
157
+
158
+ ### s.trackToolCall(toolName, latencyMs, success, opts?) → eventId
159
+ Track a tool call explicitly (prefer tool() HOF instead).
160
+ ```
161
+ s.trackToolCall('search_products', 45, true);
18
162
  ```
19
163
 
20
- ### wrap(client, amplitude, opts?)
21
- Convert an existing provider client into an instrumented wrapper.
164
+ ### s.score(name, value, targetId, opts?)
165
+ Track a quality score (user feedback, automated eval, reviewer).
22
166
  ```
23
- wrap(openaiClient, ai) OpenAI wrapper
24
- wrap(anthropicClient, ai) → Anthropic wrapper
25
- wrap(azureClient, ai) → AzureOpenAI wrapper
167
+ s.score('user-feedback', 1, 'msg-001', { targetType: 'message', source: 'user' });
26
168
  ```
27
169
 
28
- ### ai.agent(agentId, options)
29
- Create a bound agent for user/session lineage.
170
+ ### ai.score(opts)
171
+ Track a score outside session context.
30
172
  ```
31
- const agent = ai.agent('my-agent', { userId: 'u1' })
32
- const child = agent.child('sub-agent')
33
- const session = agent.session({ sessionId: 's1' })
34
- await session.run(async (s) => { ... })
35
- await s.runAs(child, async (cs) => { ... }) // delegate to child agent
173
+ ai.score({ userId, name: 'user-feedback', value: 1, targetId: 'msg-001', targetType: 'message', source: 'user' });
36
174
  ```
37
175
 
38
- ### tool(fn, options) / tool(options)(fn)
39
- Wrap a function to emit [Agent] Tool Call events.
176
+ ### ai.flush() / agent.flush()
177
+ Flush pending events. CRITICAL for serverless (Next.js, Vercel, Lambda):
178
+ ```
179
+ await session.run(async (s) => { /* ... */ });
180
+ await ai.flush(); // Must call before returning response in serverless!
40
181
  ```
41
- const search = tool(async (query: string) => results, {
182
+
183
+ ### tool(fn, opts?) / tool(opts)(fn)
184
+ Higher-order function that wraps a function to auto-emit [Agent] Tool Call events.
185
+ Works inside session.run() — automatically inherits session/agent context.
186
+ ```
187
+ import { tool } from '@amplitude/ai';
188
+ const searchProducts = tool(async (args: { query: string }) => { /* ... */ }, {
42
189
  name: 'search_products',
43
- inputSchema: { type: 'object', properties: { query: { type: 'string' } } },
44
- })
190
+ });
191
+ // Inside session.run(), just call the wrapped function:
192
+ const results = await searchProducts({ query: 'pancakes' });
193
+ // [Agent] Tool Call event is automatically emitted with duration, success, input/output.
45
194
  ```
46
195
 
47
- ### observe(fn, options) / observe(options)(fn)
48
- Wrap a function to emit [Agent] Span events with session lifecycle.
196
+ ### observe(fn, opts?) / observe(opts)(fn)
197
+ Higher-order function that wraps a function to auto-emit [Agent] Span events.
198
+ Creates a session boundary if none exists.
49
199
  ```
50
- const handler = observe(async (req) => response, { name: 'request-handler' })
200
+ import { observe } from '@amplitude/ai';
201
+ const handleRequest = observe(async (req) => { /* ... */ }, { name: 'request-handler' });
51
202
  ```
52
203
 
53
- ### MockAmplitudeAI
54
- Deterministic testing helper. Drop-in replacement.
204
+ ### injectContext() / extractContext(headers)
205
+ Cross-service context propagation for message queues and microservices.
55
206
  ```
56
- const mock = new MockAmplitudeAI()
57
- mock.getEvents('[Agent] AI Response') → BaseEvent[]
58
- mock.flush() → BaseEvent[]
207
+ import { injectContext, extractContext } from '@amplitude/ai';
208
+ // Sender (inside session.run):
209
+ const headers = injectContext();
210
+ await queue.send({ payload, headers });
211
+ // Receiver:
212
+ const ctx = extractContext(message.headers);
213
+ const session = agent.session({ ...ctx });
59
214
  ```
60
215
 
216
+ ### createAmplitudeAIMiddleware(opts)
217
+ Express/Fastify/Hono middleware for automatic session tracking.
218
+ ```
219
+ import { createAmplitudeAIMiddleware } from '@amplitude/ai';
220
+ app.use(createAmplitudeAIMiddleware({
221
+ amplitudeAI: ai,
222
+ userIdResolver: (req) => req.headers['x-user-id'] ?? null,
223
+ }));
224
+ ```
225
+
226
+ ### MockAmplitudeAI (testing)
227
+ Deterministic test double. Captures events in-memory.
228
+ ```
229
+ import { AIConfig } from '@amplitude/ai';
230
+ import { MockAmplitudeAI } from '@amplitude/ai/testing';
231
+ const mock = new MockAmplitudeAI(new AIConfig({ contentMode: 'full' }));
232
+ const agent = mock.agent('test-agent', { userId: 'u1' });
233
+ await agent.session({ sessionId: 's1' }).run(async (s) => { /* ... */ });
234
+ mock.getEvents() // all events
235
+ mock.getEvents('[Agent] Tool Call') // filtered by type
236
+ mock.eventsForAgent('recipe-agent') // filtered by agent ID
237
+ mock.assertEventTracked('[Agent] User Message', { userId: 'u1' })
238
+ mock.assertSessionClosed('s1')
239
+ await mock.flush();
240
+ ```
241
+
242
+ ---
243
+
61
244
  ## Provider Wrappers
62
245
 
63
- | Provider | Import | Streaming | Tool Calls | TTFB | Cache Tokens |
64
- |-------------|---------------------------------|-----------|------------|------|--------------|
65
- | OpenAI | `new OpenAI({ amplitude })` | Yes | Yes | Yes | Yes |
66
- | Anthropic | `new Anthropic({ amplitude })` | Yes | Yes | Yes | Yes |
67
- | Gemini | `new Gemini({ amplitude })` | Yes | No | No | No |
68
- | AzureOpenAI | `new AzureOpenAI({ amplitude })` | Yes | Yes | Yes | No |
69
- | Bedrock | `new Bedrock({ amplitude })` | Yes | Yes | No | No |
70
- | Mistral | `new Mistral({ amplitude })` | Yes | No | No | No |
246
+ | Provider | Import | Streaming | Tool Calls | TTFB | Cache |
247
+ |-------------|----------------------------------|-----------|------------|------|-------|
248
+ | OpenAI | `new OpenAI({ amplitude: ai })` | Yes | Yes | Yes | Yes |
249
+ | Anthropic | `new Anthropic({ amplitude: ai })`| Yes | Yes | Yes | Yes |
250
+ | Gemini | `new Gemini({ amplitude: ai })` | Yes | No | No | No |
251
+ | AzureOpenAI | `new AzureOpenAI({ amplitude: ai })`| Yes | Yes | Yes | No |
252
+ | Bedrock | `new Bedrock({ amplitude: ai })` | Yes | Yes | No | No |
253
+ | Mistral | `new Mistral({ amplitude: ai })` | Yes | No | No | No |
254
+
255
+ All wrappers are imported from '@amplitude/ai'.
256
+
257
+ ---
71
258
 
72
259
  ## Canonical Patterns
73
260
 
@@ -85,59 +272,156 @@ const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
85
272
  const instrumented = wrap(existingOpenAIClient, ai);
86
273
  ```
87
274
 
88
- ### 3. Provider wrapper
275
+ ### 3. Provider wrapper (recommended)
89
276
  ```typescript
90
- import { AmplitudeAI, OpenAI } from '@amplitude/ai';
91
- const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
92
- const client = new OpenAI({ amplitude: ai, apiKey: process.env.OPENAI_API_KEY! });
277
+ import { AmplitudeAI, AIConfig, OpenAI } from '@amplitude/ai';
278
+ const ai = new AmplitudeAI({
279
+ apiKey: process.env.AMPLITUDE_AI_API_KEY!,
280
+ config: new AIConfig({ contentMode: 'full', redactPii: true }),
281
+ });
282
+ const openai = new OpenAI({ amplitude: ai, apiKey: process.env.OPENAI_API_KEY! });
93
283
  ```
94
284
 
95
- ### 4. Bound agent + session
285
+ ### 4. Multi-agent with session + runAs (recommended for multi-agent apps)
96
286
  ```typescript
97
- const agent = ai.agent('assistant', { userId: 'u1' });
98
- const session = agent.session({ sessionId: 's1' });
99
- await session.run(async () => { /* LLM calls tracked automatically */ });
287
+ import { AmplitudeAI, AIConfig, OpenAI, tool } from '@amplitude/ai';
288
+
289
+ const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY!, config: new AIConfig({ contentMode: 'full', redactPii: true }) });
290
+ const openai = new OpenAI({ amplitude: ai, apiKey: process.env.OPENAI_API_KEY! });
291
+
292
+ const orchestrator = ai.agent('shopping-agent', { description: 'Orchestrates shopping requests' });
293
+ const recipeAgent = orchestrator.child('recipe-agent', { description: 'Finds recipes' });
294
+
295
+ const askRecipeAgent = tool(async (args: { query: string }) => {
296
+ // ... calls openai inside s.runAs(recipeAgent, ...)
297
+ }, { name: 'ask_recipe_agent' });
298
+
299
+ export async function POST(req: Request) {
300
+ const { messages, userId } = await req.json();
301
+ const browserSessionId = req.headers.get('x-amplitude-session-id');
302
+ const deviceId = req.headers.get('x-amplitude-device-id');
303
+
304
+ return orchestrator.session({ userId, browserSessionId, deviceId }).run(async (s) => {
305
+ s.trackUserMessage(messages[messages.length - 1].content);
306
+
307
+ // Provider calls auto-tracked. tool() calls auto-tracked.
308
+ const response = await openai.chat.completions.create({ model: 'gpt-4o-mini', messages });
309
+
310
+ s.trackAiMessage(response.choices[0].message.content, 'gpt-4o-mini', 'openai', latencyMs);
311
+ await ai.flush(); // serverless: flush before returning
312
+ return Response.json({ content: response.choices[0].message.content });
313
+ });
314
+ }
100
315
  ```
101
316
 
102
317
  ### 5. Express middleware
103
318
  ```typescript
104
- import { createAmplitudeAIMiddleware } from '@amplitude/ai';
105
- app.use(createAmplitudeAIMiddleware());
319
+ import { AmplitudeAI, createAmplitudeAIMiddleware } from '@amplitude/ai';
320
+ const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
321
+ app.use(createAmplitudeAIMiddleware({
322
+ amplitudeAI: ai,
323
+ userIdResolver: (req) => req.headers['x-user-id'] ?? null,
324
+ }));
106
325
  ```
107
326
 
108
- ### 6. Multi-agent orchestration with session.runAs()
327
+ ### 6. Testing with MockAmplitudeAI
109
328
  ```typescript
110
- const orchestrator = ai.agent('orchestrator', { userId: 'u1' });
111
- const researcher = orchestrator.child('researcher');
112
- const session = orchestrator.session({ sessionId: 's1' });
329
+ import { AIConfig, tool } from '@amplitude/ai';
330
+ import { MockAmplitudeAI } from '@amplitude/ai/testing';
331
+ const mock = new MockAmplitudeAI(new AIConfig({ contentMode: 'full' }));
332
+ const agent = mock.agent('test-agent');
333
+ const session = agent.session({ userId: 'test-user', sessionId: 'test-session' });
113
334
  await session.run(async (s) => {
114
- // Provider calls inside runAs are automatically tagged with the child's agentId
115
- const result = await s.runAs(researcher, async (cs) => {
116
- return openai.chat.completions.create({ model: 'gpt-4o', messages: [...] });
117
- });
335
+ s.trackUserMessage('hello');
118
336
  });
119
- // runAs shares sessionId, traceId, turn counter; does NOT emit Session End
337
+ expect(mock.getEvents('[Agent] User Message').length).toBe(1);
338
+ mock.assertSessionClosed('test-session');
120
339
  ```
121
340
 
122
- ## MCP Tools
341
+ ---
342
+
343
+ ## MCP Tools (via amplitude-ai mcp)
344
+
345
+ All tools are available when the MCP server is connected.
346
+
347
+ ### scan_project(root_path) → ScanResult
348
+ Detect framework, providers, agents, call sites, and multi-agent signals.
349
+ Returns structured JSON with: framework, providers[], agents[] (each with inferred_id,
350
+ file, call_site_details, tool_definitions), is_multi_agent, multi_agent_signals[],
351
+ has_streaming, has_edge_runtime, has_frontend_deps, recommendations[].
352
+ Use this as the first step of instrumentation.
353
+
354
+ ### validate_file(source, language?) → FileAnalysis
355
+ AST-based analysis of a single file for uninstrumented LLM call sites.
356
+ Returns: call_sites[] with instrumented (bool), has_patch, has_amplitude_import,
357
+ has_session_context. Run before and after instrumentation to verify coverage.
358
+
359
+ ### instrument_file(source, language?, scan_result?) → InstrumentedSource
360
+ Apply text-based provider swap and session transforms.
361
+ Best for simple import swaps. For multi-agent patterns, prefer direct editing.
362
+
363
+ ### generate_verify_test(scan_result) → TestSource
364
+ Generate a vitest verification test using MockAmplitudeAI.
365
+ Tests each discovered agent and multi-agent delegation.
366
+
367
+ ### validate_setup() → SetupStatus
368
+ Check required environment variables (AMPLITUDE_AI_API_KEY, etc.).
123
369
 
124
- - `get_event_schema(event_type?)` Return event schema and property definitions
125
- - `get_integration_pattern(id?)` Return canonical instrumentation patterns
126
- - `validate_setup()` — Check required environment variables
127
- - `suggest_instrumentation(framework?, provider?, content_tier?)` Value-first setup guidance with content-tier and privacy defaults
128
- - `validate_file(source, language?)` Detect uninstrumented LLM call sites
129
- - `search_docs(query, max_results?)` — Search README and API reference by keyword
370
+ ### suggest_instrumentation(framework?, provider?, content_tier?) Guidance
371
+ Framework-specific instrumentation guidance with content-tier and privacy defaults.
372
+
373
+ ### get_event_schema(event_type?) EventSchema
374
+ Return the event property catalog for all or specific event types.
375
+
376
+ ### get_integration_pattern(id?) → Pattern
377
+ Return canonical instrumentation patterns (zero-code, wrap, bound-agent, etc.).
378
+
379
+ ### search_docs(query, max_results?) → SearchResults
380
+ Keyword search over README and this file.
381
+
382
+ ## MCP Resources
383
+
384
+ ### amplitude-ai://event-schema
385
+ Full event property catalog (all [Agent] event types and their properties).
386
+
387
+ ### amplitude-ai://integration-patterns
388
+ Canonical instrumentation patterns as structured JSON.
389
+
390
+ ### amplitude-ai://instrument-guide
391
+ The complete 4-phase instrumentation workflow (Detect → Discover → Instrument → Verify)
392
+ with code examples for every step. Read this for guided instrumentation.
393
+
394
+ ---
130
395
 
131
396
  ## CLI
132
397
 
133
- - `amplitude-ai init [--dry-run] [--force]` — Scaffold .env.example and setup file
398
+ - `amplitude-ai mcp` — Start the MCP server for AI coding agents
134
399
  - `amplitude-ai doctor [--json] [--no-mock-check]` — Validate environment and event pipeline
135
400
  - `amplitude-ai status [--json]` — Show SDK version, installed providers, and env config
136
- - `amplitude-ai mcp` — Start MCP server over stdio
137
401
  - `amplitude-ai --help` / `amplitude-ai --version`
138
402
 
403
+ ---
404
+
405
+ ## Events Emitted
406
+
407
+ | Event Type | Emitted By |
408
+ |---|---|
409
+ | [Agent] User Message | s.trackUserMessage() |
410
+ | [Agent] AI Response | Provider wrappers (auto) or s.trackAiMessage() |
411
+ | [Agent] Tool Call | tool() HOF (auto) or s.trackToolCall() |
412
+ | [Agent] Span | observe() HOF (auto) or s.trackSpan() |
413
+ | [Agent] Session End | session.run() completion (auto) or s.trackSessionEnd() |
414
+ | [Agent] Score | ai.score() or s.score() |
415
+ | [Agent] Embedding | provider wrappers (auto) |
416
+ | [Agent] Session Enrichment | s.trackSessionEnrichment() |
417
+
418
+ ---
419
+
139
420
  ## Common Errors
140
421
 
141
422
  - "No events captured" → Ensure session.run() wraps your LLM calls
142
423
  - "patch() drops events silently" → patch() requires active SessionContext; use session.run()
143
- - "flush() timeout" → Call ai.flush() before process exit in serverless
424
+ - "flush() timeout" → Call ai.flush() before process exit in serverless (Next.js, Lambda)
425
+ - "tool() not tracking" → tool() must be called inside session.run() to inherit context
426
+ - "child agent events missing parentAgentId" → Use s.runAs(child, fn), not direct child calls
427
+ - "observe() not emitting spans" → observe() must be called inside session.run() for session context
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
- <!-- GENERATED FILE: do not edit manually -->
1
+ <!-- GENERATED FILE: do not edit manually. Update scripts/generate-agent-docs.mjs instead. -->
2
2
  # llms.txt
3
3
  package=@amplitude/ai
4
- version=0.2.0
4
+ version=0.3.0
5
5
 
6
6
  [mcp.tools]
7
7
  get_event_schema
@@ -10,10 +10,14 @@ validate_setup
10
10
  suggest_instrumentation
11
11
  validate_file
12
12
  search_docs
13
+ scan_project
14
+ generate_verify_test
15
+ instrument_file
13
16
 
14
17
  [mcp.resources]
15
18
  amplitude-ai://event-schema
16
19
  amplitude-ai://integration-patterns
20
+ amplitude-ai://instrument-guide
17
21
 
18
22
  [events]
19
23
  [Agent] AI Response
package/mcp.schema.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "generated": true,
3
3
  "package": "@amplitude/ai",
4
- "version": "0.2.0",
4
+ "version": "0.3.0",
5
5
  "prompt": "instrument_app",
6
6
  "tools": [
7
7
  "get_event_schema",
@@ -9,10 +9,14 @@
9
9
  "validate_setup",
10
10
  "suggest_instrumentation",
11
11
  "validate_file",
12
- "search_docs"
12
+ "search_docs",
13
+ "scan_project",
14
+ "generate_verify_test",
15
+ "instrument_file"
13
16
  ],
14
17
  "resources": [
15
18
  "amplitude-ai://event-schema",
16
- "amplitude-ai://integration-patterns"
19
+ "amplitude-ai://integration-patterns",
20
+ "amplitude-ai://instrument-guide"
17
21
  ]
18
22
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amplitude/ai",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
5
  "description": "Amplitude AI SDK - LLM usage tracking for Amplitude Analytics",
6
6
  "keywords": [
@@ -30,7 +30,6 @@
30
30
  ".": "./dist/index.js",
31
31
  "./bound-agent": "./dist/bound-agent.js",
32
32
  "./cli/doctor": "./dist/cli/doctor.js",
33
- "./cli/init": "./dist/cli/init.js",
34
33
  "./cli/providers": "./dist/cli/providers.js",
35
34
  "./cli/status": "./dist/cli/status.js",
36
35
  "./client": "./dist/client.js",
@@ -51,7 +50,10 @@
51
50
  "./internals": "./dist/internals.js",
52
51
  "./mcp": "./dist/mcp/index.js",
53
52
  "./mcp/contract": "./dist/mcp/contract.js",
53
+ "./mcp/generate-verify-test": "./dist/mcp/generate-verify-test.js",
54
+ "./mcp/instrument-file": "./dist/mcp/instrument-file.js",
54
55
  "./mcp/patterns": "./dist/mcp/patterns.js",
56
+ "./mcp/scan-project": "./dist/mcp/scan-project.js",
55
57
  "./mcp/server": "./dist/mcp/server.js",
56
58
  "./mcp/validate-file": "./dist/mcp/validate-file.js",
57
59
  "./middleware": "./dist/middleware.js",
@@ -65,6 +67,7 @@
65
67
  "./providers/mistral": "./dist/providers/mistral.js",
66
68
  "./providers/openai": "./dist/providers/openai.js",
67
69
  "./register": "./dist/register.js",
70
+ "./serverless": "./dist/serverless.js",
68
71
  "./session": "./dist/session.js",
69
72
  "./tenant": "./dist/tenant.js",
70
73
  "./testing": "./dist/testing.js",
@@ -87,13 +90,13 @@
87
90
  "amplitude-ai": "./bin/amplitude-ai.mjs",
88
91
  "amplitude-ai-completions": "./bin/amplitude-ai-completions.mjs",
89
92
  "amplitude-ai-doctor": "./bin/amplitude-ai-doctor.mjs",
90
- "amplitude-ai-init": "./bin/amplitude-ai-init.mjs",
91
93
  "amplitude-ai-instrument": "./bin/amplitude-ai-instrument.mjs",
92
94
  "amplitude-ai-mcp": "./bin/amplitude-ai-mcp.mjs",
93
95
  "amplitude-ai-register-catalog": "./bin/amplitude-ai-register-catalog.mjs",
94
96
  "amplitude-ai-status": "./bin/amplitude-ai-status.mjs"
95
97
  },
96
98
  "files": [
99
+ "amplitude-ai.md",
97
100
  "AGENTS.md",
98
101
  "dist",
99
102
  "bin",
@@ -101,7 +104,8 @@
101
104
  "llms.txt",
102
105
  "llms-full.txt",
103
106
  "mcp.schema.json",
104
- ".cursor/skills/instrument-with-amplitude-ai/SKILL.md"
107
+ ".cursor/skills/instrument-with-amplitude-ai/SKILL.md",
108
+ ".claude/commands/instrument-with-amplitude-ai.md"
105
109
  ],
106
110
  "scripts": {
107
111
  "build": "tsdown",
@@ -117,7 +121,6 @@
117
121
  "test:typescript": "tsc -p tsconfig.json --noEmit",
118
122
  "prepublishOnly": "pnpm run build"
119
123
  },
120
- "dependencies": {},
121
124
  "devDependencies": {
122
125
  "@amplitude/analytics-node": "1.3.5",
123
126
  "@biomejs/biome": "1.9.4",
@@ -174,6 +177,8 @@
174
177
  },
175
178
  "optionalDependencies": {
176
179
  "@modelcontextprotocol/sdk": "1.27.1",
180
+ "acorn": "^8.16.0",
181
+ "acorn-typescript": "^1.4.13",
177
182
  "zod": "3.25.76"
178
183
  },
179
184
  "pnpm": {
@@ -1,27 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { runInit } from '../dist/cli/init.js';
4
-
5
- const args = process.argv.slice(2);
6
- const dryRun = args.includes('--dry-run');
7
- const force = args.includes('--force');
8
-
9
- const result = runInit({
10
- cwd: process.cwd(),
11
- dryRun,
12
- force,
13
- });
14
-
15
- process.stdout.write(
16
- JSON.stringify(
17
- {
18
- command: 'init',
19
- dryRun,
20
- force,
21
- created: result.created,
22
- skipped: result.skipped,
23
- },
24
- null,
25
- 2,
26
- ) + '\n',
27
- );
@@ -1,14 +0,0 @@
1
- //#region src/cli/init.d.ts
2
- type InitOptions = {
3
- cwd: string;
4
- force: boolean;
5
- dryRun: boolean;
6
- };
7
- type InitResult = {
8
- created: string[];
9
- skipped: string[];
10
- };
11
- declare const runInit: (options: InitOptions) => InitResult;
12
- //#endregion
13
- export { type InitOptions, type InitResult, runInit };
14
- //# sourceMappingURL=init.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"init.d.ts","names":[],"sources":["../../src/cli/init.ts"],"sourcesContent":[],"mappings":";KAGK,WAAA;EAAA,GAAA,EAAA,MAAA;EAMA,KAAA,EAAA,OAAU;EAqBT,MAAA,EAAA,OAmBL;;KAxCI,UAAA;;;;cAqBC,mBAAoB,gBAAc"}