@amplitude/ai 0.3.5 → 0.3.7

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/AGENTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AGENTS.md
2
2
 
3
- Package: `@amplitude/ai` v0.3.5
3
+ Package: `@amplitude/ai` v0.3.7
4
4
 
5
5
  ## Install
6
6
 
@@ -53,12 +53,12 @@ Codex auto-reads this `AGENTS.md` file for context.
53
53
 
54
54
  ## Decision Tree
55
55
 
56
- - Need zero-code coverage: use `patch()`.
57
- - Already have a provider client: use `wrap()` or provider wrappers.
58
- - Need user/session lineage: use `ai.agent(...).session(...)`.
56
+ - **Default: use `ai.agent(...).session(...)` with provider wrappers** — gives you every event type, per-user analytics, session enrichment, quality scoring.
57
+ - Already have a provider client: use `wrap()` to instrument it.
59
58
  - Multiple agents collaborating: use `session.runAs(childAgent, fn)` for automatic identity propagation.
60
59
  - Need tool telemetry: use `tool()`.
61
60
  - Need span/observability: use `observe()`.
61
+ - Cannot modify call sites at all: use `patch()` for aggregate-only monitoring (no per-user analytics).
62
62
  - Need agent-assistant guidance: run MCP prompt `instrument_app`.
63
63
 
64
64
  ## MCP Surface
@@ -102,6 +102,8 @@ Prompt:
102
102
 
103
103
  ## CLI
104
104
 
105
+ - `amplitude-ai` — Print instrumentation prompt for AI coding agents
106
+ - `amplitude-ai --print-guide` — Print the full amplitude-ai.md guide to stdout
105
107
  - `amplitude-ai mcp` — Start the MCP server for AI coding agents
106
108
  - `amplitude-ai doctor [--json]` — Validate environment, deps, and event pipeline
107
109
  - `amplitude-ai status [--json]` — Show SDK version, installed providers, and env config
package/README.md CHANGED
@@ -51,14 +51,19 @@ The CLI prints a prompt to paste into any AI coding agent (Cursor, Claude Code,
51
51
 
52
52
  The agent reads the guide, scans your project, discovers your agents and LLM call sites, and instruments everything — provider wrappers, session lifecycle, multi-agent delegation, tool tracking, scoring, and a verification test. You review and approve each step.
53
53
 
54
- ### Other paths
54
+ ### Manual setup
55
55
 
56
- | Your situation | Recommended path | What happens |
57
- |---|---|---|
58
- | **Manual setup** | Follow the [Quick Start guide](#quick-start) | Agents + sessions + provider wrappers — the full event model |
59
- | **Just want to verify the SDK works** | `patch()` ([details below](#patching)) | Aggregate cost/latency monitoring only — no user analytics, no funnels |
56
+ Whether you use a coding agent or set up manually, the goal is the same: **full instrumentation** — agents + sessions + provider wrappers. This gives you every event type, per-user analytics, and server-side enrichment.
57
+
58
+ Follow the [code example above](#amplitude-ai) to get started. The pattern is:
59
+
60
+ 1. **Swap your LLM import** — `import { OpenAI } from '@amplitude/ai'` (or `Anthropic`, `Gemini`, etc.)
61
+ 2. **Create an agent** — `ai.agent('my-agent')` to name and track your AI component
62
+ 3. **Wrap in a session** — `agent.session({ userId, sessionId }).run(async (s) => { ... })` for per-user analytics, funnels, cohorts, and server-side enrichment
63
+ 4. **Track user messages** — `s.trackUserMessage(...)` for conversation context
64
+ 5. **Score responses** — `s.score(...)` for quality measurement
60
65
 
61
- > **Start with full instrumentation.** The coding agent workflow defaults to agents + sessions + provider wrappers. This gives you every event type, per-user analytics, and server-side enrichment. `patch()` exists for quick verification or legacy codebases where you can't modify call sites, but it only captures `[Agent] AI Response` without user identity — no funnels, no cohorts, no retention.
66
+ > `patch()` exists for quick verification or legacy codebases where you can't modify call sites, but it only captures `[Agent] AI Response` without user identity — no funnels, no cohorts, no retention. Start with full instrumentation; fall back to `patch()` only if you can't modify call sites.
62
67
 
63
68
  | Property | Value |
64
69
  | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -100,7 +105,7 @@ The agent reads the guide, scans your project, discovers your agents and LLM cal
100
105
  - [Auto-Instrumentation CLI](#auto-instrumentation-cli)
101
106
  - [Integrations](#integrations)
102
107
  - [Data Flow](#data-flow)
103
- - [Which Integration Should I Use?](#which-integration-should-i-use)
108
+ - [Integration Approaches](#integration-approaches)
104
109
  - [Integration Patterns](#integration-patterns)
105
110
  - [Serverless Environments](#serverless-environments)
106
111
  - [Error Handling and Reliability](#error-handling-and-reliability)
@@ -137,7 +142,7 @@ Install provider SDKs based on what you use (for example: `openai`, `@anthropic-
137
142
 
138
143
  1. **Install:** `npm install @amplitude/ai @amplitude/analytics-node`
139
144
  2. **Get your API key:** In Amplitude, go to **Settings > Projects** and copy the API key.
140
- 3. **Auto-instrument:** Run `npx amplitude-ai` and paste the printed prompt into your AI coding agent it scans your project, generates a bootstrap file, instruments your LLM call sites, and creates a verification test. Or follow the manual patterns below.
145
+ 3. **Instrument:** Run `npx amplitude-ai` and paste the printed prompt into your AI coding agent. Or follow the [manual setup](#manual-setup) steps the goal is the same: agents + sessions + provider wrappers.
141
146
  4. **Set your API key** in the generated `.env` file and replace the placeholder `userId`/`sessionId`.
142
147
  5. **Run your app.** You should see `[Agent] User Message`, `[Agent] AI Response`, and `[Agent] Session End` within 30 seconds.
143
148
 
@@ -504,7 +509,13 @@ const session = agent.session({
504
509
  });
505
510
  ```
506
511
 
507
- Without this, sessions with long idle periods may be closed and evaluated prematurely. The default is 30 minutes.
512
+ Without this, sessions with long idle periods may be closed and enrichment may run earlier than expected. The default is 30 minutes.
513
+
514
+ **Session lifecycle and enrichment.** You do **not** need to call `trackSessionEnd()` for sessions to work. Amplitude's server automatically closes sessions after 30 minutes of inactivity and queues them for enrichment (topic classification, quality scoring, session evaluation) at that point. The only reason to call `trackSessionEnd()` is to **trigger enrichment sooner** — for example, if you know the conversation is over and want evaluation results immediately rather than waiting for the idle timeout.
515
+
516
+ "Closed" is a server-side concept meaning "queued for enrichment" — it does **not** prevent new events from flowing into the same session. If the user resumes a conversation after session end, new messages with the same `sessionId` are still associated with that session.
517
+
518
+ If you use `session.run()`, session end is tracked automatically when the callback completes. For long-lived conversations (chatbots, support agents), you can skip explicit session end entirely and let the server handle it.
508
519
 
509
520
  **Link to Session Replay**: If your frontend uses Amplitude's [Session Replay](https://www.docs.developers.amplitude.com/session-replay/), pass the browser's `deviceId` and `browserSessionId` to link AI sessions to browser recordings:
510
521
 
@@ -1643,33 +1654,20 @@ Your Application
1643
1654
  - The LLM Enrichment Pipeline runs asynchronously after session close (only when `contentMode: 'full'`). It produces server-side events like `[Agent] Session Evaluation` and `[Agent] Score`.
1644
1655
  - With `contentMode: 'customer_enriched'`, the enrichment pipeline is skipped — you provide your own enrichments via `trackSessionEnrichment()`.
1645
1656
 
1646
- ## Which Integration Should I Use?
1647
-
1648
- Start here and pick the first tier that satisfies your analytics needs:
1649
-
1650
- ```
1651
- Do you need per-user analytics
1652
- (funnels, cohorts, retention)?
1653
-
1654
- ┌─── No ──┴── Yes ──┐
1655
- │ │
1656
- Tier 0: Zero-Code Do you need session
1657
- (CLI auto-patch) grouping & enrichment?
1658
- Cost, latency, │
1659
- tokens, errors. ┌─ No ──┴── Yes ──┐
1660
- │ │
1661
- Tier 1: Events Do you control
1662
- Per-call tracking the LLM call site?
1663
- + userId │
1664
- ┌── Yes ──┴── No ──┐
1665
- │ │
1666
- Tier 2: Sessions Tier 3: OTEL Bridge
1667
- session.run() Add exporter to
1668
- Full enrichment existing OTEL pipeline
1669
- Implicit feedback Limited to OTEL attrs
1670
- ```
1671
-
1672
- **Rule of thumb:** If you own the LLM call site, start with **Tier 2** (sessions). If you don't (e.g., a third-party framework exports OTEL spans), use **Tier 3** (OTEL bridge). If you just want aggregate cost monitoring without user analytics, **Tier 0** (zero-code) is ready in 60 seconds.
1657
+ ## Integration Approaches
1658
+
1659
+ **Start with full instrumentation.** Use agents + sessions + provider wrappers. This is the recommended approach for both coding agent and manual workflows — it gives you every event type, per-user analytics, and server-side enrichment.
1660
+
1661
+ | Approach | When to use | What you get |
1662
+ |---|---|---|
1663
+ | **Full control** (recommended) | Any project, new or existing | `BoundAgent` + `session.run()` + provider wrappers — all event types, per-user funnels, cohorts, retention, quality scoring, enrichments |
1664
+ | **Express/Fastify middleware** | Web app, auto-session per request | Same as full control with automatic session lifecycle via `createAmplitudeAIMiddleware` |
1665
+ | **Swap import** | Existing codebase, incremental adoption | `new OpenAI({ amplitude: ai })` — auto-tracking per call, add sessions when ready |
1666
+ | **Wrap** | You've already created a client | `wrap(client, ai)` — instruments an existing client instance |
1667
+ | **Zero-code / `patch()`** | Verification or legacy codebases only | `patch({ amplitudeAI: ai })` — `[Agent] AI Response` only, no user identity, no funnels |
1668
+ | **OTEL Bridge** | Third-party framework exports OTEL spans | Add exporter to existing OTEL pipeline — limited to OTEL attributes |
1669
+
1670
+ > The first four approaches all support the full event model. Choose based on how you want to integrate — the analytics capabilities are the same. **`patch()` is the exception**: it only captures aggregate `[Agent] AI Response` events without user identity, useful only for verifying the SDK works or for codebases where you can't modify call sites.
1673
1671
 
1674
1672
  ## Integration Patterns
1675
1673
 
package/amplitude-ai.md CHANGED
@@ -29,30 +29,21 @@ Detected environment:
29
29
  Multi-agent signals: [yes/no]
30
30
  Streaming: [yes/no]
31
31
  Frontend deps: [yes/no]
32
- Recommended tier: [quick_start / standard / advanced]
32
+ Recommended: full instrumentation
33
33
  ```
34
34
 
35
- **Decision point:** Ask the developer to confirm the detection and choose a tier:
36
- - **Quick start** — `patch({ amplitudeAI: ai })`, zero code changes, good for getting data flowing
37
- - **Standard** — Provider wrappers + session middleware (recommended for most apps)
38
- - **Advanced** — Multi-agent `runAs`, agent descriptions, scoring, tool tracking
39
-
40
- If multi-agent signals are detected, recommend Advanced.
35
+ **Next step:** Confirm the detection with the developer, then proceed to full instrumentation. Always instrument with agents, sessions, provider wrappers, tool tracking, and scoring. If multi-agent signals are detected, also add child agents and `runAs` delegation.
41
36
 
42
37
  ---
43
38
 
44
39
  ## Phase 2: Discover Agents and Call Sites
45
40
 
46
- For **Quick start** tier, skip to Phase 3 — discovery is just "which providers are imported."
47
-
48
- For **Standard** and **Advanced** tiers:
49
-
50
41
  1. Identify files with LLM call sites (search for `chat.completions.create`, `messages.create`, `generateContent`, `streamText`, `generateText`)
51
42
  2. For each file with call sites, read the actual source and review:
52
43
  - Is it a route handler / API endpoint?
53
44
  - What provider(s) does it use?
54
45
  - Does it call other files with LLM call sites? (delegation → multi-agent)
55
- 3. For **Advanced** tier, also identify:
46
+ 3. Identify:
56
47
  - Agent boundaries (each distinct orchestration unit = one agent)
57
48
  - Delegation patterns (parent calls child → `runAs`)
58
49
  - Feedback handlers (thumbs up/down UI components)
@@ -135,17 +126,10 @@ export const openai = new OpenAI({
135
126
  // export const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, amplitude: ai });
136
127
  ```
137
128
 
138
- **Alternative: `wrap()` for existing clients.** If the project creates provider clients dynamically:
139
-
140
- ```typescript
141
- import { wrap } from '@amplitude/ai';
142
- import OpenAI from 'openai';
143
- const rawClient = new OpenAI({ apiKey: process.env.OPENAI_API_KEY! });
144
- export const openai = wrap(rawClient, ai);
145
- ```
146
-
147
129
  Add `AMPLITUDE_AI_API_KEY` to `.env.example`. Check `.gitignore` includes `.env`.
148
130
 
131
+ > **Note:** If you cannot modify provider instantiation sites, use `wrap(existingClient, ai)` to instrument an existing client, or `patch({ amplitudeAI: ai })` for zero-code verification. These capture fewer event types — always prefer provider wrappers when possible.
132
+
149
133
  ### Step 3c: Swap provider imports
150
134
 
151
135
  Replace direct provider instantiation with imports from the bootstrap file:
@@ -163,7 +147,7 @@ import { openai as client } from '@/lib/amplitude';
163
147
 
164
148
  ### Step 3d: Add session context
165
149
 
166
- **Standard tier** — wrap route handlers with agent + session:
150
+ Wrap route handlers with agent + session:
167
151
 
168
152
  ```typescript
169
153
  import { ai } from '@/lib/amplitude';
@@ -183,7 +167,7 @@ export async function POST(req: Request) {
183
167
  }
184
168
  ```
185
169
 
186
- **Advanced tier** add multi-agent delegation with `runAs`:
170
+ If multi-agent signals were detected, add delegation with `runAs`:
187
171
 
188
172
  ```typescript
189
173
  const orchestrator = ai.agent('shopping-agent', { description: 'Orchestrates shopping requests' });
@@ -220,7 +204,7 @@ s.trackAiMessage(completedMessage.content, 'gpt-4o', 'openai', latencyMs, {
220
204
  });
221
205
  ```
222
206
 
223
- ### Step 3f: Add span tracking for orchestration (Advanced tier)
207
+ ### Step 3f: Add span tracking for orchestration
224
208
 
225
209
  When a parent agent delegates work to a child agent, wrap the delegation call with span tracking to measure latency and capture errors. Look for existing try/catch blocks around sub-agent execution — these are natural places to add span tracking with both success and error paths:
226
210
 
@@ -253,9 +237,9 @@ try {
253
237
  }
254
238
  ```
255
239
 
256
- ### Step 3g: Add scoring (Advanced tier only)
240
+ ### Step 3g: Add scoring
257
241
 
258
- If feedback handlers were detected (thumbs up/down UI), check whether the handler receives a `messageId` — if so, target the specific message for finer-grained scoring. Otherwise fall back to session-level scoring:
242
+ If feedback handlers were detected (thumbs up/down UI), add scoring. Check whether the handler receives a `messageId` — if so, target the specific message for finer-grained scoring. Otherwise fall back to session-level scoring:
259
243
 
260
244
  ```typescript
261
245
  const targetId = messageId ?? sessionId;
@@ -25,9 +25,10 @@ Paste this into your AI coding agent (Cursor, Claude Code, Copilot, etc.):
25
25
  Instrument this app with @amplitude/ai. Follow node_modules/@amplitude/ai/amplitude-ai.md
26
26
 
27
27
  CLI commands:
28
- mcp Start the MCP server (optional, for advanced tooling)
29
- doctor Validate environment, deps, and event pipeline
30
- status Show SDK version, installed providers, and env config
28
+ mcp Start the MCP server (optional, for advanced tooling)
29
+ doctor Validate environment, deps, and event pipeline
30
+ status Show SDK version, installed providers, and env config
31
+ --print-guide Print the full amplitude-ai.md instrumentation guide to stdout
31
32
  `
32
33
  );
33
34
  process.exit(0);
@@ -39,6 +40,17 @@ if (command === '--version' || command === '-v') {
39
40
  process.exit(0);
40
41
  }
41
42
 
43
+ if (command === '--print-guide') {
44
+ const guidePath = join(dirname(fileURLToPath(import.meta.url)), '..', 'amplitude-ai.md');
45
+ try {
46
+ process.stdout.write(readFileSync(guidePath, 'utf8'));
47
+ } catch {
48
+ process.stderr.write(`Guide not found at ${guidePath}\n`);
49
+ process.exit(1);
50
+ }
51
+ process.exit(0);
52
+ }
53
+
42
54
  if (command in commandToBin) {
43
55
  const binPath = fileURLToPath(new URL(commandToBin[command], import.meta.url));
44
56
  const result = spawnSync(process.execPath, [binPath, ...rest], {
@@ -48,5 +60,5 @@ if (command in commandToBin) {
48
60
  process.exit(result.status ?? 1);
49
61
  }
50
62
 
51
- process.stderr.write(`Unknown command: ${command}\nUsage: amplitude-ai <mcp|doctor|status>\n`);
63
+ process.stderr.write(`Unknown command: ${command}\nUsage: amplitude-ai <mcp|doctor|status|--print-guide>\n`);
52
64
  process.exit(1);
@@ -1 +1 @@
1
- {"version":3,"file":"bound-agent.d.ts","names":[],"sources":["../src/bound-agent.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"bound-agent.d.ts","names":[],"sources":["../src/bound-agent.ts"],"sourcesContent":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"privacy.d.ts","names":[],"sources":["../../src/core/privacy.ts"],"sourcesContent":[],"mappings":";cAaa,0BAAA;AAAA,cACA,4BAAA,GAD0B,oBAAA;AAC1B,cAIA,cAAA,GAJ4B,IAAA;AAI5B,cACA,UAAA,GADc,CAAA;AACd,iBAgBG,eAAA,CAhBO,IAAA,EAAA,MAAA,CAAA,EAAA,OAAA;AAgBP,iBAIA,UAAA,CAJe,IAAA,EAAA,MAAA,CAAA,EAAA,OAAA;AAIf,iBAYA,WAAA,CAZU,IAAA,EAAA,MAAA,CAAA,EAAA,OAAA;AAYV,iBAKA,iBAAA,CALW,OAAA,EAAA,OAAA,CAAA,EAAA,MAAA;AAKX,iBAMA,mBAAA,CANiB,KAAA,EAAA,OAAA,CAAA,EAAA,OAAA;AAMjB,iBAOA,iBAAA,CAPmB,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAOnC;AA8CA;AAIA;AAeA;AAgCA;AAkCA;;;;;;AAqEiB,iBA1JD,YAAA,CA0JqB,IAAA,EAAA,MAAA,CAAA,EA1JO,MA0JP,CAAA,MAAA,EAAA,OAAA,CAAA;AASxB,iBA/JG,qBAAA,CA+JU,UAAA,EA9JZ,MA8JY,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,EAAA,MAAA;AASH,iBAzJP,kBAAA,CAyJO,OAAA,EAAA,OAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,SAAA,CAAA,EAAA,OAAA,CAAA,EArJpB,MAqJoB,CAAA,MAAA,EAAA,OAAA,CAAA;AAyEc,iBAlMrB,yBAAA,CAkMqB,OAAA,EAAA,OAAA,EAAA,SAAA,EAAA,OAAA,CAAA,EAAA,OAAA;;;;;AA6EhC,iBA7OW,wBAAA,CA6OX,eAAA,EA5Oc,KA4Od,CA5OoB,MA4OpB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA,EA3OF,KA2OE,CA3OI,MA2OJ,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;AAAM,UAxKM,oBAAA,CAwKN;;;;;;;;cA/JE,aAAA;;;;;;;;wBASU;;;;qCAyEc;qDAwBgB;8FAwBhD;2CA4BgB,MAAM,8CACtB"}
1
+ {"version":3,"file":"privacy.d.ts","names":[],"sources":["../../src/core/privacy.ts"],"sourcesContent":[],"mappings":";cAaa,0BAAA;AAAA,cACA,4BAAA,GAD0B,oBAAA;AAC1B,cAIA,cAAA,GAJ4B,IAAA;AAI5B,cACA,UAAA,GADc,CAAA;AACd,iBAgBG,eAAA,CAhBO,IAAA,EAAA,MAAA,CAAA,EAAA,OAAA;AAgBP,iBAIA,UAAA,CAJe,IAAA,EAAA,MAAA,CAAA,EAAA,OAAA;AAIf,iBAYA,WAAA,CAZU,IAAA,EAAA,MAAA,CAAA,EAAA,OAAA;AAYV,iBAKA,iBAAA,CALW,OAAA,EAAA,OAAA,CAAA,EAAA,MAAA;AAKX,iBAMA,mBAAA,CANiB,KAAA,EAAA,OAAA,CAAA,EAAA,OAAA;AAMjB,iBAOA,iBAAA,CAPmB,IAAA,EAAA,MAAA,CAAA,EAAA,MAAA;AAOnC;AA8CA;AAIA;AAeA;AAqCA;AAkCA;;;;;;AAqEiB,iBA/JD,YAAA,CA+JqB,IAAA,EAAA,MAAA,CAAA,EA/JO,MA+JP,CAAA,MAAA,EAAA,OAAA,CAAA;AASxB,iBApKG,qBAAA,CAoKU,UAAA,EAnKZ,MAmKY,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,EAAA,MAAA;AASH,iBA9JP,kBAAA,CA8JO,OAAA,EAAA,OAAA,EAAA,WAAA,CAAA,EAAA,OAAA,EAAA,SAAA,CAAA,EAAA,OAAA,CAAA,EA1JpB,MA0JoB,CAAA,MAAA,EAAA,OAAA,CAAA;AAyEc,iBAlMrB,yBAAA,CAkMqB,OAAA,EAAA,OAAA,EAAA,SAAA,EAAA,OAAA,CAAA,EAAA,OAAA;;;;;AA6EhC,iBA7OW,wBAAA,CA6OX,eAAA,EA5Oc,KA4Od,CA5OoB,MA4OpB,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA,EA3OF,KA2OE,CA3OI,MA2OJ,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA;AAAM,UAxKM,oBAAA,CAwKN;;;;;;;;cA/JE,aAAA;;;;;;;;wBASU;;;;qCAyEc;qDAwBgB;8FAwBhD;2CA4BgB,MAAM,8CACtB"}
@@ -103,6 +103,7 @@ function sanitizeAnyContent(content, privacyMode = false, redactPii = true) {
103
103
  if (typeof content === "string") textContent = content;
104
104
  else if (typeof content === "object") textContent = extractTextFromStructuredContent(content);
105
105
  else textContent = String(content);
106
+ if (textContent.length === 0) return result;
106
107
  if (redactPii) {
107
108
  textContent = redactPiiPatterns(textContent);
108
109
  const redacted = redactBase64Content(textContent);
@@ -1 +1 @@
1
- {"version":3,"file":"privacy.js","names":["parts: string[]","result: Record<string, unknown>","textContent: string","sanitized: Record<string, unknown>","normalized: Array<Record<string, unknown>>","modeStr: string | null","sorted: Record<string, unknown>"],"sources":["../../src/core/privacy.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { getLogger } from '../utils/logger.js';\nimport {\n PROP_HAS_REASONING,\n PROP_REASONING_CONTENT,\n PROP_REASONING_TOKENS,\n PROP_SYSTEM_PROMPT,\n PROP_SYSTEM_PROMPT_LENGTH,\n PROP_TOOL_DEFINITIONS,\n PROP_TOOL_DEFINITIONS_COUNT,\n PROP_TOOL_DEFINITIONS_HASH,\n} from './constants.js';\n\nexport const REDACTED_IMAGE_PLACEHOLDER = '[base64 image redacted]';\nexport const REDACTED_CONTENT_PLACEHOLDER = '[content redacted]';\n\n// Legacy chunking constants — kept only so getTextFromLlmMessage() can\n// still read old chunked events. New events always use { text: content }.\nexport const MAX_CHUNK_SIZE = 1024;\nexport const MAX_CHUNKS = 8;\n\nconst VALID_CONTENT_MODES = new Set([\n 'full',\n 'metadata_only',\n 'customer_enriched',\n]);\n\n// PII regex patterns\nconst EMAIL_RE = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g;\nconst PHONE_RE = /\\b\\(?([0-9]{3})\\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})\\b/g;\nconst CREDIT_CARD_RE = /\\b(?:\\d{4}[-\\s]?){3}\\d{4}\\b/g;\nconst SSN_RE = /\\b\\d{3}-\\d{2}-\\d{4}\\b/g;\nconst BASE64_DATA_URL_RE = /^data:([^;]+);base64,/;\nconst RAW_BASE64_RE = /^[A-Za-z0-9+/]+=*$/;\n\nexport function isBase64DataUrl(text: string): boolean {\n return BASE64_DATA_URL_RE.test(text);\n}\n\nexport function isValidUrl(text: string): boolean {\n try {\n const result = new URL(text);\n return Boolean(result.protocol && result.hostname);\n } catch {\n // not a valid absolute URL\n }\n return (\n text.startsWith('/') || text.startsWith('./') || text.startsWith('../')\n );\n}\n\nexport function isRawBase64(text: string): boolean {\n if (isValidUrl(text)) return false;\n return text.length > 20 && RAW_BASE64_RE.test(text);\n}\n\nexport function createContentHash(content: unknown): string {\n if (content == null) return '';\n const contentStr = typeof content === 'string' ? content : String(content);\n return crypto.createHash('sha256').update(contentStr, 'utf8').digest('hex');\n}\n\nexport function redactBase64Content(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n if (isBase64DataUrl(value)) return REDACTED_IMAGE_PLACEHOLDER;\n if (isRawBase64(value)) return REDACTED_IMAGE_PLACEHOLDER;\n return value;\n}\n\nexport function redactPiiPatterns(text: string): string {\n let result = text;\n result = result.replace(EMAIL_RE, '[email]');\n result = result.replace(PHONE_RE, '[phone]');\n result = result.replace(CREDIT_CARD_RE, '[credit_card]');\n result = result.replace(SSN_RE, '[ssn]');\n return result;\n}\n\nfunction extractTextFromStructuredContent(content: unknown): string {\n if (content == null) return '';\n if (typeof content === 'string') return content;\n\n if (typeof content === 'object' && !Array.isArray(content)) {\n const dict = content as Record<string, unknown>;\n for (const field of ['content', 'text', 'message']) {\n if (field in dict) return extractTextFromStructuredContent(dict[field]);\n }\n return String(content);\n }\n\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const item of content) {\n if (typeof item === 'string') parts.push(item);\n else if (typeof item === 'object')\n parts.push(extractTextFromStructuredContent(item));\n else parts.push(String(item));\n }\n return parts.join('');\n }\n\n return String(content);\n}\n\n/**\n * Return the `$llm_message` payload for the given text.\n *\n * Content is stored as `{ text: content }` at full length — the Node SDK\n * does not truncate string properties, and Nova already whitelists\n * `$llm_message` server-side.\n *\n * Previous versions split long content into `c0`..`c7` chunks. That format\n * is still readable via {@link getTextFromLlmMessage} for backward\n * compatibility, but is no longer produced.\n */\nexport function chunkContent(text: string): Record<string, unknown> {\n return { text };\n}\n\nexport function getTextFromLlmMessage(\n llmMessage: Record<string, unknown>,\n): string {\n if ('text' in llmMessage) return String(llmMessage.text);\n const n = llmMessage.n;\n if (typeof n === 'number' && n > 0) {\n const parts: string[] = [];\n for (let i = 0; i < n; i++) {\n parts.push(String(llmMessage[`c${i}`] ?? ''));\n }\n return parts.join('');\n }\n return '';\n}\n\nexport function sanitizeAnyContent(\n content: unknown,\n privacyMode = false,\n redactPii = true,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n if (content == null) return result;\n\n let textContent: string;\n if (typeof content === 'string') {\n textContent = content;\n } else if (typeof content === 'object') {\n textContent = extractTextFromStructuredContent(content);\n } else {\n textContent = String(content);\n }\n\n if (redactPii) {\n textContent = redactPiiPatterns(textContent);\n const redacted = redactBase64Content(textContent);\n if (typeof redacted === 'string') textContent = redacted;\n }\n\n if (privacyMode) {\n result.content_hash = createContentHash(textContent);\n } else {\n result.$llm_message = chunkContent(textContent);\n }\n\n return result;\n}\n\nexport function sanitizeStructuredContent(\n content: unknown,\n redactPii: boolean,\n): unknown {\n if (typeof content === 'string') {\n let text = content;\n if (redactPii) text = redactPiiPatterns(text);\n return redactBase64Content(text);\n }\n\n if (\n content != null &&\n typeof content === 'object' &&\n !Array.isArray(content)\n ) {\n const dict = content as Record<string, unknown>;\n const sanitized: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(dict)) {\n sanitized[key] = sanitizeStructuredContent(value, redactPii);\n }\n return sanitized;\n }\n\n if (Array.isArray(content)) {\n return content.map((item) => sanitizeStructuredContent(item, redactPii));\n }\n\n return content;\n}\n\n/**\n * Normalize tool definitions from various provider formats into a canonical shape:\n * `[{ name, description, parameters }]`.\n */\nexport function normalizeToolDefinitions(\n toolDefinitions: Array<Record<string, unknown>>,\n): Array<Record<string, unknown>> {\n const normalized: Array<Record<string, unknown>> = [];\n for (const tool of toolDefinitions) {\n if (tool == null || typeof tool !== 'object') continue;\n\n // OpenAI Chat format: { type: \"function\", function: { name, description, parameters } }\n const fn = tool.function;\n if (fn != null && typeof fn === 'object') {\n const f = fn as Record<string, unknown>;\n normalized.push({\n name: f.name ?? '',\n description: f.description ?? '',\n parameters: f.parameters ?? null,\n });\n continue;\n }\n\n // Anthropic format: { name, description, input_schema }\n if ('input_schema' in tool) {\n normalized.push({\n name: tool.name ?? '',\n description: tool.description ?? '',\n parameters: tool.input_schema ?? null,\n });\n continue;\n }\n\n // Bedrock format: { toolSpec: { name, description, inputSchema } }\n const toolSpec = tool.toolSpec;\n if (toolSpec != null && typeof toolSpec === 'object') {\n const ts = toolSpec as Record<string, unknown>;\n normalized.push({\n name: ts.name ?? '',\n description: ts.description ?? '',\n parameters: ts.inputSchema ?? null,\n });\n continue;\n }\n\n // Gemini format: { function_declarations: [{ name, description, parameters }] }\n const fnDecls = tool.function_declarations;\n if (Array.isArray(fnDecls)) {\n for (const decl of fnDecls) {\n if (decl != null && typeof decl === 'object') {\n const d = decl as Record<string, unknown>;\n normalized.push({\n name: d.name ?? '',\n description: d.description ?? '',\n parameters: d.parameters ?? null,\n });\n }\n }\n continue;\n }\n\n // Generic / OpenAI Responses format: { name, description, parameters }\n if ('name' in tool) {\n normalized.push({\n name: tool.name ?? '',\n description: tool.description ?? '',\n parameters: tool.parameters ?? null,\n });\n }\n }\n return normalized;\n}\n\nexport interface PrivacyConfigOptions {\n privacyMode?: boolean;\n redactPii?: boolean;\n customRedactionPatterns?: string[];\n contentMode?: string | null;\n validate?: boolean;\n debug?: boolean;\n}\n\nexport class PrivacyConfig {\n readonly privacyMode: boolean;\n readonly redactPii: boolean;\n readonly validate: boolean;\n readonly debug: boolean;\n readonly customPatterns: string[];\n private readonly _compiledCustomPatterns: RegExp[];\n private readonly _contentMode: string | null;\n\n constructor(options: PrivacyConfigOptions = {}) {\n this.privacyMode = options.privacyMode ?? false;\n this.redactPii = options.redactPii ?? false;\n this.validate = options.validate ?? false;\n this.debug = options.debug ?? false;\n this.customPatterns = options.customRedactionPatterns ?? [];\n this._compiledCustomPatterns = [];\n\n for (const pattern of this.customPatterns) {\n try {\n this._compiledCustomPatterns.push(new RegExp(pattern, 'g'));\n } catch (e) {\n getLogger().warn(\n `Invalid custom redaction regex \"${pattern}\": ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n\n let modeStr: string | null = null;\n if (options.contentMode != null) {\n modeStr = String(options.contentMode);\n if (!VALID_CONTENT_MODES.has(modeStr)) {\n throw new Error(\n `Invalid content_mode \"${options.contentMode}\". ` +\n `Must be one of: ${[...VALID_CONTENT_MODES].sort().join(', ')}`,\n );\n }\n }\n this._contentMode = modeStr;\n }\n\n get contentMode(): string | null {\n return this._contentMode;\n }\n\n private _applyCustomPatterns(text: string): string {\n if (!this._compiledCustomPatterns.length || typeof text !== 'string') {\n return text;\n }\n let result = text;\n for (const pattern of this._compiledCustomPatterns) {\n try {\n result = result.replace(pattern, '[REDACTED]');\n } catch (e) {\n getLogger().warn(\n `Invalid custom redaction regex \"${pattern.source}\": ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n return result;\n }\n\n private _applyCustomPatternsToLlmMessage(\n llmMessage: Record<string, unknown>,\n ): void {\n if ('text' in llmMessage) {\n llmMessage.text = this._applyCustomPatterns(String(llmMessage.text));\n return;\n }\n\n const n = llmMessage.n;\n if (typeof n === 'number' && n > 0) {\n for (let i = 0; i < n; i++) {\n const key = `c${i}`;\n if (key in llmMessage) {\n llmMessage[key] = this._applyCustomPatterns(\n String(llmMessage[key] ?? ''),\n );\n }\n }\n }\n }\n\n sanitizeContent(content: unknown): Record<string, unknown> {\n if (this._contentMode == null) {\n if (this.privacyMode) return {};\n const result = sanitizeAnyContent(content, false, this.redactPii);\n if (this.customPatterns.length && '$llm_message' in result) {\n const msg = result.$llm_message as Record<string, unknown>;\n this._applyCustomPatternsToLlmMessage(msg);\n }\n return result;\n }\n\n if (this._contentMode === 'full') {\n const result = sanitizeAnyContent(content, false, this.redactPii);\n if (this.customPatterns.length && '$llm_message' in result) {\n const msg = result.$llm_message as Record<string, unknown>;\n this._applyCustomPatternsToLlmMessage(msg);\n }\n return result;\n }\n\n // metadata_only, customer_enriched → no content\n return {};\n }\n\n sanitizeSystemPrompt(systemPrompt: string | null): Record<string, unknown> {\n if (!systemPrompt) return {};\n\n const result: Record<string, unknown> = {\n [PROP_SYSTEM_PROMPT_LENGTH]: systemPrompt.length,\n };\n\n let mode = this._contentMode;\n if (mode == null) mode = this.privacyMode ? 'metadata_only' : 'full';\n\n if (mode === 'full') {\n let sanitized = systemPrompt;\n if (this.redactPii) sanitized = redactPiiPatterns(sanitized);\n sanitized = this._applyCustomPatterns(sanitized);\n result[PROP_SYSTEM_PROMPT] =\n sanitized.length > 10000 ? sanitized.slice(0, 10000) : sanitized;\n }\n\n return result;\n }\n\n sanitizeReasoningContent(\n reasoningContent: string | null,\n reasoningTokens?: number | null,\n ): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n const hasReasoning =\n Boolean(reasoningContent) ||\n (reasoningTokens != null && reasoningTokens > 0);\n if (!hasReasoning && reasoningTokens == null) return result;\n\n if (hasReasoning) result[PROP_HAS_REASONING] = true;\n if (reasoningTokens != null)\n result[PROP_REASONING_TOKENS] = reasoningTokens;\n if (!hasReasoning || reasoningContent == null) return result;\n\n let mode = this._contentMode;\n if (mode == null) mode = this.privacyMode ? 'metadata_only' : 'full';\n\n if (mode === 'full') {\n let sanitized = reasoningContent;\n if (this.redactPii) sanitized = redactPiiPatterns(sanitized);\n sanitized = this._applyCustomPatterns(sanitized);\n result[PROP_REASONING_CONTENT] =\n sanitized.length > 10000 ? sanitized.slice(0, 10000) : sanitized;\n }\n\n return result;\n }\n\n sanitizeToolDefinitions(\n toolDefinitions: Array<Record<string, unknown>> | null | undefined,\n ): Record<string, unknown> {\n if (!toolDefinitions?.length) return {};\n\n const normalized = normalizeToolDefinitions(toolDefinitions);\n const result: Record<string, unknown> = {\n [PROP_TOOL_DEFINITIONS_COUNT]: normalized.length,\n };\n\n const canonicalSorted = JSON.stringify(\n normalized.map((t) => {\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(t).sort()) sorted[key] = t[key];\n return sorted;\n }),\n );\n result[PROP_TOOL_DEFINITIONS_HASH] = crypto\n .createHash('sha256')\n .update(canonicalSorted)\n .digest('hex')\n .slice(0, 16);\n\n let mode = this._contentMode;\n if (mode == null) mode = this.privacyMode ? 'metadata_only' : 'full';\n\n if (mode === 'full') {\n let serialized = JSON.stringify(normalized);\n if (this.redactPii) serialized = redactPiiPatterns(serialized);\n serialized = this._applyCustomPatterns(serialized);\n result[PROP_TOOL_DEFINITIONS] =\n serialized.length > 10000 ? serialized.slice(0, 10000) : serialized;\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"privacy.js","names":["parts: string[]","result: Record<string, unknown>","textContent: string","sanitized: Record<string, unknown>","normalized: Array<Record<string, unknown>>","modeStr: string | null","sorted: Record<string, unknown>"],"sources":["../../src/core/privacy.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport { getLogger } from '../utils/logger.js';\nimport {\n PROP_HAS_REASONING,\n PROP_REASONING_CONTENT,\n PROP_REASONING_TOKENS,\n PROP_SYSTEM_PROMPT,\n PROP_SYSTEM_PROMPT_LENGTH,\n PROP_TOOL_DEFINITIONS,\n PROP_TOOL_DEFINITIONS_COUNT,\n PROP_TOOL_DEFINITIONS_HASH,\n} from './constants.js';\n\nexport const REDACTED_IMAGE_PLACEHOLDER = '[base64 image redacted]';\nexport const REDACTED_CONTENT_PLACEHOLDER = '[content redacted]';\n\n// Legacy chunking constants — kept only so getTextFromLlmMessage() can\n// still read old chunked events. New events always use { text: content }.\nexport const MAX_CHUNK_SIZE = 1024;\nexport const MAX_CHUNKS = 8;\n\nconst VALID_CONTENT_MODES = new Set([\n 'full',\n 'metadata_only',\n 'customer_enriched',\n]);\n\n// PII regex patterns\nconst EMAIL_RE = /\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b/g;\nconst PHONE_RE = /\\b\\(?([0-9]{3})\\)?[-. ]?([0-9]{3})[-. ]?([0-9]{4})\\b/g;\nconst CREDIT_CARD_RE = /\\b(?:\\d{4}[-\\s]?){3}\\d{4}\\b/g;\nconst SSN_RE = /\\b\\d{3}-\\d{2}-\\d{4}\\b/g;\nconst BASE64_DATA_URL_RE = /^data:([^;]+);base64,/;\nconst RAW_BASE64_RE = /^[A-Za-z0-9+/]+=*$/;\n\nexport function isBase64DataUrl(text: string): boolean {\n return BASE64_DATA_URL_RE.test(text);\n}\n\nexport function isValidUrl(text: string): boolean {\n try {\n const result = new URL(text);\n return Boolean(result.protocol && result.hostname);\n } catch {\n // not a valid absolute URL\n }\n return (\n text.startsWith('/') || text.startsWith('./') || text.startsWith('../')\n );\n}\n\nexport function isRawBase64(text: string): boolean {\n if (isValidUrl(text)) return false;\n return text.length > 20 && RAW_BASE64_RE.test(text);\n}\n\nexport function createContentHash(content: unknown): string {\n if (content == null) return '';\n const contentStr = typeof content === 'string' ? content : String(content);\n return crypto.createHash('sha256').update(contentStr, 'utf8').digest('hex');\n}\n\nexport function redactBase64Content(value: unknown): unknown {\n if (typeof value !== 'string') return value;\n if (isBase64DataUrl(value)) return REDACTED_IMAGE_PLACEHOLDER;\n if (isRawBase64(value)) return REDACTED_IMAGE_PLACEHOLDER;\n return value;\n}\n\nexport function redactPiiPatterns(text: string): string {\n let result = text;\n result = result.replace(EMAIL_RE, '[email]');\n result = result.replace(PHONE_RE, '[phone]');\n result = result.replace(CREDIT_CARD_RE, '[credit_card]');\n result = result.replace(SSN_RE, '[ssn]');\n return result;\n}\n\nfunction extractTextFromStructuredContent(content: unknown): string {\n if (content == null) return '';\n if (typeof content === 'string') return content;\n\n if (typeof content === 'object' && !Array.isArray(content)) {\n const dict = content as Record<string, unknown>;\n for (const field of ['content', 'text', 'message']) {\n if (field in dict) return extractTextFromStructuredContent(dict[field]);\n }\n return String(content);\n }\n\n if (Array.isArray(content)) {\n const parts: string[] = [];\n for (const item of content) {\n if (typeof item === 'string') parts.push(item);\n else if (typeof item === 'object')\n parts.push(extractTextFromStructuredContent(item));\n else parts.push(String(item));\n }\n return parts.join('');\n }\n\n return String(content);\n}\n\n/**\n * Return the `$llm_message` payload for the given text.\n *\n * Content is stored as `{ text: content }` at full length — the Node SDK\n * does not truncate string properties, and Nova already whitelists\n * `$llm_message` server-side.\n *\n * Previous versions split long content into `c0`..`c7` chunks. That format\n * is still readable via {@link getTextFromLlmMessage} for backward\n * compatibility, but is no longer produced.\n */\nexport function chunkContent(text: string): Record<string, unknown> {\n return { text };\n}\n\nexport function getTextFromLlmMessage(\n llmMessage: Record<string, unknown>,\n): string {\n if ('text' in llmMessage) return String(llmMessage.text);\n const n = llmMessage.n;\n if (typeof n === 'number' && n > 0) {\n const parts: string[] = [];\n for (let i = 0; i < n; i++) {\n parts.push(String(llmMessage[`c${i}`] ?? ''));\n }\n return parts.join('');\n }\n return '';\n}\n\nexport function sanitizeAnyContent(\n content: unknown,\n privacyMode = false,\n redactPii = true,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n if (content == null) return result;\n\n let textContent: string;\n if (typeof content === 'string') {\n textContent = content;\n } else if (typeof content === 'object') {\n textContent = extractTextFromStructuredContent(content);\n } else {\n textContent = String(content);\n }\n\n // Tool-call-only LLM responses have content=null which the provider\n // coerces to ''. Emitting $llm_message with an empty string causes\n // the response to appear as \"missing text\" in the thread view.\n if (textContent.length === 0) return result;\n\n if (redactPii) {\n textContent = redactPiiPatterns(textContent);\n const redacted = redactBase64Content(textContent);\n if (typeof redacted === 'string') textContent = redacted;\n }\n\n if (privacyMode) {\n result.content_hash = createContentHash(textContent);\n } else {\n result.$llm_message = chunkContent(textContent);\n }\n\n return result;\n}\n\nexport function sanitizeStructuredContent(\n content: unknown,\n redactPii: boolean,\n): unknown {\n if (typeof content === 'string') {\n let text = content;\n if (redactPii) text = redactPiiPatterns(text);\n return redactBase64Content(text);\n }\n\n if (\n content != null &&\n typeof content === 'object' &&\n !Array.isArray(content)\n ) {\n const dict = content as Record<string, unknown>;\n const sanitized: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(dict)) {\n sanitized[key] = sanitizeStructuredContent(value, redactPii);\n }\n return sanitized;\n }\n\n if (Array.isArray(content)) {\n return content.map((item) => sanitizeStructuredContent(item, redactPii));\n }\n\n return content;\n}\n\n/**\n * Normalize tool definitions from various provider formats into a canonical shape:\n * `[{ name, description, parameters }]`.\n */\nexport function normalizeToolDefinitions(\n toolDefinitions: Array<Record<string, unknown>>,\n): Array<Record<string, unknown>> {\n const normalized: Array<Record<string, unknown>> = [];\n for (const tool of toolDefinitions) {\n if (tool == null || typeof tool !== 'object') continue;\n\n // OpenAI Chat format: { type: \"function\", function: { name, description, parameters } }\n const fn = tool.function;\n if (fn != null && typeof fn === 'object') {\n const f = fn as Record<string, unknown>;\n normalized.push({\n name: f.name ?? '',\n description: f.description ?? '',\n parameters: f.parameters ?? null,\n });\n continue;\n }\n\n // Anthropic format: { name, description, input_schema }\n if ('input_schema' in tool) {\n normalized.push({\n name: tool.name ?? '',\n description: tool.description ?? '',\n parameters: tool.input_schema ?? null,\n });\n continue;\n }\n\n // Bedrock format: { toolSpec: { name, description, inputSchema } }\n const toolSpec = tool.toolSpec;\n if (toolSpec != null && typeof toolSpec === 'object') {\n const ts = toolSpec as Record<string, unknown>;\n normalized.push({\n name: ts.name ?? '',\n description: ts.description ?? '',\n parameters: ts.inputSchema ?? null,\n });\n continue;\n }\n\n // Gemini format: { function_declarations: [{ name, description, parameters }] }\n const fnDecls = tool.function_declarations;\n if (Array.isArray(fnDecls)) {\n for (const decl of fnDecls) {\n if (decl != null && typeof decl === 'object') {\n const d = decl as Record<string, unknown>;\n normalized.push({\n name: d.name ?? '',\n description: d.description ?? '',\n parameters: d.parameters ?? null,\n });\n }\n }\n continue;\n }\n\n // Generic / OpenAI Responses format: { name, description, parameters }\n if ('name' in tool) {\n normalized.push({\n name: tool.name ?? '',\n description: tool.description ?? '',\n parameters: tool.parameters ?? null,\n });\n }\n }\n return normalized;\n}\n\nexport interface PrivacyConfigOptions {\n privacyMode?: boolean;\n redactPii?: boolean;\n customRedactionPatterns?: string[];\n contentMode?: string | null;\n validate?: boolean;\n debug?: boolean;\n}\n\nexport class PrivacyConfig {\n readonly privacyMode: boolean;\n readonly redactPii: boolean;\n readonly validate: boolean;\n readonly debug: boolean;\n readonly customPatterns: string[];\n private readonly _compiledCustomPatterns: RegExp[];\n private readonly _contentMode: string | null;\n\n constructor(options: PrivacyConfigOptions = {}) {\n this.privacyMode = options.privacyMode ?? false;\n this.redactPii = options.redactPii ?? false;\n this.validate = options.validate ?? false;\n this.debug = options.debug ?? false;\n this.customPatterns = options.customRedactionPatterns ?? [];\n this._compiledCustomPatterns = [];\n\n for (const pattern of this.customPatterns) {\n try {\n this._compiledCustomPatterns.push(new RegExp(pattern, 'g'));\n } catch (e) {\n getLogger().warn(\n `Invalid custom redaction regex \"${pattern}\": ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n\n let modeStr: string | null = null;\n if (options.contentMode != null) {\n modeStr = String(options.contentMode);\n if (!VALID_CONTENT_MODES.has(modeStr)) {\n throw new Error(\n `Invalid content_mode \"${options.contentMode}\". ` +\n `Must be one of: ${[...VALID_CONTENT_MODES].sort().join(', ')}`,\n );\n }\n }\n this._contentMode = modeStr;\n }\n\n get contentMode(): string | null {\n return this._contentMode;\n }\n\n private _applyCustomPatterns(text: string): string {\n if (!this._compiledCustomPatterns.length || typeof text !== 'string') {\n return text;\n }\n let result = text;\n for (const pattern of this._compiledCustomPatterns) {\n try {\n result = result.replace(pattern, '[REDACTED]');\n } catch (e) {\n getLogger().warn(\n `Invalid custom redaction regex \"${pattern.source}\": ${e instanceof Error ? e.message : String(e)}`,\n );\n }\n }\n return result;\n }\n\n private _applyCustomPatternsToLlmMessage(\n llmMessage: Record<string, unknown>,\n ): void {\n if ('text' in llmMessage) {\n llmMessage.text = this._applyCustomPatterns(String(llmMessage.text));\n return;\n }\n\n const n = llmMessage.n;\n if (typeof n === 'number' && n > 0) {\n for (let i = 0; i < n; i++) {\n const key = `c${i}`;\n if (key in llmMessage) {\n llmMessage[key] = this._applyCustomPatterns(\n String(llmMessage[key] ?? ''),\n );\n }\n }\n }\n }\n\n sanitizeContent(content: unknown): Record<string, unknown> {\n if (this._contentMode == null) {\n if (this.privacyMode) return {};\n const result = sanitizeAnyContent(content, false, this.redactPii);\n if (this.customPatterns.length && '$llm_message' in result) {\n const msg = result.$llm_message as Record<string, unknown>;\n this._applyCustomPatternsToLlmMessage(msg);\n }\n return result;\n }\n\n if (this._contentMode === 'full') {\n const result = sanitizeAnyContent(content, false, this.redactPii);\n if (this.customPatterns.length && '$llm_message' in result) {\n const msg = result.$llm_message as Record<string, unknown>;\n this._applyCustomPatternsToLlmMessage(msg);\n }\n return result;\n }\n\n // metadata_only, customer_enriched → no content\n return {};\n }\n\n sanitizeSystemPrompt(systemPrompt: string | null): Record<string, unknown> {\n if (!systemPrompt) return {};\n\n const result: Record<string, unknown> = {\n [PROP_SYSTEM_PROMPT_LENGTH]: systemPrompt.length,\n };\n\n let mode = this._contentMode;\n if (mode == null) mode = this.privacyMode ? 'metadata_only' : 'full';\n\n if (mode === 'full') {\n let sanitized = systemPrompt;\n if (this.redactPii) sanitized = redactPiiPatterns(sanitized);\n sanitized = this._applyCustomPatterns(sanitized);\n result[PROP_SYSTEM_PROMPT] =\n sanitized.length > 10000 ? sanitized.slice(0, 10000) : sanitized;\n }\n\n return result;\n }\n\n sanitizeReasoningContent(\n reasoningContent: string | null,\n reasoningTokens?: number | null,\n ): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n const hasReasoning =\n Boolean(reasoningContent) ||\n (reasoningTokens != null && reasoningTokens > 0);\n if (!hasReasoning && reasoningTokens == null) return result;\n\n if (hasReasoning) result[PROP_HAS_REASONING] = true;\n if (reasoningTokens != null)\n result[PROP_REASONING_TOKENS] = reasoningTokens;\n if (!hasReasoning || reasoningContent == null) return result;\n\n let mode = this._contentMode;\n if (mode == null) mode = this.privacyMode ? 'metadata_only' : 'full';\n\n if (mode === 'full') {\n let sanitized = reasoningContent;\n if (this.redactPii) sanitized = redactPiiPatterns(sanitized);\n sanitized = this._applyCustomPatterns(sanitized);\n result[PROP_REASONING_CONTENT] =\n sanitized.length > 10000 ? sanitized.slice(0, 10000) : sanitized;\n }\n\n return result;\n }\n\n sanitizeToolDefinitions(\n toolDefinitions: Array<Record<string, unknown>> | null | undefined,\n ): Record<string, unknown> {\n if (!toolDefinitions?.length) return {};\n\n const normalized = normalizeToolDefinitions(toolDefinitions);\n const result: Record<string, unknown> = {\n [PROP_TOOL_DEFINITIONS_COUNT]: normalized.length,\n };\n\n const canonicalSorted = JSON.stringify(\n normalized.map((t) => {\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(t).sort()) sorted[key] = t[key];\n return sorted;\n }),\n );\n result[PROP_TOOL_DEFINITIONS_HASH] = crypto\n .createHash('sha256')\n .update(canonicalSorted)\n .digest('hex')\n .slice(0, 16);\n\n let mode = this._contentMode;\n if (mode == null) mode = this.privacyMode ? 'metadata_only' : 'full';\n\n if (mode === 'full') {\n let serialized = JSON.stringify(normalized);\n if (this.redactPii) serialized = redactPiiPatterns(serialized);\n serialized = this._applyCustomPatterns(serialized);\n result[PROP_TOOL_DEFINITIONS] =\n serialized.length > 10000 ? serialized.slice(0, 10000) : serialized;\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","names":[],"sources":["../../src/providers/anthropic.ts"],"sourcesContent":[],"mappings":";;;;;;AAyBM,cADO,mBACW,EAAM,OAAA;AAI9B,cAJM,gBAI2B,EAJT,MAKX,CAAA,MAAA,EAAA,OAEK,CAAA,GAAA,IAAA;AAOA,UAVD,gBAAA,CAUC;EAA0B,SAAA,EAT/B,aAS+B;EAGvB,MAAA,CAAA,EAAA,MAAA;EAGE,aAAA,CAAA,EAbL,aAaK,GAAA,IAAA;EAmCP,gBAAA,CAAA,EAAA,OAAA;EAxCN;EAAc,eAAA,CAAA,EAAA,OAAA;AA6CxB;AACa,cAhDA,SAgDA,CAAA,gBA/CK,MA+CL,CAAA,MAAA,EAAA,OAAA,CAAA,GA/C+B,MA+C/B,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,SA9CH,cAAA,CA8CG;EAQA,QAAA,OAAA;EACE,SAAA,QAAA,EArDM,eAqDN;EACI,QAAA,iBAAA;EAeP,WAAA,CAAA,OAAA,EAlEW,gBAkEX;EACa,IAAA,MAAA,CAAA,CAAA,EAhCT,OAgCS;;AACQ,cA5BpB,eAAA,CA4BoB;EAA5B,SAAA,EA3BQ,MA2BR,CAAA,MAAA,EAAA,OAAA,CAAA;EAAO,QAAA,QAAA;EAySI,QAAA,UAAA;EAmBA,QAAA,cAAA;EACC,QAAA,iBAAA;EAAN,WAAA,CAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAhVE,OAgVF,EAAA,SAAA,EA/UI,aA+UJ,EAAA,aAAA,EA9UQ,aA8UR,GAAA,IAAA,EAAA,gBAAA,EAAA,OAAA;EAIQ,MAAA,CAAA,MAAA,EAnUP,MAmUO,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,kBAAA,CAAA,EAlUM,oBAkUN,CAAA,EAjUd,OAiUc,CAjUN,iBAiUM,GAjUc,aAiUd,CAAA,OAAA,CAAA,CAAA;EAAN,QAAA,WAAA;EAAK,QAAA,mBAAA;;;iBAxBF,4BAAA;iBAmBA,uBAAA,UACL,MAAM;;;aAIJ,MAAM"}
1
+ {"version":3,"file":"anthropic.d.ts","names":[],"sources":["../../src/providers/anthropic.ts"],"sourcesContent":[],"mappings":";;;;;;AAyBM,cADO,mBACW,EAAM,OAAA;AAI9B,cAJM,gBAI2B,EAJT,MAKX,CAAA,MAAA,EAAA,OAEK,CAAA,GAAA,IAAA;AAOA,UAVD,gBAAA,CAUC;EAA0B,SAAA,EAT/B,aAS+B;EAGvB,MAAA,CAAA,EAAA,MAAA;EAGE,aAAA,CAAA,EAbL,aAaK,GAAA,IAAA;EAmCP,gBAAA,CAAA,EAAA,OAAA;EAxCN;EAAc,eAAA,CAAA,EAAA,OAAA;AA6CxB;AACa,cAhDA,SAgDA,CAAA,gBA/CK,MA+CL,CAAA,MAAA,EAAA,OAAA,CAAA,GA/C+B,MA+C/B,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,SA9CH,cAAA,CA8CG;EAQA,QAAA,OAAA;EACE,SAAA,QAAA,EArDM,eAqDN;EACI,QAAA,iBAAA;EAeP,WAAA,CAAA,OAAA,EAlEW,gBAkEX;EACa,IAAA,MAAA,CAAA,CAAA,EAhCT,OAgCS;;AACQ,cA5BpB,eAAA,CA4BoB;EAA5B,SAAA,EA3BQ,MA2BR,CAAA,MAAA,EAAA,OAAA,CAAA;EAAO,QAAA,QAAA;EAkTI,QAAA,UAAA;EAmBA,QAAA,cAAA;EACC,QAAA,iBAAA;EAAN,WAAA,CAAA,MAAA,EAAA,OAAA,EAAA,OAAA,EAzVE,OAyVF,EAAA,SAAA,EAxVI,aAwVJ,EAAA,aAAA,EAvVQ,aAuVR,GAAA,IAAA,EAAA,gBAAA,EAAA,OAAA;EAIQ,MAAA,CAAA,MAAA,EA5UP,MA4UO,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,kBAAA,CAAA,EA3UM,oBA2UN,CAAA,EA1Ud,OA0Uc,CA1UN,iBA0UM,GA1Uc,aA0Ud,CAAA,OAAA,CAAA,CAAA;EAAN,QAAA,WAAA;EAAK,QAAA,mBAAA;;;iBAxBF,4BAAA;iBAmBA,uBAAA,UACL,MAAM;;;aAIJ,MAAM"}
@@ -209,7 +209,9 @@ var WrappedMessages = class {
209
209
  if (!shouldTrackInputMessages) return;
210
210
  if (ctx.userId == null || ctx.sessionId == null) return;
211
211
  if (!Array.isArray(messages)) return;
212
- for (const msg of messages) {
212
+ const lastReplyIdx = messages.findLastIndex((m) => m?.role === "assistant");
213
+ const newMessages = messages.slice(lastReplyIdx + 1);
214
+ for (const msg of newMessages) {
213
215
  if (msg?.role !== "user") continue;
214
216
  const rawContent = msg?.content;
215
217
  const content = Array.isArray(rawContent) ? rawContent.map((part) => {
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.js","names":["_AnthropicModule: Record<string, unknown> | null","clientOpts: Record<string, unknown>","costUsd: number | null","reasoning: string | undefined","toolCalls: Array<Record<string, unknown>>"],"sources":["../../src/providers/anthropic.ts"],"sourcesContent":["/**\n * Anthropic provider wrapper with automatic tracking.\n */\n\nimport type { PrivacyConfig } from '../core/privacy.js';\nimport { trackUserMessage } from '../core/tracking.js';\nimport { getDefaultPropagateContext, injectContext } from '../propagation.js';\nimport type {\n AmplitudeLike,\n AmplitudeOrAI,\n AnthropicResponse,\n TrackFn,\n} from '../types.js';\nimport { calculateCost } from '../utils/costs.js';\nimport { tryRequire } from '../utils/resolve-module.js';\nimport { StreamingAccumulator } from '../utils/streaming.js';\nimport {\n applySessionContext,\n BaseAIProvider,\n contextFields,\n type ProviderTrackOptions,\n} from './base.js';\n\nconst _resolved = tryRequire('@anthropic-ai/sdk');\nexport const ANTHROPIC_AVAILABLE = _resolved != null;\nconst _AnthropicModule: Record<string, unknown> | null = _resolved;\n\nexport { _AnthropicModule };\n\nexport interface AnthropicOptions {\n amplitude: AmplitudeOrAI;\n apiKey?: string;\n privacyConfig?: PrivacyConfig | null;\n propagateContext?: boolean;\n /** Pass the `@anthropic-ai/sdk` module directly to bypass `tryRequire` (required in bundler environments). */\n anthropicModule?: unknown;\n}\n\nexport class Anthropic<\n TClient extends Record<string, unknown> = Record<string, unknown>,\n> extends BaseAIProvider {\n private _client: TClient;\n readonly messages: WrappedMessages;\n private _propagateContext: boolean;\n\n constructor(options: AnthropicOptions) {\n super({\n amplitude: options.amplitude,\n privacyConfig: options.privacyConfig,\n providerName: 'anthropic',\n });\n\n const mod =\n (options.anthropicModule as Record<string, unknown> | null) ??\n _AnthropicModule;\n if (mod == null) {\n throw new Error(\n '@anthropic-ai/sdk package is required. Install it with: npm install @anthropic-ai/sdk — or pass the module directly via the anthropicModule option.',\n );\n }\n\n const AnthropicSDK = mod.Anthropic as new (\n opts: Record<string, unknown>,\n ) => unknown;\n\n const clientOpts: Record<string, unknown> = {};\n if (options.apiKey) clientOpts.apiKey = options.apiKey;\n\n this._client = new AnthropicSDK(clientOpts) as TClient;\n this._propagateContext =\n options.propagateContext ?? getDefaultPropagateContext();\n this.messages = new WrappedMessages(\n this._client,\n this.trackFn(),\n this._amplitude,\n this._privacyConfig,\n this._propagateContext,\n );\n }\n\n get client(): TClient {\n return this._client;\n }\n}\n\nexport class WrappedMessages {\n _original: Record<string, unknown>;\n private _trackFn: TrackFn;\n private _amplitude: AmplitudeLike;\n private _privacyConfig: PrivacyConfig | null;\n private _propagateContext: boolean;\n\n constructor(\n client: unknown,\n trackFn: TrackFn,\n amplitude: AmplitudeLike,\n privacyConfig: PrivacyConfig | null,\n propagateContext: boolean,\n ) {\n const clientObj = client as Record<string, unknown>;\n this._original = clientObj.messages as Record<string, unknown>;\n this._trackFn =\n typeof trackFn === 'function'\n ? trackFn\n : (trackFn as unknown as { trackFn(): TrackFn }).trackFn();\n this._amplitude = amplitude;\n this._privacyConfig = privacyConfig;\n this._propagateContext = propagateContext;\n }\n\n async create(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<AnthropicResponse | AsyncIterable<unknown>> {\n const createFn = this._original.create as (\n ...args: unknown[]\n ) => Promise<unknown>;\n const startTime = performance.now();\n const requestParams = this._withContextHeaders(params);\n const ctx = applySessionContext(amplitudeOverrides);\n\n try {\n this._trackInputMessages(\n requestParams.messages as unknown,\n ctx,\n amplitudeOverrides?.trackInputMessages ?? true,\n );\n const response = await createFn.call(this._original, requestParams);\n\n if (requestParams.stream === true && _isAsyncIterable(response)) {\n return this._wrapStream(\n response as AsyncIterable<unknown>,\n requestParams,\n startTime,\n ctx,\n );\n }\n\n const latencyMs = performance.now() - startTime;\n\n const resp = response as AnthropicResponse;\n const usage = resp.usage;\n const extracted = extractAnthropicContent(\n resp.content as unknown as Array<Record<string, unknown>> | undefined,\n );\n const firstTextBlock = resp.content?.find((b) => b.type === 'text');\n const modelName = String(resp.model ?? requestParams.model ?? 'unknown');\n\n const cacheRead = usage?.cache_read_input_tokens ?? 0;\n const cacheCreation = usage?.cache_creation_input_tokens ?? 0;\n const rawInput = usage?.input_tokens ?? 0;\n const normalizedInput =\n cacheRead || cacheCreation\n ? rawInput + cacheRead + cacheCreation\n : rawInput;\n\n let costUsd: number | null = null;\n if (usage?.input_tokens != null && usage?.output_tokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: normalizedInput,\n outputTokens: usage.output_tokens,\n cacheReadInputTokens: cacheRead,\n cacheCreationInputTokens: cacheCreation,\n defaultProvider: 'anthropic',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(ctx),\n modelName,\n provider: 'anthropic',\n responseContent: String(firstTextBlock?.text ?? ''),\n reasoningContent: extracted.reasoning,\n latencyMs,\n inputTokens: normalizedInput || undefined,\n outputTokens: usage?.output_tokens,\n cacheReadInputTokens: cacheRead || undefined,\n cacheCreationInputTokens: cacheCreation || undefined,\n totalCostUsd: costUsd,\n finishReason: resp.stop_reason,\n toolCalls:\n extracted.toolCalls.length > 0 ? extracted.toolCalls : undefined,\n isStreaming: false,\n toolDefinitions: extractAnthropicToolDefinitions(requestParams),\n systemPrompt: extractAnthropicSystemPrompt(requestParams.system),\n temperature: requestParams.temperature as number | undefined,\n maxOutputTokens: requestParams.max_tokens as number | undefined,\n topP: requestParams.top_p as number | undefined,\n });\n\n return response as AnthropicResponse;\n } catch (error) {\n const latencyMs = performance.now() - startTime;\n this._trackFn({\n ...contextFields(ctx),\n modelName: String(requestParams.model ?? 'unknown'),\n provider: 'anthropic',\n responseContent: '',\n latencyMs,\n isError: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n }\n }\n\n private async *_wrapStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n _startTime: number,\n sessionCtx: ProviderTrackOptions,\n ): AsyncGenerator<unknown> {\n const accumulator = new StreamingAccumulator();\n accumulator.model = String(params.model ?? 'unknown');\n let reasoningContent = '';\n\n try {\n for await (const event of stream) {\n const evt = event as Record<string, unknown>;\n const type = evt.type as string | undefined;\n\n if (type === 'content_block_delta') {\n const delta = evt.delta as Record<string, unknown> | undefined;\n if (delta?.type === 'text_delta' && delta.text != null) {\n accumulator.addContent(String(delta.text));\n } else if (\n delta?.type === 'thinking_delta' &&\n delta.thinking != null\n ) {\n reasoningContent += String(delta.thinking);\n }\n } else if (type === 'content_block_start') {\n const block = evt.content_block as\n | Record<string, unknown>\n | undefined;\n if (block?.type === 'tool_use') {\n accumulator.addToolCall({\n type: 'function',\n id: block.id,\n function: {\n name: String(block.name ?? ''),\n arguments:\n typeof block.input === 'string'\n ? block.input\n : JSON.stringify(block.input ?? {}),\n },\n });\n }\n } else if (type === 'message_delta') {\n const delta = evt.delta as Record<string, unknown> | undefined;\n if (delta?.stop_reason != null) {\n accumulator.finishReason = String(delta.stop_reason);\n }\n const usage = evt.usage as Record<string, number> | undefined;\n if (usage != null) {\n accumulator.setUsage({\n outputTokens: usage.output_tokens,\n });\n }\n } else if (type === 'message_start') {\n const message = evt.message as Record<string, unknown> | undefined;\n if (message?.model != null) {\n accumulator.model = String(message.model);\n }\n const usage = message?.usage as Record<string, number> | undefined;\n if (usage != null) {\n accumulator.setUsage({\n inputTokens: usage.input_tokens,\n cacheReadTokens: usage.cache_read_input_tokens,\n cacheCreationTokens: usage.cache_creation_input_tokens,\n });\n }\n }\n\n yield event;\n }\n } catch (error) {\n accumulator.setError(\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n } finally {\n const state = accumulator.getState();\n const modelName = String(accumulator.model ?? params.model ?? 'unknown');\n\n const streamCacheRead = state.cacheReadTokens ?? 0;\n const streamCacheCreation = state.cacheCreationTokens ?? 0;\n const streamRawInput = state.inputTokens ?? 0;\n const streamNormalizedInput =\n streamCacheRead || streamCacheCreation\n ? streamRawInput + streamCacheRead + streamCacheCreation\n : streamRawInput;\n\n let costUsd: number | null = null;\n if (state.inputTokens != null && state.outputTokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: streamNormalizedInput,\n outputTokens: state.outputTokens,\n cacheReadInputTokens: streamCacheRead,\n cacheCreationInputTokens: streamCacheCreation,\n defaultProvider: 'anthropic',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(sessionCtx),\n modelName,\n provider: 'anthropic',\n responseContent: state.content,\n latencyMs: accumulator.elapsedMs,\n inputTokens: streamNormalizedInput || undefined,\n outputTokens: state.outputTokens,\n cacheReadInputTokens: streamCacheRead || undefined,\n cacheCreationInputTokens: streamCacheCreation || undefined,\n totalCostUsd: costUsd,\n finishReason: state.finishReason,\n toolCalls: state.toolCalls.length > 0 ? state.toolCalls : undefined,\n providerTtfbMs: state.ttfbMs,\n isStreaming: true,\n isError: state.isError,\n errorMessage: state.errorMessage,\n reasoningContent: reasoningContent || undefined,\n toolDefinitions: extractAnthropicToolDefinitions(params),\n systemPrompt: extractAnthropicSystemPrompt(params.system),\n temperature: params.temperature as number | undefined,\n maxOutputTokens: params.max_tokens as number | undefined,\n topP: params.top_p as number | undefined,\n });\n }\n }\n\n private _withContextHeaders(\n params: Record<string, unknown>,\n ): Record<string, unknown> {\n if (!this._propagateContext) return params;\n const existing = (params.extra_headers ?? params.headers) as\n | Record<string, string>\n | undefined;\n const injected = injectContext(existing);\n return {\n ...params,\n extra_headers: injected,\n };\n }\n\n private _trackInputMessages(\n messages: unknown,\n ctx: ProviderTrackOptions,\n shouldTrackInputMessages: boolean,\n ): void {\n if (!shouldTrackInputMessages) return;\n if (ctx.userId == null || ctx.sessionId == null) return;\n if (!Array.isArray(messages)) return;\n for (const msg of messages) {\n const role = (msg as Record<string, unknown>)?.role;\n if (role !== 'user') continue;\n const rawContent = (msg as Record<string, unknown>)?.content;\n const content = Array.isArray(rawContent)\n ? rawContent\n .map((part) => {\n if (typeof part === 'string') return part;\n const text = (part as Record<string, unknown>)?.text;\n return typeof text === 'string' ? text : '';\n })\n .join('')\n : typeof rawContent === 'string'\n ? rawContent\n : '';\n if (!content) continue;\n trackUserMessage({\n amplitude: this._amplitude,\n userId: ctx.userId,\n messageContent: content,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n turnId: ctx.turnId ?? undefined,\n messageSource: ctx.parentAgentId ? 'agent' : 'user',\n agentId: ctx.agentId,\n parentAgentId: ctx.parentAgentId,\n customerOrgId: ctx.customerOrgId,\n agentVersion: ctx.agentVersion,\n context: ctx.context,\n env: ctx.env,\n groups: ctx.groups,\n eventProperties: ctx.eventProperties,\n privacyConfig: this._privacyConfig,\n });\n }\n }\n}\n\nfunction _isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n value != null &&\n typeof (value as Record<symbol, unknown>)[Symbol.asyncIterator] ===\n 'function'\n );\n}\n\nexport function extractAnthropicSystemPrompt(\n system: unknown,\n): string | undefined {\n if (typeof system === 'string') return system;\n if (Array.isArray(system)) {\n return system\n .map((block) => {\n if (typeof block === 'string') return block;\n if (typeof block === 'object' && block != null) {\n return String((block as Record<string, unknown>).text ?? '');\n }\n return '';\n })\n .filter(Boolean)\n .join('\\n');\n }\n return undefined;\n}\n\nexport function extractAnthropicContent(\n content: Array<Record<string, unknown>> | undefined,\n): {\n text: string;\n reasoning: string | undefined;\n toolCalls: Array<Record<string, unknown>>;\n} {\n let text = '';\n let reasoning: string | undefined;\n const toolCalls: Array<Record<string, unknown>> = [];\n\n if (!content) return { text, reasoning, toolCalls };\n\n for (const block of content) {\n if (block.type === 'text') {\n text += String(block.text ?? '');\n } else if (block.type === 'thinking') {\n reasoning = String(block.thinking ?? '');\n } else if (block.type === 'tool_use') {\n toolCalls.push({\n type: 'function',\n id: block.id,\n function: {\n name: String(block.name ?? ''),\n arguments:\n typeof block.input === 'string'\n ? block.input\n : JSON.stringify(block.input ?? {}),\n },\n });\n }\n }\n\n return { text, reasoning, toolCalls };\n}\n\nfunction extractAnthropicToolDefinitions(\n params: Record<string, unknown>,\n): Array<Record<string, unknown>> | undefined {\n const tools = params.tools;\n return Array.isArray(tools) && tools.length > 0\n ? (tools as Array<Record<string, unknown>>)\n : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"anthropic.js","names":["_AnthropicModule: Record<string, unknown> | null","clientOpts: Record<string, unknown>","costUsd: number | null","reasoning: string | undefined","toolCalls: Array<Record<string, unknown>>"],"sources":["../../src/providers/anthropic.ts"],"sourcesContent":["/**\n * Anthropic provider wrapper with automatic tracking.\n */\n\nimport type { PrivacyConfig } from '../core/privacy.js';\nimport { trackUserMessage } from '../core/tracking.js';\nimport { getDefaultPropagateContext, injectContext } from '../propagation.js';\nimport type {\n AmplitudeLike,\n AmplitudeOrAI,\n AnthropicResponse,\n TrackFn,\n} from '../types.js';\nimport { calculateCost } from '../utils/costs.js';\nimport { tryRequire } from '../utils/resolve-module.js';\nimport { StreamingAccumulator } from '../utils/streaming.js';\nimport {\n applySessionContext,\n BaseAIProvider,\n contextFields,\n type ProviderTrackOptions,\n} from './base.js';\n\nconst _resolved = tryRequire('@anthropic-ai/sdk');\nexport const ANTHROPIC_AVAILABLE = _resolved != null;\nconst _AnthropicModule: Record<string, unknown> | null = _resolved;\n\nexport { _AnthropicModule };\n\nexport interface AnthropicOptions {\n amplitude: AmplitudeOrAI;\n apiKey?: string;\n privacyConfig?: PrivacyConfig | null;\n propagateContext?: boolean;\n /** Pass the `@anthropic-ai/sdk` module directly to bypass `tryRequire` (required in bundler environments). */\n anthropicModule?: unknown;\n}\n\nexport class Anthropic<\n TClient extends Record<string, unknown> = Record<string, unknown>,\n> extends BaseAIProvider {\n private _client: TClient;\n readonly messages: WrappedMessages;\n private _propagateContext: boolean;\n\n constructor(options: AnthropicOptions) {\n super({\n amplitude: options.amplitude,\n privacyConfig: options.privacyConfig,\n providerName: 'anthropic',\n });\n\n const mod =\n (options.anthropicModule as Record<string, unknown> | null) ??\n _AnthropicModule;\n if (mod == null) {\n throw new Error(\n '@anthropic-ai/sdk package is required. Install it with: npm install @anthropic-ai/sdk — or pass the module directly via the anthropicModule option.',\n );\n }\n\n const AnthropicSDK = mod.Anthropic as new (\n opts: Record<string, unknown>,\n ) => unknown;\n\n const clientOpts: Record<string, unknown> = {};\n if (options.apiKey) clientOpts.apiKey = options.apiKey;\n\n this._client = new AnthropicSDK(clientOpts) as TClient;\n this._propagateContext =\n options.propagateContext ?? getDefaultPropagateContext();\n this.messages = new WrappedMessages(\n this._client,\n this.trackFn(),\n this._amplitude,\n this._privacyConfig,\n this._propagateContext,\n );\n }\n\n get client(): TClient {\n return this._client;\n }\n}\n\nexport class WrappedMessages {\n _original: Record<string, unknown>;\n private _trackFn: TrackFn;\n private _amplitude: AmplitudeLike;\n private _privacyConfig: PrivacyConfig | null;\n private _propagateContext: boolean;\n\n constructor(\n client: unknown,\n trackFn: TrackFn,\n amplitude: AmplitudeLike,\n privacyConfig: PrivacyConfig | null,\n propagateContext: boolean,\n ) {\n const clientObj = client as Record<string, unknown>;\n this._original = clientObj.messages as Record<string, unknown>;\n this._trackFn =\n typeof trackFn === 'function'\n ? trackFn\n : (trackFn as unknown as { trackFn(): TrackFn }).trackFn();\n this._amplitude = amplitude;\n this._privacyConfig = privacyConfig;\n this._propagateContext = propagateContext;\n }\n\n async create(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<AnthropicResponse | AsyncIterable<unknown>> {\n const createFn = this._original.create as (\n ...args: unknown[]\n ) => Promise<unknown>;\n const startTime = performance.now();\n const requestParams = this._withContextHeaders(params);\n const ctx = applySessionContext(amplitudeOverrides);\n\n try {\n this._trackInputMessages(\n requestParams.messages as unknown,\n ctx,\n amplitudeOverrides?.trackInputMessages ?? true,\n );\n const response = await createFn.call(this._original, requestParams);\n\n if (requestParams.stream === true && _isAsyncIterable(response)) {\n return this._wrapStream(\n response as AsyncIterable<unknown>,\n requestParams,\n startTime,\n ctx,\n );\n }\n\n const latencyMs = performance.now() - startTime;\n\n const resp = response as AnthropicResponse;\n const usage = resp.usage;\n const extracted = extractAnthropicContent(\n resp.content as unknown as Array<Record<string, unknown>> | undefined,\n );\n const firstTextBlock = resp.content?.find((b) => b.type === 'text');\n const modelName = String(resp.model ?? requestParams.model ?? 'unknown');\n\n const cacheRead = usage?.cache_read_input_tokens ?? 0;\n const cacheCreation = usage?.cache_creation_input_tokens ?? 0;\n const rawInput = usage?.input_tokens ?? 0;\n const normalizedInput =\n cacheRead || cacheCreation\n ? rawInput + cacheRead + cacheCreation\n : rawInput;\n\n let costUsd: number | null = null;\n if (usage?.input_tokens != null && usage?.output_tokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: normalizedInput,\n outputTokens: usage.output_tokens,\n cacheReadInputTokens: cacheRead,\n cacheCreationInputTokens: cacheCreation,\n defaultProvider: 'anthropic',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(ctx),\n modelName,\n provider: 'anthropic',\n responseContent: String(firstTextBlock?.text ?? ''),\n reasoningContent: extracted.reasoning,\n latencyMs,\n inputTokens: normalizedInput || undefined,\n outputTokens: usage?.output_tokens,\n cacheReadInputTokens: cacheRead || undefined,\n cacheCreationInputTokens: cacheCreation || undefined,\n totalCostUsd: costUsd,\n finishReason: resp.stop_reason,\n toolCalls:\n extracted.toolCalls.length > 0 ? extracted.toolCalls : undefined,\n isStreaming: false,\n toolDefinitions: extractAnthropicToolDefinitions(requestParams),\n systemPrompt: extractAnthropicSystemPrompt(requestParams.system),\n temperature: requestParams.temperature as number | undefined,\n maxOutputTokens: requestParams.max_tokens as number | undefined,\n topP: requestParams.top_p as number | undefined,\n });\n\n return response as AnthropicResponse;\n } catch (error) {\n const latencyMs = performance.now() - startTime;\n this._trackFn({\n ...contextFields(ctx),\n modelName: String(requestParams.model ?? 'unknown'),\n provider: 'anthropic',\n responseContent: '',\n latencyMs,\n isError: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n }\n }\n\n private async *_wrapStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n _startTime: number,\n sessionCtx: ProviderTrackOptions,\n ): AsyncGenerator<unknown> {\n const accumulator = new StreamingAccumulator();\n accumulator.model = String(params.model ?? 'unknown');\n let reasoningContent = '';\n\n try {\n for await (const event of stream) {\n const evt = event as Record<string, unknown>;\n const type = evt.type as string | undefined;\n\n if (type === 'content_block_delta') {\n const delta = evt.delta as Record<string, unknown> | undefined;\n if (delta?.type === 'text_delta' && delta.text != null) {\n accumulator.addContent(String(delta.text));\n } else if (\n delta?.type === 'thinking_delta' &&\n delta.thinking != null\n ) {\n reasoningContent += String(delta.thinking);\n }\n } else if (type === 'content_block_start') {\n const block = evt.content_block as\n | Record<string, unknown>\n | undefined;\n if (block?.type === 'tool_use') {\n accumulator.addToolCall({\n type: 'function',\n id: block.id,\n function: {\n name: String(block.name ?? ''),\n arguments:\n typeof block.input === 'string'\n ? block.input\n : JSON.stringify(block.input ?? {}),\n },\n });\n }\n } else if (type === 'message_delta') {\n const delta = evt.delta as Record<string, unknown> | undefined;\n if (delta?.stop_reason != null) {\n accumulator.finishReason = String(delta.stop_reason);\n }\n const usage = evt.usage as Record<string, number> | undefined;\n if (usage != null) {\n accumulator.setUsage({\n outputTokens: usage.output_tokens,\n });\n }\n } else if (type === 'message_start') {\n const message = evt.message as Record<string, unknown> | undefined;\n if (message?.model != null) {\n accumulator.model = String(message.model);\n }\n const usage = message?.usage as Record<string, number> | undefined;\n if (usage != null) {\n accumulator.setUsage({\n inputTokens: usage.input_tokens,\n cacheReadTokens: usage.cache_read_input_tokens,\n cacheCreationTokens: usage.cache_creation_input_tokens,\n });\n }\n }\n\n yield event;\n }\n } catch (error) {\n accumulator.setError(\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n } finally {\n const state = accumulator.getState();\n const modelName = String(accumulator.model ?? params.model ?? 'unknown');\n\n const streamCacheRead = state.cacheReadTokens ?? 0;\n const streamCacheCreation = state.cacheCreationTokens ?? 0;\n const streamRawInput = state.inputTokens ?? 0;\n const streamNormalizedInput =\n streamCacheRead || streamCacheCreation\n ? streamRawInput + streamCacheRead + streamCacheCreation\n : streamRawInput;\n\n let costUsd: number | null = null;\n if (state.inputTokens != null && state.outputTokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: streamNormalizedInput,\n outputTokens: state.outputTokens,\n cacheReadInputTokens: streamCacheRead,\n cacheCreationInputTokens: streamCacheCreation,\n defaultProvider: 'anthropic',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(sessionCtx),\n modelName,\n provider: 'anthropic',\n responseContent: state.content,\n latencyMs: accumulator.elapsedMs,\n inputTokens: streamNormalizedInput || undefined,\n outputTokens: state.outputTokens,\n cacheReadInputTokens: streamCacheRead || undefined,\n cacheCreationInputTokens: streamCacheCreation || undefined,\n totalCostUsd: costUsd,\n finishReason: state.finishReason,\n toolCalls: state.toolCalls.length > 0 ? state.toolCalls : undefined,\n providerTtfbMs: state.ttfbMs,\n isStreaming: true,\n isError: state.isError,\n errorMessage: state.errorMessage,\n reasoningContent: reasoningContent || undefined,\n toolDefinitions: extractAnthropicToolDefinitions(params),\n systemPrompt: extractAnthropicSystemPrompt(params.system),\n temperature: params.temperature as number | undefined,\n maxOutputTokens: params.max_tokens as number | undefined,\n topP: params.top_p as number | undefined,\n });\n }\n }\n\n private _withContextHeaders(\n params: Record<string, unknown>,\n ): Record<string, unknown> {\n if (!this._propagateContext) return params;\n const existing = (params.extra_headers ?? params.headers) as\n | Record<string, string>\n | undefined;\n const injected = injectContext(existing);\n return {\n ...params,\n extra_headers: injected,\n };\n }\n\n private _trackInputMessages(\n messages: unknown,\n ctx: ProviderTrackOptions,\n shouldTrackInputMessages: boolean,\n ): void {\n if (!shouldTrackInputMessages) return;\n if (ctx.userId == null || ctx.sessionId == null) return;\n if (!Array.isArray(messages)) return;\n\n // In agent loops each create() re-sends the full conversation.\n // Only track user messages appended after the last assistant reply\n // so the same message isn't autotracked on every iteration.\n const lastReplyIdx = (messages as Record<string, unknown>[]).findLastIndex(\n (m) => m?.role === 'assistant',\n );\n const newMessages = (messages as Record<string, unknown>[]).slice(lastReplyIdx + 1);\n\n for (const msg of newMessages) {\n const role = (msg as Record<string, unknown>)?.role;\n if (role !== 'user') continue;\n const rawContent = (msg as Record<string, unknown>)?.content;\n const content = Array.isArray(rawContent)\n ? rawContent\n .map((part) => {\n if (typeof part === 'string') return part;\n const text = (part as Record<string, unknown>)?.text;\n return typeof text === 'string' ? text : '';\n })\n .join('')\n : typeof rawContent === 'string'\n ? rawContent\n : '';\n if (!content) continue;\n trackUserMessage({\n amplitude: this._amplitude,\n userId: ctx.userId,\n messageContent: content,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n turnId: ctx.turnId ?? undefined,\n messageSource: ctx.parentAgentId ? 'agent' : 'user',\n agentId: ctx.agentId,\n parentAgentId: ctx.parentAgentId,\n customerOrgId: ctx.customerOrgId,\n agentVersion: ctx.agentVersion,\n context: ctx.context,\n env: ctx.env,\n groups: ctx.groups,\n eventProperties: ctx.eventProperties,\n privacyConfig: this._privacyConfig,\n });\n }\n }\n}\n\nfunction _isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n value != null &&\n typeof (value as Record<symbol, unknown>)[Symbol.asyncIterator] ===\n 'function'\n );\n}\n\nexport function extractAnthropicSystemPrompt(\n system: unknown,\n): string | undefined {\n if (typeof system === 'string') return system;\n if (Array.isArray(system)) {\n return system\n .map((block) => {\n if (typeof block === 'string') return block;\n if (typeof block === 'object' && block != null) {\n return String((block as Record<string, unknown>).text ?? '');\n }\n return '';\n })\n .filter(Boolean)\n .join('\\n');\n }\n return undefined;\n}\n\nexport function extractAnthropicContent(\n content: Array<Record<string, unknown>> | undefined,\n): {\n text: string;\n reasoning: string | undefined;\n toolCalls: Array<Record<string, unknown>>;\n} {\n let text = '';\n let reasoning: string | undefined;\n const toolCalls: Array<Record<string, unknown>> = [];\n\n if (!content) return { text, reasoning, toolCalls };\n\n for (const block of content) {\n if (block.type === 'text') {\n text += String(block.text ?? '');\n } else if (block.type === 'thinking') {\n reasoning = String(block.thinking ?? '');\n } else if (block.type === 'tool_use') {\n toolCalls.push({\n type: 'function',\n id: block.id,\n function: {\n name: String(block.name ?? ''),\n arguments:\n typeof block.input === 'string'\n ? block.input\n : JSON.stringify(block.input ?? {}),\n },\n });\n }\n }\n\n return { text, reasoning, toolCalls };\n}\n\nfunction extractAnthropicToolDefinitions(\n params: Record<string, unknown>,\n): Array<Record<string, unknown>> | undefined {\n const tools = params.tools;\n return Array.isArray(tools) && tools.length > 0\n ? (tools as Array<Record<string, unknown>>)\n : undefined;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"mistral.d.ts","names":[],"sources":["../../src/providers/mistral.ts"],"sourcesContent":[],"mappings":";;;;;;AAaM,cADO,iBACS,EAAM,OAAA;AAI5B,cAJM,cAIyB,EAJT,MAKT,CAAA,MAAA,EAAA,OAEK,CAAA,GAAA,IAAA;AAOD,UAVA,cAAA,CAUA;EAEM,SAAA,EAXV,aAWU;EAJM,MAAA,CAAA,EAAA,MAAA;EAAc,aAAA,CAAA,EALzB,aAKyB,GAAA,IAAA;EAoC9B;EAI2B,aAAA,CAAA,EAAA,OAAA;;AAQW,cAhDtC,OAAA,SAAgB,cAAA,CAgDsB;EAoF5B,QAAA,OAAA;EAA0B,SAAA,IAAA,EAlIhC,WAkIgC;EAAO,WAAA,CAAA,OAAA,EAhIjC,cAgIiC;;;cAhG3C,WAAA;;;wCAI2B;mBAQf,0BAA0B;iBAoF5B,0BAA0B"}
1
+ {"version":3,"file":"mistral.d.ts","names":[],"sources":["../../src/providers/mistral.ts"],"sourcesContent":[],"mappings":";;;;;;AAaM,cADO,iBACS,EAAA,OAAM;AAI5B,cAJM,cAIyB,EAJT,MAKT,CAAA,MAAA,EAAA,OAEK,CAAA,GAAA,IAAA;AAOD,UAVA,cAAA,CAUA;EAEM,SAAA,EAXV,aAWU;EAJM,MAAA,CAAA,EAAA,MAAA;EAAc,aAAA,CAAA,EALzB,aAKyB,GAAA,IAAA;EAoC9B;EAI2B,aAAA,CAAA,EAAA,OAAA;;AAQW,cAhDtC,OAAA,SAAgB,cAAA,CAgDsB;EAoF5B,QAAA,OAAA;EAA0B,SAAA,IAAA,EAlIhC,WAkIgC;EAAO,WAAA,CAAA,OAAA,EAhIjC,cAgIiC;;;cAhG3C,WAAA;;;wCAI2B;mBAQf,0BAA0B;iBAoF5B,0BAA0B"}
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","names":[],"sources":["../../src/providers/openai.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"openai.d.ts","names":[],"sources":["../../src/providers/openai.ts"],"sourcesContent":[],"mappings":"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"}
@@ -237,7 +237,10 @@ var WrappedCompletions = class {
237
237
  if (!shouldTrackInputMessages) return;
238
238
  if (ctx.userId == null || ctx.sessionId == null) return;
239
239
  if (!Array.isArray(messages)) return;
240
- for (const msg of messages) {
240
+ const msgs = messages;
241
+ const lastReplyIdx = msgs.findLastIndex((m) => m?.role === "assistant" || m?.role === "tool");
242
+ const newMessages = msgs.slice(lastReplyIdx + 1);
243
+ for (const msg of newMessages) {
241
244
  if (msg?.role !== "user") continue;
242
245
  const content = msg.content;
243
246
  if (typeof content !== "string" || content.length === 0) continue;
@@ -515,8 +518,14 @@ function extractResponsesToolDefinitions(params) {
515
518
  function extractResponsesUserInputs(input) {
516
519
  if (typeof input === "string") return [input];
517
520
  if (!Array.isArray(input)) return [];
521
+ const entries = input;
522
+ const lastReplyIdx = entries.findLastIndex((e) => {
523
+ if (typeof e === "string") return false;
524
+ return e.role === "assistant" || e.type === "function_call" || e.type === "function_call_output";
525
+ });
526
+ const newEntries = entries.slice(lastReplyIdx + 1);
518
527
  const result = [];
519
- for (const entry of input) {
528
+ for (const entry of newEntries) {
520
529
  if (typeof entry === "string") {
521
530
  result.push(entry);
522
531
  continue;
@@ -1 +1 @@
1
- {"version":3,"file":"openai.js","names":["_OpenAIModule: Record<string, unknown> | null","clientOpts: Record<string, unknown>","costUsd: number | null","toolCalls: Array<Record<string, unknown>>","result: string[]"],"sources":["../../src/providers/openai.ts"],"sourcesContent":["/**\n * OpenAI provider wrapper with automatic tracking.\n *\n * Wraps the OpenAI client and instruments:\n * - chat.completions.create()\n * - responses.create()\n * to track AI response events via Amplitude.\n */\n\nimport type { PrivacyConfig } from '../core/privacy.js';\nimport { trackUserMessage } from '../core/tracking.js';\nimport { getDefaultPropagateContext, injectContext } from '../propagation.js';\nimport type {\n AmplitudeLike,\n AmplitudeOrAI,\n ChatCompletionResponse,\n ChatMessage,\n OpenAIResponse,\n OpenAIResponseInput,\n OpenAIResponseOutputItem,\n TrackFn,\n} from '../types.js';\nimport { calculateCost } from '../utils/costs.js';\nimport { tryRequire } from '../utils/resolve-module.js';\nimport { StreamingAccumulator } from '../utils/streaming.js';\nimport {\n applySessionContext,\n BaseAIProvider,\n contextFields,\n type ProviderTrackOptions,\n} from './base.js';\n\nconst _resolved = tryRequire('openai');\nexport const OPENAI_AVAILABLE = _resolved != null;\nconst _OpenAIModule: Record<string, unknown> | null = _resolved;\n\nexport { _OpenAIModule };\n\nexport interface OpenAIOptions {\n amplitude: AmplitudeOrAI;\n apiKey?: string;\n baseUrl?: string;\n privacyConfig?: PrivacyConfig | null;\n propagateContext?: boolean;\n /** Pass the `openai` module directly to bypass `tryRequire` (required in bundler environments). */\n openaiModule?: unknown;\n}\n\nexport class OpenAI<\n TClient extends Record<string, unknown> = Record<string, unknown>,\n> extends BaseAIProvider {\n private _client: TClient;\n readonly chat: WrappedChat;\n readonly responses: WrappedResponses;\n private _propagateContext: boolean;\n\n constructor(options: OpenAIOptions) {\n super({\n amplitude: options.amplitude,\n privacyConfig: options.privacyConfig,\n providerName: 'openai',\n });\n\n const mod =\n (options.openaiModule as Record<string, unknown> | null) ?? _OpenAIModule;\n if (mod == null) {\n throw new Error(\n 'openai package is required. Install it with: npm install openai — or pass the module directly via the openaiModule option.',\n );\n }\n\n const OpenAISDK = mod.OpenAI as new (\n opts: Record<string, unknown>,\n ) => unknown;\n\n const clientOpts: Record<string, unknown> = {};\n if (options.apiKey) clientOpts.apiKey = options.apiKey;\n if (options.baseUrl) clientOpts.baseURL = options.baseUrl;\n\n this._client = new OpenAISDK(clientOpts) as TClient;\n this._propagateContext =\n options.propagateContext ?? getDefaultPropagateContext();\n this.chat = new WrappedChat(\n this._client,\n this.trackFn(),\n this._amplitude,\n this._privacyConfig,\n this._propagateContext,\n );\n this.responses = new WrappedResponses(\n this._client,\n this.trackFn(),\n this._amplitude,\n this._privacyConfig,\n this._propagateContext,\n );\n }\n\n get client(): TClient {\n return this._client;\n }\n}\n\nexport class WrappedChat {\n readonly completions: WrappedCompletions;\n\n constructor(\n client: unknown,\n trackFn: TrackFn,\n amplitude: AmplitudeLike,\n privacyConfig: PrivacyConfig | null,\n propagateContext: boolean,\n ) {\n const clientObj = client as Record<string, unknown>;\n const chat = clientObj.chat as Record<string, unknown>;\n this.completions = new WrappedCompletions(\n chat.completions as Record<string, unknown>,\n trackFn,\n amplitude,\n privacyConfig,\n propagateContext,\n );\n }\n}\n\nexport class WrappedCompletions {\n _original: Record<string, unknown>;\n private _trackFn: TrackFn;\n private _amplitude: AmplitudeLike;\n private _privacyConfig: PrivacyConfig | null;\n private _propagateContext: boolean;\n private _providerName: string;\n\n constructor(\n completions: Record<string, unknown>,\n trackFn: TrackFn,\n amplitude: AmplitudeLike,\n privacyConfig: PrivacyConfig | null,\n propagateContext: boolean,\n providerName = 'openai',\n ) {\n this._original = completions;\n this._trackFn =\n typeof trackFn === 'function'\n ? trackFn\n : (trackFn as unknown as { trackFn(): TrackFn }).trackFn();\n this._amplitude = amplitude;\n this._privacyConfig = privacyConfig;\n this._propagateContext = propagateContext;\n this._providerName = providerName;\n }\n\n async create(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<ChatCompletionResponse | AsyncIterable<unknown>> {\n const createFn = this._original.create as (\n ...args: unknown[]\n ) => Promise<unknown>;\n const startTime = performance.now();\n let requestParams = this._withContextHeaders(params);\n const ctx = applySessionContext(amplitudeOverrides);\n\n if (requestParams.stream === true && requestParams.stream_options == null) {\n requestParams = {\n ...requestParams,\n stream_options: { include_usage: true },\n };\n }\n\n try {\n this._trackInputMessages(\n requestParams.messages as unknown,\n ctx,\n amplitudeOverrides?.trackInputMessages ?? true,\n );\n const response = await createFn.call(this._original, requestParams);\n\n if (requestParams.stream === true && _isAsyncIterable(response)) {\n return this._wrapStream(\n response as AsyncIterable<unknown>,\n requestParams,\n startTime,\n ctx,\n );\n }\n\n const latencyMs = performance.now() - startTime;\n\n const resp = response as ChatCompletionResponse;\n const usage = resp.usage;\n const choice = resp.choices?.[0];\n const modelName = String(resp.model ?? requestParams.model ?? 'unknown');\n const toolCalls = choice?.message?.tool_calls;\n\n const usageExt = usage as Record<string, unknown> | undefined;\n const promptDetails = usageExt?.prompt_tokens_details as\n | Record<string, number>\n | undefined;\n const completionDetails = usageExt?.completion_tokens_details as\n | Record<string, number>\n | undefined;\n const reasoningTokens = completionDetails?.reasoning_tokens;\n const cachedTokens = promptDetails?.cached_tokens;\n\n let costUsd: number | null = null;\n if (usage?.prompt_tokens != null && usage?.completion_tokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: usage.prompt_tokens,\n outputTokens: usage.completion_tokens,\n reasoningTokens: reasoningTokens ?? 0,\n cacheReadInputTokens: cachedTokens ?? 0,\n defaultProvider: 'openai',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(ctx),\n modelName,\n provider: this._providerName,\n responseContent: String(choice?.message?.content ?? ''),\n latencyMs,\n inputTokens: usage?.prompt_tokens,\n outputTokens: usage?.completion_tokens,\n totalTokens: usage?.total_tokens,\n reasoningTokens,\n cacheReadInputTokens: cachedTokens,\n totalCostUsd: costUsd,\n finishReason: choice?.finish_reason,\n toolCalls: toolCalls ?? undefined,\n isStreaming: false,\n toolDefinitions: extractToolDefinitions(requestParams),\n systemPrompt: extractSystemPrompt(requestParams),\n temperature: requestParams.temperature as number | undefined,\n maxOutputTokens: requestParams.max_tokens as number | undefined,\n topP: requestParams.top_p as number | undefined,\n });\n\n return response as ChatCompletionResponse;\n } catch (error) {\n const latencyMs = performance.now() - startTime;\n this._trackFn({\n ...contextFields(ctx),\n modelName: String(requestParams.model ?? 'unknown'),\n provider: this._providerName,\n responseContent: '',\n latencyMs,\n isError: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n }\n }\n\n async parse(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<ChatCompletionResponse | AsyncIterable<unknown>> {\n const parseFn = this._original.parse as\n | ((...args: unknown[]) => Promise<unknown>)\n | undefined;\n if (typeof parseFn !== 'function') {\n throw new Error('OpenAI SDK does not expose chat.completions.parse');\n }\n const originalCreate = this._original.create;\n this._original.create = parseFn;\n try {\n return await this.create(params, amplitudeOverrides);\n } finally {\n this._original.create = originalCreate;\n }\n }\n\n private async *_wrapStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n _startTime: number,\n sessionCtx: ProviderTrackOptions,\n ): AsyncGenerator<unknown> {\n const accumulator = new StreamingAccumulator();\n accumulator.model = String(params.model ?? 'unknown');\n let reasoningContent = '';\n\n try {\n for await (const chunk of stream) {\n const c = chunk as Record<string, unknown>;\n const choices = c.choices as Array<Record<string, unknown>> | undefined;\n const delta = choices?.[0]?.delta as\n | Record<string, unknown>\n | undefined;\n\n if (delta?.content != null) {\n accumulator.addContent(String(delta.content));\n }\n\n const deltaToolCalls = delta?.tool_calls as\n | Array<Record<string, unknown>>\n | undefined;\n if (Array.isArray(deltaToolCalls)) {\n for (const call of deltaToolCalls) {\n const idx = call.index as number | undefined;\n const id = call.id as string | undefined;\n const fn = call.function as Record<string, unknown> | undefined;\n if (idx != null && id && fn?.name != null) {\n accumulator.setToolCallAt(idx, {\n type: 'function',\n id,\n function: {\n name: fn.name,\n arguments: ((fn.arguments as string) ?? ''),\n },\n });\n } else if (idx != null && fn?.arguments) {\n accumulator.appendToolCallArgs(idx, fn.arguments as string);\n } else {\n accumulator.addToolCall(call);\n }\n }\n }\n\n if (delta?.reasoning_content != null) {\n reasoningContent += String(delta.reasoning_content);\n }\n\n const finishReason = choices?.[0]?.finish_reason;\n if (finishReason != null) {\n accumulator.finishReason = String(finishReason);\n }\n\n const usage = c.usage as Record<string, unknown> | undefined;\n if (usage != null) {\n const promptDetails = usage.prompt_tokens_details as\n | Record<string, number>\n | undefined;\n const completionDetails = usage.completion_tokens_details as\n | Record<string, number>\n | undefined;\n\n accumulator.setUsage({\n inputTokens: usage.prompt_tokens as number | undefined,\n outputTokens: usage.completion_tokens as number | undefined,\n totalTokens: usage.total_tokens as number | undefined,\n reasoningTokens: completionDetails?.reasoning_tokens,\n cacheReadTokens: promptDetails?.cached_tokens,\n });\n }\n\n yield chunk;\n }\n } catch (error) {\n accumulator.setError(\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n } finally {\n const state = accumulator.getState();\n const modelName = String(accumulator.model ?? params.model ?? 'unknown');\n\n let costUsd: number | null = null;\n if (state.inputTokens != null && state.outputTokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n reasoningTokens: state.reasoningTokens ?? 0,\n cacheReadInputTokens: state.cacheReadTokens ?? 0,\n defaultProvider: 'openai',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(sessionCtx),\n modelName,\n provider: this._providerName,\n responseContent: state.content,\n latencyMs: accumulator.elapsedMs,\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n totalTokens: state.totalTokens,\n reasoningTokens: state.reasoningTokens,\n cacheReadInputTokens: state.cacheReadTokens,\n totalCostUsd: costUsd,\n finishReason: state.finishReason,\n toolCalls: state.toolCalls.length > 0 ? state.toolCalls : undefined,\n providerTtfbMs: state.ttfbMs,\n isStreaming: true,\n isError: state.isError,\n errorMessage: state.errorMessage,\n reasoningContent: reasoningContent || undefined,\n toolDefinitions: extractToolDefinitions(params),\n systemPrompt: extractSystemPrompt(params),\n temperature: params.temperature as number | undefined,\n maxOutputTokens: params.max_tokens as number | undefined,\n topP: params.top_p as number | undefined,\n });\n }\n }\n\n private _withContextHeaders(\n params: Record<string, unknown>,\n ): Record<string, unknown> {\n if (!this._propagateContext) return params;\n const existing = (params.extra_headers ?? params.headers) as\n | Record<string, string>\n | undefined;\n const injected = injectContext(existing);\n return {\n ...params,\n extra_headers: injected,\n };\n }\n\n private _trackInputMessages(\n messages: unknown,\n ctx: ProviderTrackOptions,\n shouldTrackInputMessages: boolean,\n ): void {\n if (!shouldTrackInputMessages) return;\n if (ctx.userId == null || ctx.sessionId == null) return;\n if (!Array.isArray(messages)) return;\n for (const msg of messages as ChatMessage[]) {\n if (msg?.role !== 'user') continue;\n const content = msg.content;\n if (typeof content !== 'string' || content.length === 0) continue;\n trackUserMessage({\n amplitude: this._amplitude,\n userId: ctx.userId,\n messageContent: content,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n turnId: ctx.turnId ?? undefined,\n messageSource: ctx.parentAgentId ? 'agent' : 'user',\n agentId: ctx.agentId,\n parentAgentId: ctx.parentAgentId,\n customerOrgId: ctx.customerOrgId,\n agentVersion: ctx.agentVersion,\n context: ctx.context,\n env: ctx.env,\n groups: ctx.groups,\n eventProperties: ctx.eventProperties,\n privacyConfig: this._privacyConfig,\n });\n }\n }\n}\n\nexport class WrappedResponses {\n _original: Record<string, unknown>;\n private _trackFn: TrackFn;\n private _amplitude: AmplitudeLike;\n private _privacyConfig: PrivacyConfig | null;\n private _propagateContext: boolean;\n private _providerName: string;\n\n constructor(\n client: unknown,\n trackFn: TrackFn,\n amplitude: AmplitudeLike,\n privacyConfig: PrivacyConfig | null,\n propagateContext: boolean,\n providerName = 'openai',\n ) {\n const clientObj = client as Record<string, unknown>;\n const responses = (clientObj.responses ?? {}) as Record<string, unknown>;\n this._original = responses;\n this._trackFn =\n typeof trackFn === 'function'\n ? trackFn\n : (trackFn as unknown as { trackFn(): TrackFn }).trackFn();\n this._amplitude = amplitude;\n this._privacyConfig = privacyConfig;\n this._propagateContext = propagateContext;\n this._providerName = providerName;\n }\n\n async create(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<OpenAIResponse | AsyncIterable<unknown>> {\n const createFn = this._original.create as (\n ...args: unknown[]\n ) => Promise<unknown>;\n const startTime = performance.now();\n const requestParams = this._withContextHeaders(params);\n const ctx = applySessionContext(amplitudeOverrides);\n\n try {\n this._trackInputMessages(\n requestParams.input as unknown,\n ctx,\n amplitudeOverrides?.trackInputMessages ?? true,\n );\n const response = await createFn.call(this._original, requestParams);\n if (requestParams.stream === true && _isAsyncIterable(response)) {\n return this._wrapStream(\n response as AsyncIterable<unknown>,\n requestParams,\n startTime,\n ctx,\n );\n }\n\n const latencyMs = performance.now() - startTime;\n const resp = response as OpenAIResponse;\n const usage = resp.usage;\n const responseText = extractResponsesText(resp);\n const responseToolCalls = extractResponsesToolCalls(resp);\n const modelName = String(resp.model ?? requestParams.model ?? 'unknown');\n\n let costUsd: number | null = null;\n if (usage?.input_tokens != null && usage?.output_tokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: usage.input_tokens,\n outputTokens: usage.output_tokens,\n reasoningTokens: usage.output_tokens_details?.reasoning_tokens ?? 0,\n defaultProvider: 'openai',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(ctx),\n modelName,\n provider: this._providerName,\n responseContent: responseText,\n latencyMs,\n inputTokens: usage?.input_tokens,\n outputTokens: usage?.output_tokens,\n totalTokens: usage?.total_tokens,\n reasoningTokens: usage?.output_tokens_details?.reasoning_tokens,\n totalCostUsd: costUsd,\n finishReason: extractResponsesFinishReason(resp),\n toolCalls: responseToolCalls.length > 0 ? responseToolCalls : undefined,\n isStreaming: false,\n toolDefinitions: extractResponsesToolDefinitions(requestParams),\n systemPrompt: extractResponsesSystemPrompt(requestParams),\n temperature: requestParams.temperature as number | undefined,\n maxOutputTokens: requestParams.max_output_tokens as number | undefined,\n topP: requestParams.top_p as number | undefined,\n });\n\n return response as OpenAIResponse;\n } catch (error) {\n const latencyMs = performance.now() - startTime;\n this._trackFn({\n ...contextFields(ctx),\n modelName: String(requestParams.model ?? 'unknown'),\n provider: this._providerName,\n responseContent: '',\n latencyMs,\n isError: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n async stream(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<AsyncIterable<unknown>> {\n const streamFn = this._original.stream as\n | ((...args: unknown[]) => Promise<unknown>)\n | undefined;\n if (typeof streamFn !== 'function') {\n throw new Error('OpenAI SDK does not expose responses.stream');\n }\n const startTime = performance.now();\n const requestParams = this._withContextHeaders(params);\n const ctx = applySessionContext(amplitudeOverrides);\n\n try {\n this._trackInputMessages(\n requestParams.input as unknown,\n ctx,\n amplitudeOverrides?.trackInputMessages ?? true,\n );\n const response = await streamFn.call(this._original, requestParams);\n if (!_isAsyncIterable(response)) {\n throw new Error('OpenAI responses.stream did not return AsyncIterable');\n }\n return this._wrapStream(\n response as AsyncIterable<unknown>,\n requestParams,\n startTime,\n ctx,\n );\n } catch (error) {\n this._trackFn({\n ...contextFields(ctx),\n modelName: String(requestParams.model ?? 'unknown'),\n provider: this._providerName,\n responseContent: '',\n latencyMs: performance.now() - startTime,\n isError: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n isStreaming: true,\n });\n throw error;\n }\n }\n\n private async *_wrapStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n _startTime: number,\n sessionCtx: ProviderTrackOptions,\n ): AsyncGenerator<unknown> {\n const accumulator = new StreamingAccumulator();\n accumulator.model = String(params.model ?? 'unknown');\n\n try {\n for await (const event of stream) {\n const e = event as Record<string, unknown>;\n const type = e.type as string | undefined;\n if (type === 'response.output_text.delta') {\n const delta = e.delta;\n if (typeof delta === 'string') accumulator.addContent(delta);\n } else if (type === 'response.completed') {\n const response = e.response as OpenAIResponse | undefined;\n if (response != null) {\n const outputText = extractResponsesText(response);\n if (outputText.length > 0) {\n accumulator.content = outputText;\n }\n const usage = response.usage;\n accumulator.setUsage({\n inputTokens: usage?.input_tokens,\n outputTokens: usage?.output_tokens,\n totalTokens: usage?.total_tokens,\n reasoningTokens: usage?.output_tokens_details?.reasoning_tokens,\n });\n const finishReason = extractResponsesFinishReason(response);\n if (finishReason != null) accumulator.finishReason = finishReason;\n }\n }\n yield event;\n }\n } catch (error) {\n accumulator.setError(\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n } finally {\n const state = accumulator.getState();\n let costUsd: number | null = null;\n if (state.inputTokens != null && state.outputTokens != null) {\n try {\n costUsd = calculateCost({\n modelName: String(accumulator.model ?? params.model ?? 'unknown'),\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n reasoningTokens: state.reasoningTokens ?? 0,\n defaultProvider: 'openai',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n this._trackFn({\n ...contextFields(sessionCtx),\n modelName: String(accumulator.model ?? params.model ?? 'unknown'),\n provider: this._providerName,\n responseContent: state.content,\n latencyMs: accumulator.elapsedMs,\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n totalTokens: state.totalTokens,\n reasoningTokens: state.reasoningTokens,\n totalCostUsd: costUsd,\n finishReason: state.finishReason,\n providerTtfbMs: state.ttfbMs,\n isStreaming: true,\n isError: state.isError,\n errorMessage: state.errorMessage,\n toolDefinitions: extractResponsesToolDefinitions(params),\n systemPrompt: extractResponsesSystemPrompt(params),\n temperature: params.temperature as number | undefined,\n maxOutputTokens: params.max_output_tokens as number | undefined,\n topP: params.top_p as number | undefined,\n });\n }\n }\n\n private _withContextHeaders(\n params: Record<string, unknown>,\n ): Record<string, unknown> {\n if (!this._propagateContext) return params;\n const existing = (params.extra_headers ?? params.headers) as\n | Record<string, string>\n | undefined;\n const injected = injectContext(existing);\n return {\n ...params,\n extra_headers: injected,\n };\n }\n\n private _trackInputMessages(\n input: unknown,\n ctx: ProviderTrackOptions,\n shouldTrackInputMessages: boolean,\n ): void {\n if (!shouldTrackInputMessages) return;\n if (ctx.userId == null || ctx.sessionId == null) return;\n for (const text of extractResponsesUserInputs(input)) {\n if (!text) continue;\n trackUserMessage({\n amplitude: this._amplitude,\n userId: ctx.userId,\n messageContent: text,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n turnId: ctx.turnId ?? undefined,\n messageSource: ctx.parentAgentId ? 'agent' : 'user',\n agentId: ctx.agentId,\n parentAgentId: ctx.parentAgentId,\n customerOrgId: ctx.customerOrgId,\n agentVersion: ctx.agentVersion,\n context: ctx.context,\n env: ctx.env,\n groups: ctx.groups,\n eventProperties: ctx.eventProperties,\n privacyConfig: this._privacyConfig,\n });\n }\n }\n}\n\nfunction _isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n value != null &&\n typeof (value as Record<symbol, unknown>)[Symbol.asyncIterator] ===\n 'function'\n );\n}\n\nexport function extractSystemPrompt(\n params: Record<string, unknown>,\n): string | undefined {\n const messages = params.messages as\n | Array<Record<string, unknown>>\n | undefined;\n if (!messages?.length) return undefined;\n const systemMsg = messages.find(\n (m) => m.role === 'system' || m.role === 'developer',\n );\n return systemMsg ? String(systemMsg.content ?? '') : undefined;\n}\n\nfunction extractResponsesSystemPrompt(\n params: Record<string, unknown>,\n): string | undefined {\n const instructions = params.instructions;\n return typeof instructions === 'string' ? instructions : undefined;\n}\n\nfunction extractResponsesFinishReason(\n resp: OpenAIResponse,\n): string | undefined {\n const status = resp.status;\n if (typeof status === 'string' && status.length > 0) return status;\n const out = resp.output?.[0];\n if (out != null && typeof out.status === 'string') return out.status;\n return undefined;\n}\n\nfunction extractResponsesText(resp: OpenAIResponse): string {\n if (typeof resp.output_text === 'string') return resp.output_text;\n const outputs = resp.output ?? [];\n let text = '';\n for (const item of outputs) {\n text += extractOutputItemText(item);\n }\n return text;\n}\n\nfunction extractResponsesToolCalls(\n resp: OpenAIResponse,\n): Array<Record<string, unknown>> {\n const toolCalls: Array<Record<string, unknown>> = [];\n const outputs = resp.output ?? [];\n for (const item of outputs) {\n if (!Array.isArray(item.content)) continue;\n for (const contentItem of item.content) {\n if (\n contentItem?.type === 'tool_call' ||\n contentItem?.type === 'function_call'\n ) {\n toolCalls.push(contentItem as Record<string, unknown>);\n }\n }\n }\n return toolCalls;\n}\n\nfunction extractOutputItemText(item: OpenAIResponseOutputItem): string {\n if (!Array.isArray(item.content)) return '';\n let text = '';\n for (const c of item.content) {\n if (typeof c?.text === 'string') text += c.text;\n }\n return text;\n}\n\nexport function extractToolDefinitions(\n params: Record<string, unknown>,\n): Array<Record<string, unknown>> | undefined {\n const tools = params.tools;\n return Array.isArray(tools) && tools.length > 0\n ? (tools as Array<Record<string, unknown>>)\n : undefined;\n}\n\nfunction extractResponsesToolDefinitions(\n params: Record<string, unknown>,\n): Array<Record<string, unknown>> | undefined {\n const tools = params.tools;\n return Array.isArray(tools) && tools.length > 0\n ? (tools as Array<Record<string, unknown>>)\n : undefined;\n}\n\nfunction extractResponsesUserInputs(input: unknown): string[] {\n if (typeof input === 'string') return [input];\n if (!Array.isArray(input)) return [];\n\n const result: string[] = [];\n for (const entry of input as OpenAIResponseInput[]) {\n if (typeof entry === 'string') {\n result.push(entry);\n continue;\n }\n const role = entry.role;\n if (role !== 'user') continue;\n const content = entry.content;\n if (typeof content === 'string') {\n result.push(content);\n } else if (Array.isArray(content)) {\n for (const part of content) {\n if (typeof part?.text === 'string') result.push(part.text);\n }\n }\n }\n return result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"openai.js","names":["_OpenAIModule: Record<string, unknown> | null","clientOpts: Record<string, unknown>","costUsd: number | null","toolCalls: Array<Record<string, unknown>>","result: string[]"],"sources":["../../src/providers/openai.ts"],"sourcesContent":["/**\n * OpenAI provider wrapper with automatic tracking.\n *\n * Wraps the OpenAI client and instruments:\n * - chat.completions.create()\n * - responses.create()\n * to track AI response events via Amplitude.\n */\n\nimport type { PrivacyConfig } from '../core/privacy.js';\nimport { trackUserMessage } from '../core/tracking.js';\nimport { getDefaultPropagateContext, injectContext } from '../propagation.js';\nimport type {\n AmplitudeLike,\n AmplitudeOrAI,\n ChatCompletionResponse,\n ChatMessage,\n OpenAIResponse,\n OpenAIResponseInput,\n OpenAIResponseOutputItem,\n TrackFn,\n} from '../types.js';\nimport { calculateCost } from '../utils/costs.js';\nimport { tryRequire } from '../utils/resolve-module.js';\nimport { StreamingAccumulator } from '../utils/streaming.js';\nimport {\n applySessionContext,\n BaseAIProvider,\n contextFields,\n type ProviderTrackOptions,\n} from './base.js';\n\nconst _resolved = tryRequire('openai');\nexport const OPENAI_AVAILABLE = _resolved != null;\nconst _OpenAIModule: Record<string, unknown> | null = _resolved;\n\nexport { _OpenAIModule };\n\nexport interface OpenAIOptions {\n amplitude: AmplitudeOrAI;\n apiKey?: string;\n baseUrl?: string;\n privacyConfig?: PrivacyConfig | null;\n propagateContext?: boolean;\n /** Pass the `openai` module directly to bypass `tryRequire` (required in bundler environments). */\n openaiModule?: unknown;\n}\n\nexport class OpenAI<\n TClient extends Record<string, unknown> = Record<string, unknown>,\n> extends BaseAIProvider {\n private _client: TClient;\n readonly chat: WrappedChat;\n readonly responses: WrappedResponses;\n private _propagateContext: boolean;\n\n constructor(options: OpenAIOptions) {\n super({\n amplitude: options.amplitude,\n privacyConfig: options.privacyConfig,\n providerName: 'openai',\n });\n\n const mod =\n (options.openaiModule as Record<string, unknown> | null) ?? _OpenAIModule;\n if (mod == null) {\n throw new Error(\n 'openai package is required. Install it with: npm install openai — or pass the module directly via the openaiModule option.',\n );\n }\n\n const OpenAISDK = mod.OpenAI as new (\n opts: Record<string, unknown>,\n ) => unknown;\n\n const clientOpts: Record<string, unknown> = {};\n if (options.apiKey) clientOpts.apiKey = options.apiKey;\n if (options.baseUrl) clientOpts.baseURL = options.baseUrl;\n\n this._client = new OpenAISDK(clientOpts) as TClient;\n this._propagateContext =\n options.propagateContext ?? getDefaultPropagateContext();\n this.chat = new WrappedChat(\n this._client,\n this.trackFn(),\n this._amplitude,\n this._privacyConfig,\n this._propagateContext,\n );\n this.responses = new WrappedResponses(\n this._client,\n this.trackFn(),\n this._amplitude,\n this._privacyConfig,\n this._propagateContext,\n );\n }\n\n get client(): TClient {\n return this._client;\n }\n}\n\nexport class WrappedChat {\n readonly completions: WrappedCompletions;\n\n constructor(\n client: unknown,\n trackFn: TrackFn,\n amplitude: AmplitudeLike,\n privacyConfig: PrivacyConfig | null,\n propagateContext: boolean,\n ) {\n const clientObj = client as Record<string, unknown>;\n const chat = clientObj.chat as Record<string, unknown>;\n this.completions = new WrappedCompletions(\n chat.completions as Record<string, unknown>,\n trackFn,\n amplitude,\n privacyConfig,\n propagateContext,\n );\n }\n}\n\nexport class WrappedCompletions {\n _original: Record<string, unknown>;\n private _trackFn: TrackFn;\n private _amplitude: AmplitudeLike;\n private _privacyConfig: PrivacyConfig | null;\n private _propagateContext: boolean;\n private _providerName: string;\n\n constructor(\n completions: Record<string, unknown>,\n trackFn: TrackFn,\n amplitude: AmplitudeLike,\n privacyConfig: PrivacyConfig | null,\n propagateContext: boolean,\n providerName = 'openai',\n ) {\n this._original = completions;\n this._trackFn =\n typeof trackFn === 'function'\n ? trackFn\n : (trackFn as unknown as { trackFn(): TrackFn }).trackFn();\n this._amplitude = amplitude;\n this._privacyConfig = privacyConfig;\n this._propagateContext = propagateContext;\n this._providerName = providerName;\n }\n\n async create(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<ChatCompletionResponse | AsyncIterable<unknown>> {\n const createFn = this._original.create as (\n ...args: unknown[]\n ) => Promise<unknown>;\n const startTime = performance.now();\n let requestParams = this._withContextHeaders(params);\n const ctx = applySessionContext(amplitudeOverrides);\n\n if (requestParams.stream === true && requestParams.stream_options == null) {\n requestParams = {\n ...requestParams,\n stream_options: { include_usage: true },\n };\n }\n\n try {\n this._trackInputMessages(\n requestParams.messages as unknown,\n ctx,\n amplitudeOverrides?.trackInputMessages ?? true,\n );\n const response = await createFn.call(this._original, requestParams);\n\n if (requestParams.stream === true && _isAsyncIterable(response)) {\n return this._wrapStream(\n response as AsyncIterable<unknown>,\n requestParams,\n startTime,\n ctx,\n );\n }\n\n const latencyMs = performance.now() - startTime;\n\n const resp = response as ChatCompletionResponse;\n const usage = resp.usage;\n const choice = resp.choices?.[0];\n const modelName = String(resp.model ?? requestParams.model ?? 'unknown');\n const toolCalls = choice?.message?.tool_calls;\n\n const usageExt = usage as Record<string, unknown> | undefined;\n const promptDetails = usageExt?.prompt_tokens_details as\n | Record<string, number>\n | undefined;\n const completionDetails = usageExt?.completion_tokens_details as\n | Record<string, number>\n | undefined;\n const reasoningTokens = completionDetails?.reasoning_tokens;\n const cachedTokens = promptDetails?.cached_tokens;\n\n let costUsd: number | null = null;\n if (usage?.prompt_tokens != null && usage?.completion_tokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: usage.prompt_tokens,\n outputTokens: usage.completion_tokens,\n reasoningTokens: reasoningTokens ?? 0,\n cacheReadInputTokens: cachedTokens ?? 0,\n defaultProvider: 'openai',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(ctx),\n modelName,\n provider: this._providerName,\n responseContent: String(choice?.message?.content ?? ''),\n latencyMs,\n inputTokens: usage?.prompt_tokens,\n outputTokens: usage?.completion_tokens,\n totalTokens: usage?.total_tokens,\n reasoningTokens,\n cacheReadInputTokens: cachedTokens,\n totalCostUsd: costUsd,\n finishReason: choice?.finish_reason,\n toolCalls: toolCalls ?? undefined,\n isStreaming: false,\n toolDefinitions: extractToolDefinitions(requestParams),\n systemPrompt: extractSystemPrompt(requestParams),\n temperature: requestParams.temperature as number | undefined,\n maxOutputTokens: requestParams.max_tokens as number | undefined,\n topP: requestParams.top_p as number | undefined,\n });\n\n return response as ChatCompletionResponse;\n } catch (error) {\n const latencyMs = performance.now() - startTime;\n this._trackFn({\n ...contextFields(ctx),\n modelName: String(requestParams.model ?? 'unknown'),\n provider: this._providerName,\n responseContent: '',\n latencyMs,\n isError: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n }\n }\n\n async parse(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<ChatCompletionResponse | AsyncIterable<unknown>> {\n const parseFn = this._original.parse as\n | ((...args: unknown[]) => Promise<unknown>)\n | undefined;\n if (typeof parseFn !== 'function') {\n throw new Error('OpenAI SDK does not expose chat.completions.parse');\n }\n const originalCreate = this._original.create;\n this._original.create = parseFn;\n try {\n return await this.create(params, amplitudeOverrides);\n } finally {\n this._original.create = originalCreate;\n }\n }\n\n private async *_wrapStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n _startTime: number,\n sessionCtx: ProviderTrackOptions,\n ): AsyncGenerator<unknown> {\n const accumulator = new StreamingAccumulator();\n accumulator.model = String(params.model ?? 'unknown');\n let reasoningContent = '';\n\n try {\n for await (const chunk of stream) {\n const c = chunk as Record<string, unknown>;\n const choices = c.choices as Array<Record<string, unknown>> | undefined;\n const delta = choices?.[0]?.delta as\n | Record<string, unknown>\n | undefined;\n\n if (delta?.content != null) {\n accumulator.addContent(String(delta.content));\n }\n\n const deltaToolCalls = delta?.tool_calls as\n | Array<Record<string, unknown>>\n | undefined;\n if (Array.isArray(deltaToolCalls)) {\n for (const call of deltaToolCalls) {\n const idx = call.index as number | undefined;\n const id = call.id as string | undefined;\n const fn = call.function as Record<string, unknown> | undefined;\n if (idx != null && id && fn?.name != null) {\n accumulator.setToolCallAt(idx, {\n type: 'function',\n id,\n function: {\n name: fn.name,\n arguments: ((fn.arguments as string) ?? ''),\n },\n });\n } else if (idx != null && fn?.arguments) {\n accumulator.appendToolCallArgs(idx, fn.arguments as string);\n } else {\n accumulator.addToolCall(call);\n }\n }\n }\n\n if (delta?.reasoning_content != null) {\n reasoningContent += String(delta.reasoning_content);\n }\n\n const finishReason = choices?.[0]?.finish_reason;\n if (finishReason != null) {\n accumulator.finishReason = String(finishReason);\n }\n\n const usage = c.usage as Record<string, unknown> | undefined;\n if (usage != null) {\n const promptDetails = usage.prompt_tokens_details as\n | Record<string, number>\n | undefined;\n const completionDetails = usage.completion_tokens_details as\n | Record<string, number>\n | undefined;\n\n accumulator.setUsage({\n inputTokens: usage.prompt_tokens as number | undefined,\n outputTokens: usage.completion_tokens as number | undefined,\n totalTokens: usage.total_tokens as number | undefined,\n reasoningTokens: completionDetails?.reasoning_tokens,\n cacheReadTokens: promptDetails?.cached_tokens,\n });\n }\n\n yield chunk;\n }\n } catch (error) {\n accumulator.setError(\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n } finally {\n const state = accumulator.getState();\n const modelName = String(accumulator.model ?? params.model ?? 'unknown');\n\n let costUsd: number | null = null;\n if (state.inputTokens != null && state.outputTokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n reasoningTokens: state.reasoningTokens ?? 0,\n cacheReadInputTokens: state.cacheReadTokens ?? 0,\n defaultProvider: 'openai',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(sessionCtx),\n modelName,\n provider: this._providerName,\n responseContent: state.content,\n latencyMs: accumulator.elapsedMs,\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n totalTokens: state.totalTokens,\n reasoningTokens: state.reasoningTokens,\n cacheReadInputTokens: state.cacheReadTokens,\n totalCostUsd: costUsd,\n finishReason: state.finishReason,\n toolCalls: state.toolCalls.length > 0 ? state.toolCalls : undefined,\n providerTtfbMs: state.ttfbMs,\n isStreaming: true,\n isError: state.isError,\n errorMessage: state.errorMessage,\n reasoningContent: reasoningContent || undefined,\n toolDefinitions: extractToolDefinitions(params),\n systemPrompt: extractSystemPrompt(params),\n temperature: params.temperature as number | undefined,\n maxOutputTokens: params.max_tokens as number | undefined,\n topP: params.top_p as number | undefined,\n });\n }\n }\n\n private _withContextHeaders(\n params: Record<string, unknown>,\n ): Record<string, unknown> {\n if (!this._propagateContext) return params;\n const existing = (params.extra_headers ?? params.headers) as\n | Record<string, string>\n | undefined;\n const injected = injectContext(existing);\n return {\n ...params,\n extra_headers: injected,\n };\n }\n\n private _trackInputMessages(\n messages: unknown,\n ctx: ProviderTrackOptions,\n shouldTrackInputMessages: boolean,\n ): void {\n if (!shouldTrackInputMessages) return;\n if (ctx.userId == null || ctx.sessionId == null) return;\n if (!Array.isArray(messages)) return;\n\n const msgs = messages as ChatMessage[];\n\n // In agent loops each create() re-sends the full conversation history.\n // Only track user messages appended *after* the last assistant/tool\n // reply so the same message isn't autotracked on every iteration.\n const lastReplyIdx = msgs.findLastIndex(\n (m) => m?.role === 'assistant' || m?.role === 'tool',\n );\n const newMessages = msgs.slice(lastReplyIdx + 1);\n\n for (const msg of newMessages) {\n if (msg?.role !== 'user') continue;\n const content = msg.content;\n if (typeof content !== 'string' || content.length === 0) continue;\n trackUserMessage({\n amplitude: this._amplitude,\n userId: ctx.userId,\n messageContent: content,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n turnId: ctx.turnId ?? undefined,\n messageSource: ctx.parentAgentId ? 'agent' : 'user',\n agentId: ctx.agentId,\n parentAgentId: ctx.parentAgentId,\n customerOrgId: ctx.customerOrgId,\n agentVersion: ctx.agentVersion,\n context: ctx.context,\n env: ctx.env,\n groups: ctx.groups,\n eventProperties: ctx.eventProperties,\n privacyConfig: this._privacyConfig,\n });\n }\n }\n}\n\nexport class WrappedResponses {\n _original: Record<string, unknown>;\n private _trackFn: TrackFn;\n private _amplitude: AmplitudeLike;\n private _privacyConfig: PrivacyConfig | null;\n private _propagateContext: boolean;\n private _providerName: string;\n\n constructor(\n client: unknown,\n trackFn: TrackFn,\n amplitude: AmplitudeLike,\n privacyConfig: PrivacyConfig | null,\n propagateContext: boolean,\n providerName = 'openai',\n ) {\n const clientObj = client as Record<string, unknown>;\n const responses = (clientObj.responses ?? {}) as Record<string, unknown>;\n this._original = responses;\n this._trackFn =\n typeof trackFn === 'function'\n ? trackFn\n : (trackFn as unknown as { trackFn(): TrackFn }).trackFn();\n this._amplitude = amplitude;\n this._privacyConfig = privacyConfig;\n this._propagateContext = propagateContext;\n this._providerName = providerName;\n }\n\n async create(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<OpenAIResponse | AsyncIterable<unknown>> {\n const createFn = this._original.create as (\n ...args: unknown[]\n ) => Promise<unknown>;\n const startTime = performance.now();\n const requestParams = this._withContextHeaders(params);\n const ctx = applySessionContext(amplitudeOverrides);\n\n try {\n this._trackInputMessages(\n requestParams.input as unknown,\n ctx,\n amplitudeOverrides?.trackInputMessages ?? true,\n );\n const response = await createFn.call(this._original, requestParams);\n if (requestParams.stream === true && _isAsyncIterable(response)) {\n return this._wrapStream(\n response as AsyncIterable<unknown>,\n requestParams,\n startTime,\n ctx,\n );\n }\n\n const latencyMs = performance.now() - startTime;\n const resp = response as OpenAIResponse;\n const usage = resp.usage;\n const responseText = extractResponsesText(resp);\n const responseToolCalls = extractResponsesToolCalls(resp);\n const modelName = String(resp.model ?? requestParams.model ?? 'unknown');\n\n let costUsd: number | null = null;\n if (usage?.input_tokens != null && usage?.output_tokens != null) {\n try {\n costUsd = calculateCost({\n modelName,\n inputTokens: usage.input_tokens,\n outputTokens: usage.output_tokens,\n reasoningTokens: usage.output_tokens_details?.reasoning_tokens ?? 0,\n defaultProvider: 'openai',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n\n this._trackFn({\n ...contextFields(ctx),\n modelName,\n provider: this._providerName,\n responseContent: responseText,\n latencyMs,\n inputTokens: usage?.input_tokens,\n outputTokens: usage?.output_tokens,\n totalTokens: usage?.total_tokens,\n reasoningTokens: usage?.output_tokens_details?.reasoning_tokens,\n totalCostUsd: costUsd,\n finishReason: extractResponsesFinishReason(resp),\n toolCalls: responseToolCalls.length > 0 ? responseToolCalls : undefined,\n isStreaming: false,\n toolDefinitions: extractResponsesToolDefinitions(requestParams),\n systemPrompt: extractResponsesSystemPrompt(requestParams),\n temperature: requestParams.temperature as number | undefined,\n maxOutputTokens: requestParams.max_output_tokens as number | undefined,\n topP: requestParams.top_p as number | undefined,\n });\n\n return response as OpenAIResponse;\n } catch (error) {\n const latencyMs = performance.now() - startTime;\n this._trackFn({\n ...contextFields(ctx),\n modelName: String(requestParams.model ?? 'unknown'),\n provider: this._providerName,\n responseContent: '',\n latencyMs,\n isError: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n });\n throw error;\n }\n }\n\n async stream(\n params: Record<string, unknown>,\n amplitudeOverrides?: ProviderTrackOptions,\n ): Promise<AsyncIterable<unknown>> {\n const streamFn = this._original.stream as\n | ((...args: unknown[]) => Promise<unknown>)\n | undefined;\n if (typeof streamFn !== 'function') {\n throw new Error('OpenAI SDK does not expose responses.stream');\n }\n const startTime = performance.now();\n const requestParams = this._withContextHeaders(params);\n const ctx = applySessionContext(amplitudeOverrides);\n\n try {\n this._trackInputMessages(\n requestParams.input as unknown,\n ctx,\n amplitudeOverrides?.trackInputMessages ?? true,\n );\n const response = await streamFn.call(this._original, requestParams);\n if (!_isAsyncIterable(response)) {\n throw new Error('OpenAI responses.stream did not return AsyncIterable');\n }\n return this._wrapStream(\n response as AsyncIterable<unknown>,\n requestParams,\n startTime,\n ctx,\n );\n } catch (error) {\n this._trackFn({\n ...contextFields(ctx),\n modelName: String(requestParams.model ?? 'unknown'),\n provider: this._providerName,\n responseContent: '',\n latencyMs: performance.now() - startTime,\n isError: true,\n errorMessage: error instanceof Error ? error.message : String(error),\n isStreaming: true,\n });\n throw error;\n }\n }\n\n private async *_wrapStream(\n stream: AsyncIterable<unknown>,\n params: Record<string, unknown>,\n _startTime: number,\n sessionCtx: ProviderTrackOptions,\n ): AsyncGenerator<unknown> {\n const accumulator = new StreamingAccumulator();\n accumulator.model = String(params.model ?? 'unknown');\n\n try {\n for await (const event of stream) {\n const e = event as Record<string, unknown>;\n const type = e.type as string | undefined;\n if (type === 'response.output_text.delta') {\n const delta = e.delta;\n if (typeof delta === 'string') accumulator.addContent(delta);\n } else if (type === 'response.completed') {\n const response = e.response as OpenAIResponse | undefined;\n if (response != null) {\n const outputText = extractResponsesText(response);\n if (outputText.length > 0) {\n accumulator.content = outputText;\n }\n const usage = response.usage;\n accumulator.setUsage({\n inputTokens: usage?.input_tokens,\n outputTokens: usage?.output_tokens,\n totalTokens: usage?.total_tokens,\n reasoningTokens: usage?.output_tokens_details?.reasoning_tokens,\n });\n const finishReason = extractResponsesFinishReason(response);\n if (finishReason != null) accumulator.finishReason = finishReason;\n }\n }\n yield event;\n }\n } catch (error) {\n accumulator.setError(\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n } finally {\n const state = accumulator.getState();\n let costUsd: number | null = null;\n if (state.inputTokens != null && state.outputTokens != null) {\n try {\n costUsd = calculateCost({\n modelName: String(accumulator.model ?? params.model ?? 'unknown'),\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n reasoningTokens: state.reasoningTokens ?? 0,\n defaultProvider: 'openai',\n });\n } catch {\n // cost calculation is best-effort\n }\n }\n this._trackFn({\n ...contextFields(sessionCtx),\n modelName: String(accumulator.model ?? params.model ?? 'unknown'),\n provider: this._providerName,\n responseContent: state.content,\n latencyMs: accumulator.elapsedMs,\n inputTokens: state.inputTokens,\n outputTokens: state.outputTokens,\n totalTokens: state.totalTokens,\n reasoningTokens: state.reasoningTokens,\n totalCostUsd: costUsd,\n finishReason: state.finishReason,\n providerTtfbMs: state.ttfbMs,\n isStreaming: true,\n isError: state.isError,\n errorMessage: state.errorMessage,\n toolDefinitions: extractResponsesToolDefinitions(params),\n systemPrompt: extractResponsesSystemPrompt(params),\n temperature: params.temperature as number | undefined,\n maxOutputTokens: params.max_output_tokens as number | undefined,\n topP: params.top_p as number | undefined,\n });\n }\n }\n\n private _withContextHeaders(\n params: Record<string, unknown>,\n ): Record<string, unknown> {\n if (!this._propagateContext) return params;\n const existing = (params.extra_headers ?? params.headers) as\n | Record<string, string>\n | undefined;\n const injected = injectContext(existing);\n return {\n ...params,\n extra_headers: injected,\n };\n }\n\n private _trackInputMessages(\n input: unknown,\n ctx: ProviderTrackOptions,\n shouldTrackInputMessages: boolean,\n ): void {\n if (!shouldTrackInputMessages) return;\n if (ctx.userId == null || ctx.sessionId == null) return;\n for (const text of extractResponsesUserInputs(input)) {\n if (!text) continue;\n trackUserMessage({\n amplitude: this._amplitude,\n userId: ctx.userId,\n messageContent: text,\n sessionId: ctx.sessionId,\n traceId: ctx.traceId,\n turnId: ctx.turnId ?? undefined,\n messageSource: ctx.parentAgentId ? 'agent' : 'user',\n agentId: ctx.agentId,\n parentAgentId: ctx.parentAgentId,\n customerOrgId: ctx.customerOrgId,\n agentVersion: ctx.agentVersion,\n context: ctx.context,\n env: ctx.env,\n groups: ctx.groups,\n eventProperties: ctx.eventProperties,\n privacyConfig: this._privacyConfig,\n });\n }\n }\n}\n\nfunction _isAsyncIterable(value: unknown): value is AsyncIterable<unknown> {\n return (\n value != null &&\n typeof (value as Record<symbol, unknown>)[Symbol.asyncIterator] ===\n 'function'\n );\n}\n\nexport function extractSystemPrompt(\n params: Record<string, unknown>,\n): string | undefined {\n const messages = params.messages as\n | Array<Record<string, unknown>>\n | undefined;\n if (!messages?.length) return undefined;\n const systemMsg = messages.find(\n (m) => m.role === 'system' || m.role === 'developer',\n );\n return systemMsg ? String(systemMsg.content ?? '') : undefined;\n}\n\nfunction extractResponsesSystemPrompt(\n params: Record<string, unknown>,\n): string | undefined {\n const instructions = params.instructions;\n return typeof instructions === 'string' ? instructions : undefined;\n}\n\nfunction extractResponsesFinishReason(\n resp: OpenAIResponse,\n): string | undefined {\n const status = resp.status;\n if (typeof status === 'string' && status.length > 0) return status;\n const out = resp.output?.[0];\n if (out != null && typeof out.status === 'string') return out.status;\n return undefined;\n}\n\nfunction extractResponsesText(resp: OpenAIResponse): string {\n if (typeof resp.output_text === 'string') return resp.output_text;\n const outputs = resp.output ?? [];\n let text = '';\n for (const item of outputs) {\n text += extractOutputItemText(item);\n }\n return text;\n}\n\nfunction extractResponsesToolCalls(\n resp: OpenAIResponse,\n): Array<Record<string, unknown>> {\n const toolCalls: Array<Record<string, unknown>> = [];\n const outputs = resp.output ?? [];\n for (const item of outputs) {\n if (!Array.isArray(item.content)) continue;\n for (const contentItem of item.content) {\n if (\n contentItem?.type === 'tool_call' ||\n contentItem?.type === 'function_call'\n ) {\n toolCalls.push(contentItem as Record<string, unknown>);\n }\n }\n }\n return toolCalls;\n}\n\nfunction extractOutputItemText(item: OpenAIResponseOutputItem): string {\n if (!Array.isArray(item.content)) return '';\n let text = '';\n for (const c of item.content) {\n if (typeof c?.text === 'string') text += c.text;\n }\n return text;\n}\n\nexport function extractToolDefinitions(\n params: Record<string, unknown>,\n): Array<Record<string, unknown>> | undefined {\n const tools = params.tools;\n return Array.isArray(tools) && tools.length > 0\n ? (tools as Array<Record<string, unknown>>)\n : undefined;\n}\n\nfunction extractResponsesToolDefinitions(\n params: Record<string, unknown>,\n): Array<Record<string, unknown>> | undefined {\n const tools = params.tools;\n return Array.isArray(tools) && tools.length > 0\n ? (tools as Array<Record<string, unknown>>)\n : undefined;\n}\n\nfunction extractResponsesUserInputs(input: unknown): string[] {\n if (typeof input === 'string') return [input];\n if (!Array.isArray(input)) return [];\n\n const entries = input as OpenAIResponseInput[];\n\n // In agent loops the full input array is re-sent each iteration.\n // Only extract user inputs after the last assistant/function reply\n // so the same message isn't autotracked on every create() call.\n const lastReplyIdx = entries.findLastIndex((e) => {\n if (typeof e === 'string') return false;\n return e.role === 'assistant' || e.type === 'function_call' || e.type === 'function_call_output';\n });\n const newEntries = entries.slice(lastReplyIdx + 1);\n\n const result: string[] = [];\n for (const entry of newEntries) {\n if (typeof entry === 'string') {\n result.push(entry);\n continue;\n }\n const role = entry.role;\n if (role !== 'user') continue;\n const content = entry.content;\n if (typeof content === 'string') {\n result.push(content);\n } else if (Array.isArray(content)) {\n for (const part of content) {\n if (typeof part?.text === 'string') result.push(part.text);\n }\n }\n }\n return result;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","names":[],"sources":["../../src/utils/logger.ts"],"sourcesContent":[],"mappings":";UAAiB,MAAA;EAAA,KAAA,CAAA,OAAM,EAAA,MAAA,CAAA,EAAA,IAAA;EAcP,KAAA,CAAA,OAAS,EAAA,MAAA,CAAA,EAAuB,IAAA;;;;iBAAhC,SAAA,uBAAgC"}
1
+ {"version":3,"file":"logger.d.ts","names":[],"sources":["../../src/utils/logger.ts"],"sourcesContent":[],"mappings":";UAAiB,MAAA;EAAA,KAAA,CAAA,OAAM,EAAA,MAAA,CAAA,EAAA,IAAA;EAcP,KAAA,CAAA,OAAS,EAAA,MAAA,CAAA,EAAA,IAAuB;;;;iBAAhC,SAAA,uBAAgC"}
package/llms-full.txt CHANGED
@@ -1,5 +1,5 @@
1
1
  # llms-full.txt
2
- # @amplitude/ai 0.3.5 — Complete API Reference for AI Coding Agents
2
+ # @amplitude/ai 0.3.7 — Complete API Reference for AI Coding Agents
3
3
  #
4
4
  # This file is the definitive guide for any coding agent instrumenting
5
5
  # a JavaScript/TypeScript AI application with @amplitude/ai.
@@ -67,6 +67,7 @@ const ai = new AmplitudeAI({
67
67
 
68
68
  ### patch(options) / unpatch()
69
69
  Zero-code instrumentation. Monkey-patches all detected provider SDKs.
70
+ Captures aggregate `[Agent] AI Response` events only — no user identity, no funnels. Use as a quick verification or for legacy codebases where you cannot modify call sites.
70
71
  ```
71
72
  import { patch, unpatch } from '@amplitude/ai';
72
73
  patch({ amplitudeAI: ai });
@@ -395,6 +396,8 @@ with code examples for every step. Read this for guided instrumentation.
395
396
 
396
397
  ## CLI
397
398
 
399
+ - `amplitude-ai` — Print instrumentation prompt for AI coding agents
400
+ - `amplitude-ai --print-guide` — Print the full amplitude-ai.md guide to stdout
398
401
  - `amplitude-ai mcp` — Start the MCP server for AI coding agents
399
402
  - `amplitude-ai doctor [--json] [--no-mock-check]` — Validate environment and event pipeline
400
403
  - `amplitude-ai status [--json]` — Show SDK version, installed providers, and env config
package/llms.txt CHANGED
@@ -1,7 +1,7 @@
1
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.3.5
4
+ version=0.3.7
5
5
 
6
6
  [mcp.tools]
7
7
  get_event_schema
package/mcp.schema.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "generated": true,
3
3
  "package": "@amplitude/ai",
4
- "version": "0.3.5",
4
+ "version": "0.3.7",
5
5
  "prompt": "instrument_app",
6
6
  "tools": [
7
7
  "get_event_schema",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amplitude/ai",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "private": false,
5
5
  "description": "Amplitude AI SDK - LLM usage tracking for Amplitude Analytics",
6
6
  "keywords": [