@chatsee/claude-agent-sdk 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,144 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # @chatsee/claude-agent-sdk
2
+
3
+ ChatSee connector for the **Claude Agent SDK** — Anthropic's agent framework,
4
+ the one that powers Claude Code.
5
+
6
+ ```bash
7
+ npm install @chatsee/claude-agent-sdk
8
+ ```
9
+
10
+ ```ts
11
+ import { instrumentClaudeAgentSdk } from '@chatsee/claude-agent-sdk';
12
+
13
+ const chatsee = instrumentClaudeAgentSdk({ agentId: '...', tenantId: '...' });
14
+
15
+ for await (const message of chatsee.query({ prompt: 'where is my order', options })) {
16
+ ...
17
+ }
18
+ ```
19
+
20
+ `chatsee-sdk` is installed for you as a dependency. This is the JS twin of the
21
+ Python connector `chatsee-claude-agent-sdk`; the two capture the same fields
22
+ the same way.
23
+
24
+ ## Not the same thing as `@chatsee/anthropic`
25
+
26
+ | | `@chatsee/anthropic` | this |
27
+ | --- | --- | --- |
28
+ | Package it hooks | `@anthropic-ai/sdk` | `@anthropic-ai/claude-agent-sdk` |
29
+ | Who writes the agent loop | the customer | Anthropic |
30
+ | What we hook | `client.messages.create` | the `query()` message stream |
31
+
32
+ Use `@chatsee/anthropic` when they call the Messages API and run their own
33
+ tool-use loop. Use this when they call `query()` and let the SDK run it.
34
+
35
+ ## Why a handle rather than a one-liner
36
+
37
+ There is no client object to patch — the entry point is a module-level
38
+ `query()`. So `instrumentClaudeAgentSdk` returns a handle whose `.query` is a
39
+ drop-in: every message still reaches the caller, unchanged and in order, while
40
+ the turn is assembled alongside and sent when the terminal `result` message
41
+ arrives.
42
+
43
+ The returned stream is still the SDK's `Query`: `interrupt()`,
44
+ `setPermissionMode()` and the rest are forwarded, so nothing about how the
45
+ agent is driven changes.
46
+
47
+ The Python connector also replaces `claude_agent_sdk.query` on the module so
48
+ code calling it through the module needs no change. That is not possible in
49
+ JS — module exports are read-only — so the handle is the integration:
50
+
51
+ ```ts
52
+ // before
53
+ import { query } from '@anthropic-ai/claude-agent-sdk';
54
+ for await (const m of query({ prompt, options })) { ... }
55
+
56
+ // after
57
+ const chatsee = instrumentClaudeAgentSdk({ agentId, tenantId });
58
+ for await (const m of chatsee.query({ prompt, options })) { ... }
59
+ ```
60
+
61
+ ## What it captures
62
+
63
+ Per turn: the prompt, the final answer, the system prompt (`options.systemPrompt`,
64
+ a string or the `append` of a preset), every tool call with its arguments and
65
+ result, and **one model call per model**, with tokens, cached tokens and a real
66
+ dollar cost.
67
+
68
+ This SDK is the only one that states its own cost (`total_cost_usd`), so that
69
+ figure is passed through rather than estimated. Nothing here invents a price,
70
+ and a turn's cost is never divided across models — a split we made up would
71
+ look like measurement. When the SDK breaks usage down per model (`modelUsage`)
72
+ each model becomes its own call with its own `costUSD`.
73
+
74
+ Errored turns (`error_max_turns`, `error_during_execution`, ...) are still
75
+ recorded, with the subtype logged as the exception and the assistant's last
76
+ text as the answer.
77
+
78
+ ## Three things this framework does that nothing else does
79
+
80
+ **The CLI's system prompt is enormous.** Claude Code injects roughly 21k cached
81
+ tokens on every call, so a one-line "hello" costs around $0.027. Those land in
82
+ `cache_read_input_tokens`, not `input_tokens` — both are captured, or the turn
83
+ looks far cheaper than the bill.
84
+
85
+ **The CLI calls its own tools.** It loads tools on demand and emits a
86
+ `ToolSearch` tool-use alongside the agent's real one. Those are filtered: a
87
+ support agent that never touched a search tool must not show one in its trace.
88
+
89
+ **Tool names arrive MCP-qualified.** Custom tools reach the model as
90
+ `mcp__<server>__<tool>`. The customer named it `get_order_status`, so the
91
+ prefix is stripped and that is what the screens say.
92
+
93
+ ## JS specifics
94
+
95
+ * The API message sits under `msg.message`: content blocks and the model are
96
+ read from `msg.message.content` / `msg.message.model`, tool results from
97
+ `user` messages' `tool_result` blocks.
98
+ * `permissionMode: 'bypassPermissions'` also needs
99
+ `allowDangerouslySkipPermissions: true` in JS (Python does not).
100
+ * The SDK peer-requires **zod v4**; custom tools built with `tool()` should
101
+ import from `zod` v4 (or `zod/v4` on a 3.25+ install).
102
+ * The SDK ships as ES modules. `require()` of it works on Node 20.19+ / 22.12+;
103
+ on older runtimes the connector falls back to a dynamic `import()`, and the
104
+ only visible difference is that the first `query()` resolves the module.
105
+ * Streaming-input mode (an async iterable of user messages as `prompt`) is
106
+ supported: the connector watches the iterable go by to pick up the user
107
+ text, since the SDK does not echo it back.
108
+
109
+ ## Guarantees
110
+
111
+ * **Fails open.** A failure inside capture is logged and swallowed.
112
+ * **Never consumes the stream.** Every message is forwarded unchanged, in order.
113
+ * **Idempotent.** Instrumenting twice does not capture twice.
114
+ * **Concurrency-safe.** State is per `query()` call, not per handle, and emits
115
+ to one tracker are serialised so concurrent queries cannot clobber each other
116
+ across the tracker's own `await`.
117
+
118
+ All four are enforced by the contract suite, not just intended.
119
+
120
+ ## Tests
121
+
122
+ No network, no API key, no CLI subprocess — the SDK is stubbed, ChatSee is not.
123
+ That matters more here than anywhere else: a real turn costs about $0.027, so
124
+ finding a capture bug by running one would be slow and expensive.
125
+
126
+ ```bash
127
+ cd js/connectors/_shared && npm install && npm run build # the test kit
128
+ cd ../ai_sdk/claude-agent-sdk && npm install && npm test
129
+ ```
130
+
131
+ A live check against real Claude is in `examples/supportAgent.ts`:
132
+
133
+ ```bash
134
+ ANTHROPIC_API_KEY=... CHATSEE_AGENT_ID=... CHATSEE_TENANT_ID=... CHATSEE_ENV=dev \
135
+ npx tsx examples/supportAgent.ts
136
+ ```
137
+
138
+ ## Known gap
139
+
140
+ Turn-level `duration_ms` comes from the tracker's own wall clock, and all four
141
+ tracker calls are made together once the turn is known to be complete — so it
142
+ reads near zero. The honest latency is on the model call, from the SDK's
143
+ `duration_api_ms`. Fixing it properly means letting `endTurn` accept a measured
144
+ duration, which is a core-SDK change and not this connector's to make.
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Reading one Claude Agent SDK turn off its message stream.
3
+ *
4
+ * `query()` yields `system` (init) -> `assistant` (text, tool-use and thinking
5
+ * blocks) -> `user` (tool results) -> a terminal `result` carrying the final
6
+ * text, usage, cost and session id.
7
+ *
8
+ * So a turn is assembled as the stream is consumed and emitted when the
9
+ * `result` message arrives. Everything here is extraction; nothing builds a
10
+ * payload, opens a socket or knows a URL.
11
+ *
12
+ * Two quirks of this framework, both discovered in the POC and both handled
13
+ * here rather than left to show up in a customer's analytics:
14
+ *
15
+ * - The CLI loads tools on demand and emits its own meta-tool calls alongside
16
+ * the agent's real ones. Reporting those would mean a support agent that
17
+ * never touched a search tool shows one in its trace.
18
+ * - Custom tools are exposed over in-process MCP as `mcp__<server>__<tool>`.
19
+ * The customer named the tool `get_order_status`, so that is what the
20
+ * screens should say.
21
+ *
22
+ * Mirrors `chatsee_claude_agent_sdk/_capture.py` in the Python connector. The
23
+ * one JS-specific difference: the API message sits under `msg.message`, so
24
+ * content blocks and the model are read from there.
25
+ */
26
+ import { AssistantLike, ResultLike, UserLike } from './types';
27
+ export declare const FRAMEWORK = "claude-agent-sdk";
28
+ /**
29
+ * Meta-tools the Claude Code CLI uses for its own bookkeeping. They are not
30
+ * actions the customer's agent took, so they never reach the tracker.
31
+ */
32
+ export declare const INTERNAL_TOOLS: ReadonlySet<string>;
33
+ /**
34
+ * `mcp__support__get_order_status` -> `get_order_status`.
35
+ *
36
+ * Custom tools reach the model over in-process MCP, which qualifies the name
37
+ * with its server. The customer wrote `get_order_status`; reporting the
38
+ * qualified form would make the same tool look different here than in every
39
+ * other connector.
40
+ */
41
+ export declare function cleanToolName(name: unknown): string;
42
+ export declare function isInternalTool(name: unknown): boolean;
43
+ /** A tool result as the SDK hands it over: text blocks, a string, or an object. */
44
+ export declare function toolResultValue(content: unknown): unknown;
45
+ /**
46
+ * The system prompt, which lives on the caller's options rather than the
47
+ * stream. It may be a plain string, a string array, a custom object or a
48
+ * preset object with an append.
49
+ */
50
+ export declare function systemPromptOf(options: unknown): string | undefined;
51
+ export interface ModelCallEntry {
52
+ model?: string;
53
+ prompt_tokens?: number;
54
+ completion_tokens?: number;
55
+ cached_tokens?: number;
56
+ cost_usd?: number;
57
+ }
58
+ /**
59
+ * Token counts off a result message's aggregate `usage` (snake_case, the
60
+ * Anthropic API shape).
61
+ *
62
+ * Reported as the SDK reports them. `input_tokens` / `output_tokens` are the
63
+ * turn's non-cached tokens; the cache figures are separate, and the CLI's own
64
+ * ~21k-token system prompt lands in the cache reads rather than the input.
65
+ */
66
+ export declare function usageCounts(usage: unknown): ModelCallEntry;
67
+ /**
68
+ * One entry per model the turn used.
69
+ *
70
+ * `modelUsage` breaks usage down per model, which is the grain the tracker
71
+ * wants: a turn that used Haiku for a sub-agent and Sonnet for the answer
72
+ * should not report one blended number. When it is absent the aggregate
73
+ * `usage` is the fallback.
74
+ */
75
+ export declare function modelCallsFrom(result: ResultLike): {
76
+ entries: ModelCallEntry[];
77
+ cumulative: boolean;
78
+ };
79
+ /**
80
+ * Turns the SDK's running totals into one turn's own usage.
81
+ *
82
+ * `modelUsage` and `total_cost_usd` are **cumulative for the query() call**:
83
+ * each result carries the running total so far, not that turn's share. One
84
+ * turn per call -- a plain string prompt -- makes the two identical, which is
85
+ * why this is invisible in the common case. A streaming-input session or a
86
+ * resumed one runs several turns through the same total, and recording it
87
+ * as-is makes turn three report turns one, two and three. Cost and tokens
88
+ * then inflate with every turn.
89
+ *
90
+ * (The top-level `usage` beside them is already per-turn. It is not
91
+ * differenced, and must not be.)
92
+ *
93
+ * Two cases this deliberately does not pretend to solve:
94
+ *
95
+ * - **A session seen for the first time** is recorded as-is, because there is
96
+ * nothing to subtract. A *resumed* session's first result already carries
97
+ * the turns from before we were watching, so that one turn over-reports.
98
+ * Every turn after it is right, which is the best available without the
99
+ * transcript.
100
+ * - **A mid-session `/clear`** resets the SDK's running total. That shows up
101
+ * as a total smaller than the one before it, so it is taken as a new
102
+ * baseline rather than emitted as a negative.
103
+ */
104
+ export declare class UsageLedger {
105
+ private readonly maxSessions;
106
+ /** Bounded so a long-lived process cannot accumulate sessions forever. */
107
+ static readonly MAX_SESSIONS = 512;
108
+ private readonly totals;
109
+ constructor(maxSessions?: number);
110
+ private difference;
111
+ /** Per-model entries from `modelUsage`, differenced. */
112
+ perTurnModels(sessionId: string | undefined, entries: ModelCallEntry[]): ModelCallEntry[];
113
+ /**
114
+ * `total_cost_usd`, differenced. Used on the aggregate path, where the
115
+ * tokens come from the per-turn `usage` but the cost does not.
116
+ */
117
+ perTurnCost(sessionId: string | undefined, totalCostUsd: unknown): number | undefined;
118
+ get trackedSessions(): number;
119
+ }
120
+ export interface ResolvedToolCall {
121
+ tool_name: string;
122
+ arguments: Record<string, unknown>;
123
+ result: unknown;
124
+ }
125
+ /**
126
+ * One turn, assembled as its stream is consumed.
127
+ *
128
+ * An instance per `query()` call, never shared, so concurrent queries cannot
129
+ * contaminate each other.
130
+ */
131
+ export declare class TurnAccumulator {
132
+ model: string | undefined;
133
+ private readonly toolCalls;
134
+ private readonly toolResults;
135
+ private readonly seenToolIds;
136
+ private readonly texts;
137
+ observeAssistant(message: AssistantLike): void;
138
+ observeUser(message: UserLike): void;
139
+ /** Tool calls paired with their results, ready for the tracker. */
140
+ resolvedToolCalls(): ResolvedToolCall[];
141
+ /** Joined assistant text, for when the result message carries no `result`. */
142
+ assistantText(): string;
143
+ }
144
+ //# sourceMappingURL=capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE9D,eAAO,MAAM,SAAS,qBAAqB,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAAwC,CAAC;AAIxF;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAOnD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAErD;AAeD,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAezD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAoBnE;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAM1D;AAYD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG;IAClD,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB,CAgBA;AAYD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBAAa,WAAW;IASV,OAAO,CAAC,QAAQ,CAAC,WAAW;IARxC,0EAA0E;IAC1E,MAAM,CAAC,QAAQ,CAAC,YAAY,OAAO;IAEnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGnB;gBAEyB,WAAW,GAAE,MAAiC;IAE3E,OAAO,CAAC,UAAU;IAkDlB,wDAAwD;IACxD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE;IAIzF;;;OAGG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS;IAKrF,IAAI,eAAe,IAAI,MAAM,CAE5B;CACF;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,MAAM,EAAE,OAAO,CAAC;CACjB;AAQD;;;;;GAKG;AACH,qBAAa,eAAe;IAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAyB;IACnD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsB;IAClD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IAEtC,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAgC9C,WAAW,CAAC,OAAO,EAAE,QAAQ,GAAG,IAAI;IAUpC,mEAAmE;IACnE,iBAAiB,IAAI,gBAAgB,EAAE;IAQvC,8EAA8E;IAC9E,aAAa,IAAI,MAAM;CAGxB"}
@@ -0,0 +1,326 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TurnAccumulator = exports.UsageLedger = exports.INTERNAL_TOOLS = exports.FRAMEWORK = void 0;
4
+ exports.cleanToolName = cleanToolName;
5
+ exports.isInternalTool = isInternalTool;
6
+ exports.toolResultValue = toolResultValue;
7
+ exports.systemPromptOf = systemPromptOf;
8
+ exports.usageCounts = usageCounts;
9
+ exports.modelCallsFrom = modelCallsFrom;
10
+ exports.FRAMEWORK = 'claude-agent-sdk';
11
+ /**
12
+ * Meta-tools the Claude Code CLI uses for its own bookkeeping. They are not
13
+ * actions the customer's agent took, so they never reach the tracker.
14
+ */
15
+ exports.INTERNAL_TOOLS = new Set(['ToolSearch', 'TodoWrite']);
16
+ const MCP_PREFIX = 'mcp__';
17
+ /**
18
+ * `mcp__support__get_order_status` -> `get_order_status`.
19
+ *
20
+ * Custom tools reach the model over in-process MCP, which qualifies the name
21
+ * with its server. The customer wrote `get_order_status`; reporting the
22
+ * qualified form would make the same tool look different here than in every
23
+ * other connector.
24
+ */
25
+ function cleanToolName(name) {
26
+ if (typeof name !== 'string')
27
+ return 'unknown';
28
+ if (name.startsWith(MCP_PREFIX)) {
29
+ const parts = name.split('__');
30
+ if (parts.length >= 3)
31
+ return parts.slice(2).join('__');
32
+ }
33
+ return name;
34
+ }
35
+ function isInternalTool(name) {
36
+ return typeof name === 'string' && exports.INTERNAL_TOOLS.has(name);
37
+ }
38
+ function field(obj, name) {
39
+ if (obj === null || obj === undefined || typeof obj !== 'object')
40
+ return undefined;
41
+ return obj[name];
42
+ }
43
+ function wholeNumber(value) {
44
+ return typeof value === 'number' && Number.isFinite(value) ? Math.trunc(value) : undefined;
45
+ }
46
+ function finiteNumber(value) {
47
+ return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
48
+ }
49
+ /** A tool result as the SDK hands it over: text blocks, a string, or an object. */
50
+ function toolResultValue(content) {
51
+ if (content === null || content === undefined)
52
+ return content;
53
+ if (typeof content !== 'object')
54
+ return content;
55
+ if (Array.isArray(content)) {
56
+ const texts = [];
57
+ for (const block of content) {
58
+ if (field(block, 'type') === 'text' || field(block, 'text') !== undefined) {
59
+ const text = field(block, 'text');
60
+ if (text)
61
+ texts.push(String(text));
62
+ }
63
+ }
64
+ if (texts.length > 0)
65
+ return texts.join('\n');
66
+ return content.map((b) => (typeof b === 'string' ? b : JSON.stringify(b)));
67
+ }
68
+ return content;
69
+ }
70
+ /**
71
+ * The system prompt, which lives on the caller's options rather than the
72
+ * stream. It may be a plain string, a string array, a custom object or a
73
+ * preset object with an append.
74
+ */
75
+ function systemPromptOf(options) {
76
+ const prompt = field(options, 'systemPrompt');
77
+ if (typeof prompt === 'string')
78
+ return prompt || undefined;
79
+ if (Array.isArray(prompt)) {
80
+ const joined = prompt.filter((p) => typeof p === 'string').join('\n');
81
+ return joined || undefined;
82
+ }
83
+ if (prompt !== null && prompt !== undefined) {
84
+ // Preset form: { type: 'preset', preset: 'claude_code', append?: '...' }
85
+ const append = field(prompt, 'append');
86
+ if (typeof append === 'string' && append)
87
+ return append;
88
+ // Custom form: { type: 'custom', prompt: '...' | ['...'] }
89
+ const custom = field(prompt, 'prompt');
90
+ if (typeof custom === 'string' && custom)
91
+ return custom;
92
+ if (Array.isArray(custom)) {
93
+ const joined = custom.filter((p) => typeof p === 'string').join('\n');
94
+ return joined || undefined;
95
+ }
96
+ }
97
+ return undefined;
98
+ }
99
+ /**
100
+ * Token counts off a result message's aggregate `usage` (snake_case, the
101
+ * Anthropic API shape).
102
+ *
103
+ * Reported as the SDK reports them. `input_tokens` / `output_tokens` are the
104
+ * turn's non-cached tokens; the cache figures are separate, and the CLI's own
105
+ * ~21k-token system prompt lands in the cache reads rather than the input.
106
+ */
107
+ function usageCounts(usage) {
108
+ return {
109
+ prompt_tokens: wholeNumber(field(usage, 'input_tokens')),
110
+ completion_tokens: wholeNumber(field(usage, 'output_tokens')),
111
+ cached_tokens: wholeNumber(field(usage, 'cache_read_input_tokens')),
112
+ };
113
+ }
114
+ /** Token counts off one `modelUsage` entry (camelCase, the SDK's own shape). */
115
+ function modelUsageCounts(usage) {
116
+ return {
117
+ prompt_tokens: wholeNumber(field(usage, 'inputTokens')),
118
+ completion_tokens: wholeNumber(field(usage, 'outputTokens')),
119
+ cached_tokens: wholeNumber(field(usage, 'cacheReadInputTokens')),
120
+ cost_usd: finiteNumber(field(usage, 'costUSD')),
121
+ };
122
+ }
123
+ /**
124
+ * One entry per model the turn used.
125
+ *
126
+ * `modelUsage` breaks usage down per model, which is the grain the tracker
127
+ * wants: a turn that used Haiku for a sub-agent and Sonnet for the answer
128
+ * should not report one blended number. When it is absent the aggregate
129
+ * `usage` is the fallback.
130
+ */
131
+ function modelCallsFrom(result) {
132
+ const perModel = result.modelUsage;
133
+ if (perModel && typeof perModel === 'object' && !Array.isArray(perModel)) {
134
+ const models = Object.keys(perModel);
135
+ if (models.length > 0) {
136
+ // modelUsage is a running total for the query() call, so the caller has
137
+ // to difference it against the previous result.
138
+ return {
139
+ entries: models.map((model) => ({ ...modelUsageCounts(perModel[model]), model })),
140
+ cumulative: true,
141
+ };
142
+ }
143
+ }
144
+ // The top-level usage is already this turn's own. Differencing it would be
145
+ // wrong, and would quietly zero every turn after the first.
146
+ return { entries: [usageCounts(result.usage)], cumulative: false };
147
+ }
148
+ /** Fields that arrive as a running total and have to be differenced. */
149
+ const CUMULATIVE_FIELDS = [
150
+ 'prompt_tokens',
151
+ 'completion_tokens',
152
+ 'cached_tokens',
153
+ 'cost_usd',
154
+ ];
155
+ /**
156
+ * Turns the SDK's running totals into one turn's own usage.
157
+ *
158
+ * `modelUsage` and `total_cost_usd` are **cumulative for the query() call**:
159
+ * each result carries the running total so far, not that turn's share. One
160
+ * turn per call -- a plain string prompt -- makes the two identical, which is
161
+ * why this is invisible in the common case. A streaming-input session or a
162
+ * resumed one runs several turns through the same total, and recording it
163
+ * as-is makes turn three report turns one, two and three. Cost and tokens
164
+ * then inflate with every turn.
165
+ *
166
+ * (The top-level `usage` beside them is already per-turn. It is not
167
+ * differenced, and must not be.)
168
+ *
169
+ * Two cases this deliberately does not pretend to solve:
170
+ *
171
+ * - **A session seen for the first time** is recorded as-is, because there is
172
+ * nothing to subtract. A *resumed* session's first result already carries
173
+ * the turns from before we were watching, so that one turn over-reports.
174
+ * Every turn after it is right, which is the best available without the
175
+ * transcript.
176
+ * - **A mid-session `/clear`** resets the SDK's running total. That shows up
177
+ * as a total smaller than the one before it, so it is taken as a new
178
+ * baseline rather than emitted as a negative.
179
+ */
180
+ class UsageLedger {
181
+ constructor(maxSessions = UsageLedger.MAX_SESSIONS) {
182
+ this.maxSessions = maxSessions;
183
+ this.totals = new Map();
184
+ }
185
+ difference(sessionId, key, current) {
186
+ var _a;
187
+ const measured = new Map();
188
+ for (const field of CUMULATIVE_FIELDS) {
189
+ const value = current[field];
190
+ if (typeof value === 'number' && Number.isFinite(value))
191
+ measured.set(field, value);
192
+ }
193
+ if (!sessionId || measured.size === 0)
194
+ return { ...current };
195
+ let session = this.totals.get(sessionId);
196
+ if (!session) {
197
+ session = new Map();
198
+ this.totals.set(sessionId, session);
199
+ while (this.totals.size > this.maxSessions) {
200
+ const oldest = this.totals.keys().next().value;
201
+ if (oldest === undefined)
202
+ break;
203
+ this.totals.delete(oldest);
204
+ }
205
+ }
206
+ else {
207
+ // Keep insertion order meaningful, so eviction drops the least recent.
208
+ this.totals.delete(sessionId);
209
+ this.totals.set(sessionId, session);
210
+ }
211
+ const previous = session.get(key);
212
+ const snapshot = {};
213
+ measured.forEach((value, field) => {
214
+ snapshot[field] = value;
215
+ });
216
+ session.set(key, snapshot);
217
+ if (!previous)
218
+ return { ...current };
219
+ // A total that went backwards means the SDK reset it (/clear, or a fork).
220
+ // Take the new figure as the baseline rather than reporting a negative.
221
+ for (const [field, value] of measured) {
222
+ if (value < ((_a = previous[field]) !== null && _a !== void 0 ? _a : 0))
223
+ return { ...current };
224
+ }
225
+ const out = { ...current };
226
+ measured.forEach((value, field) => {
227
+ var _a;
228
+ const delta = value - ((_a = previous[field]) !== null && _a !== void 0 ? _a : 0);
229
+ out[field] = field === 'cost_usd' ? delta : Math.round(delta);
230
+ });
231
+ return out;
232
+ }
233
+ /** Per-model entries from `modelUsage`, differenced. */
234
+ perTurnModels(sessionId, entries) {
235
+ return entries.map((entry) => { var _a; return this.difference(sessionId, (_a = entry.model) !== null && _a !== void 0 ? _a : '', entry); });
236
+ }
237
+ /**
238
+ * `total_cost_usd`, differenced. Used on the aggregate path, where the
239
+ * tokens come from the per-turn `usage` but the cost does not.
240
+ */
241
+ perTurnCost(sessionId, totalCostUsd) {
242
+ if (typeof totalCostUsd !== 'number' || !Number.isFinite(totalCostUsd))
243
+ return undefined;
244
+ return this.difference(sessionId, '__total_cost__', { cost_usd: totalCostUsd }).cost_usd;
245
+ }
246
+ get trackedSessions() {
247
+ return this.totals.size;
248
+ }
249
+ }
250
+ exports.UsageLedger = UsageLedger;
251
+ /** Bounded so a long-lived process cannot accumulate sessions forever. */
252
+ UsageLedger.MAX_SESSIONS = 512;
253
+ /**
254
+ * One turn, assembled as its stream is consumed.
255
+ *
256
+ * An instance per `query()` call, never shared, so concurrent queries cannot
257
+ * contaminate each other.
258
+ */
259
+ class TurnAccumulator {
260
+ constructor() {
261
+ this.toolCalls = [];
262
+ this.toolResults = new Map();
263
+ this.seenToolIds = new Set();
264
+ this.texts = [];
265
+ }
266
+ observeAssistant(message) {
267
+ const api = message.message;
268
+ const model = field(api, 'model');
269
+ if (typeof model === 'string' && model)
270
+ this.model = model;
271
+ const content = field(api, 'content');
272
+ if (!Array.isArray(content))
273
+ return;
274
+ for (const block of content) {
275
+ const type = field(block, 'type');
276
+ if (type === 'text') {
277
+ const text = field(block, 'text');
278
+ if (typeof text === 'string' && text)
279
+ this.texts.push(text);
280
+ continue;
281
+ }
282
+ if (type !== 'tool_use' && field(block, 'name') === undefined)
283
+ continue;
284
+ const name = field(block, 'name');
285
+ if (name === undefined || isInternalTool(name))
286
+ continue;
287
+ const id = field(block, 'id');
288
+ if (this.seenToolIds.has(id))
289
+ continue; // a retried stream must not double-count
290
+ this.seenToolIds.add(id);
291
+ const input = field(block, 'input');
292
+ this.toolCalls.push({
293
+ id,
294
+ tool_name: cleanToolName(name),
295
+ arguments: input !== null && typeof input === 'object' && !Array.isArray(input)
296
+ ? input
297
+ : {},
298
+ });
299
+ }
300
+ }
301
+ observeUser(message) {
302
+ const content = field(message.message, 'content');
303
+ if (!Array.isArray(content))
304
+ return;
305
+ for (const block of content) {
306
+ const useId = field(block, 'tool_use_id');
307
+ if (useId === undefined || useId === null)
308
+ continue;
309
+ this.toolResults.set(useId, toolResultValue(field(block, 'content')));
310
+ }
311
+ }
312
+ /** Tool calls paired with their results, ready for the tracker. */
313
+ resolvedToolCalls() {
314
+ return this.toolCalls.map((call) => ({
315
+ tool_name: call.tool_name,
316
+ arguments: call.arguments,
317
+ result: this.toolResults.get(call.id),
318
+ }));
319
+ }
320
+ /** Joined assistant text, for when the result message carries no `result`. */
321
+ assistantText() {
322
+ return this.texts.join('\n');
323
+ }
324
+ }
325
+ exports.TurnAccumulator = TurnAccumulator;
326
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { QueryFn, TrackerLike } from './types';
2
+ export declare const __version__ = "0.1.0";
3
+ export { FRAMEWORK, INTERNAL_TOOLS, cleanToolName } from './capture';
4
+ export type { QueryFn, QueryLike, QueryParams, TrackerLike } from './types';
5
+ export interface InstrumentOptions {
6
+ agentId?: string;
7
+ tenantId?: string;
8
+ userId?: string;
9
+ sessionId?: string;
10
+ apiBaseUrl?: string;
11
+ /** Private: lets the contract tests inject a fake tracker. */
12
+ _tracker?: TrackerLike;
13
+ /** Private: lets the contract tests inject a stub of the SDK's `query()`. */
14
+ _query?: QueryFn;
15
+ }
16
+ /** Handle returned by {@link instrumentClaudeAgentSdk}. */
17
+ export declare class ChatseeClaudeAgentSdk {
18
+ private readonly userId?;
19
+ /**
20
+ * Emits to one tracker are serialised. `ChatseeTracker` keeps a single turn
21
+ * in instance fields and clears them only after `endTurn`'s awaited POST,
22
+ * so two queries emitting concurrently on one handle would clobber each
23
+ * other across that await. The chain makes the four calls of one turn and
24
+ * its send happen before the next turn's begin.
25
+ */
26
+ private emitChain;
27
+ /** A drop-in for the SDK's `query()`. */
28
+ readonly query: QueryFn;
29
+ private original;
30
+ constructor(tracker: TrackerLike, original: QueryFn | Promise<QueryFn>, userId?: string | undefined);
31
+ private readonly tracker;
32
+ /** Per-session running totals, so cumulative figures become per-turn. */
33
+ private readonly ledger;
34
+ private wrap;
35
+ private metadata;
36
+ private enqueueEmit;
37
+ /**
38
+ * All four tracker calls, together, when the turn is known to be over.
39
+ *
40
+ * Emitted in one go rather than opening the turn when the stream starts:
41
+ * the tracker holds a single turn at a time, so a startTurn left open across
42
+ * an await would be clobbered by any concurrent query.
43
+ */
44
+ private emit;
45
+ }
46
+ /**
47
+ * Capture every turn the Claude Agent SDK runs.
48
+ *
49
+ * `agentId` and `tenantId` say which ChatSee agent the data belongs to, and are
50
+ * all that is required. `_tracker` and `_query` are private and exist so the
51
+ * contract tests can inject a fake tracker and a stub SDK. Customers never
52
+ * pass them.
53
+ *
54
+ * Calling this twice does not capture twice: a `_query` that is already a
55
+ * wrapped query is unwrapped to the original before wrapping again.
56
+ */
57
+ export declare function instrumentClaudeAgentSdk(options?: InstrumentOptions): ChatseeClaudeAgentSdk;
58
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA8BA,OAAO,EAGL,OAAO,EAIP,WAAW,EAEZ,MAAM,SAAS,CAAC;AAEjB,eAAO,MAAM,WAAW,UAAU,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACrE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAoF5E,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,2DAA2D;AAC3D,qBAAa,qBAAqB;IAkB9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;IAjB1B;;;;;;OAMG;IACH,OAAO,CAAC,SAAS,CAAoC;IAErD,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAExB,OAAO,CAAC,QAAQ,CAA6B;gBAG3C,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EACnB,MAAM,CAAC,EAAE,MAAM,YAAA;IASlC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IAEtC,yEAAyE;IACzE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAI5C,OAAO,CAAC,IAAI;IA2GZ,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,WAAW;IAanB;;;;;;OAMG;YACW,IAAI;CA2DnB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,iBAAsB,GAAG,qBAAqB,CAwB/F"}
package/dist/index.js ADDED
@@ -0,0 +1,374 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ChatseeClaudeAgentSdk = exports.cleanToolName = exports.INTERNAL_TOOLS = exports.FRAMEWORK = exports.__version__ = void 0;
37
+ exports.instrumentClaudeAgentSdk = instrumentClaudeAgentSdk;
38
+ /**
39
+ * ChatSee connector for the Claude Agent SDK.
40
+ *
41
+ * import { instrumentClaudeAgentSdk } from '@chatsee/claude-agent-sdk';
42
+ *
43
+ * const chatsee = instrumentClaudeAgentSdk({ agentId: '...', tenantId: '...' });
44
+ *
45
+ * for await (const message of chatsee.query({ prompt: 'where is my order', options })) {
46
+ * ...
47
+ * }
48
+ *
49
+ * Unlike the Messages API connector, there is no client object to patch: the
50
+ * Agent SDK's entry point is a module-level `query()`. So this returns a handle
51
+ * whose `.query` is a drop-in for it. Every message still reaches the caller,
52
+ * unchanged and in order, while the turn is assembled alongside and sent when
53
+ * the terminal `result` message arrives.
54
+ *
55
+ * The Python connector also replaces `claude_agent_sdk.query` on the module.
56
+ * That is not possible here -- ES module and CommonJS exports are read-only --
57
+ * so the handle is the integration.
58
+ *
59
+ * Mirrors `chatsee_claude_agent_sdk/__init__.py` in the Python connector.
60
+ */
61
+ const capture_1 = require("./capture");
62
+ exports.__version__ = '0.1.0';
63
+ var capture_2 = require("./capture");
64
+ Object.defineProperty(exports, "FRAMEWORK", { enumerable: true, get: function () { return capture_2.FRAMEWORK; } });
65
+ Object.defineProperty(exports, "INTERNAL_TOOLS", { enumerable: true, get: function () { return capture_2.INTERNAL_TOOLS; } });
66
+ Object.defineProperty(exports, "cleanToolName", { enumerable: true, get: function () { return capture_2.cleanToolName; } });
67
+ const SDK_PACKAGE = '@anthropic-ai/claude-agent-sdk';
68
+ /** Tags a wrapped `query` so a second instrument call never wraps it twice. */
69
+ const WRAPPED = Symbol.for('chatsee.claude-agent-sdk.original');
70
+ let cachedFrameworkVersion;
71
+ function frameworkVersion() {
72
+ if (cachedFrameworkVersion !== undefined)
73
+ return cachedFrameworkVersion;
74
+ cachedFrameworkVersion = 'unknown';
75
+ try {
76
+ // The SDK's exports map does not expose package.json, so walk up from
77
+ // its resolved entry point and read the file directly.
78
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
79
+ const fs = require('fs');
80
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
81
+ const path = require('path');
82
+ let dir = path.dirname(require.resolve(SDK_PACKAGE));
83
+ for (let i = 0; i < 5; i++) {
84
+ const candidate = path.join(dir, 'package.json');
85
+ if (fs.existsSync(candidate)) {
86
+ const pkg = JSON.parse(fs.readFileSync(candidate, 'utf8'));
87
+ if ((pkg === null || pkg === void 0 ? void 0 : pkg.name) === SDK_PACKAGE && typeof pkg.version === 'string') {
88
+ cachedFrameworkVersion = pkg.version;
89
+ }
90
+ break;
91
+ }
92
+ const parent = path.dirname(dir);
93
+ if (parent === dir)
94
+ break;
95
+ dir = parent;
96
+ }
97
+ }
98
+ catch {
99
+ /* unknown */
100
+ }
101
+ return cachedFrameworkVersion;
102
+ }
103
+ function queryOf(sdk) {
104
+ const query = sdk === null || sdk === void 0 ? void 0 : sdk.query;
105
+ if (typeof query !== 'function') {
106
+ throw new Error(`${SDK_PACKAGE} does not export query()`);
107
+ }
108
+ return query;
109
+ }
110
+ /**
111
+ * The SDK's `query()`, resolved on first use rather than at import so the
112
+ * connector (and its offline tests) load without the SDK installed.
113
+ *
114
+ * The SDK ships as ES modules only. `require()` of it works on Node 20.19+
115
+ * and 22.12+; older runtimes throw ERR_REQUIRE_ESM, and there the load falls
116
+ * back to a dynamic `import()`, which is why this may return a promise.
117
+ */
118
+ function loadSdkQuery() {
119
+ try {
120
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
121
+ return queryOf(require(SDK_PACKAGE));
122
+ }
123
+ catch (error) {
124
+ if ((error === null || error === void 0 ? void 0 : error.code) !== 'ERR_REQUIRE_ESM')
125
+ throw error;
126
+ // A variable specifier keeps TypeScript (CommonJS output) from turning
127
+ // this into a require().
128
+ const specifier = SDK_PACKAGE;
129
+ return Promise.resolve(`${specifier}`).then(s => __importStar(require(s))).then(queryOf);
130
+ }
131
+ }
132
+ function isResult(message) {
133
+ return (message === null || message === void 0 ? void 0 : message.type) === 'result';
134
+ }
135
+ function textOf(content) {
136
+ if (typeof content === 'string')
137
+ return content;
138
+ if (!Array.isArray(content))
139
+ return '';
140
+ return content
141
+ .filter((b) => b && typeof b === 'object' && b.type === 'text')
142
+ .map((b) => { var _a; return String((_a = b.text) !== null && _a !== void 0 ? _a : ''); })
143
+ .filter(Boolean)
144
+ .join('\n');
145
+ }
146
+ /** Handle returned by {@link instrumentClaudeAgentSdk}. */
147
+ class ChatseeClaudeAgentSdk {
148
+ constructor(tracker, original, userId) {
149
+ this.userId = userId;
150
+ /**
151
+ * Emits to one tracker are serialised. `ChatseeTracker` keeps a single turn
152
+ * in instance fields and clears them only after `endTurn`'s awaited POST,
153
+ * so two queries emitting concurrently on one handle would clobber each
154
+ * other across that await. The chain makes the four calls of one turn and
155
+ * its send happen before the next turn's begin.
156
+ */
157
+ this.emitChain = Promise.resolve();
158
+ /** Per-session running totals, so cumulative figures become per-turn. */
159
+ this.ledger = new capture_1.UsageLedger();
160
+ this.tracker = tracker;
161
+ this.original = original;
162
+ const wrapped = (params) => this.wrap(params);
163
+ wrapped[WRAPPED] = original;
164
+ this.query = wrapped;
165
+ }
166
+ // -- the drop-in ---------------------------------------------------------
167
+ wrap(params) {
168
+ // One accumulator per call, never shared, so concurrent queries cannot
169
+ // contaminate each other.
170
+ const turn = new capture_1.TurnAccumulator();
171
+ const prompt = params === null || params === void 0 ? void 0 : params.prompt;
172
+ let userMessage = typeof prompt === 'string' ? prompt : '';
173
+ // Streaming-input mode: the prompt is an iterable of user messages. Watch
174
+ // it go by so the turn still has a user message; the SDK does not echo it.
175
+ let forwardedParams = params;
176
+ if (prompt && typeof prompt !== 'string' && typeof prompt[Symbol.asyncIterator] === 'function') {
177
+ const source = prompt;
178
+ const watched = {
179
+ async *[Symbol.asyncIterator]() {
180
+ var _a;
181
+ for await (const item of source) {
182
+ try {
183
+ const text = textOf((_a = item === null || item === void 0 ? void 0 : item.message) === null || _a === void 0 ? void 0 : _a.content);
184
+ if (text)
185
+ userMessage = text;
186
+ }
187
+ catch {
188
+ /* never break the agent */
189
+ }
190
+ yield item;
191
+ }
192
+ },
193
+ };
194
+ forwardedParams = { ...params, prompt: watched };
195
+ }
196
+ const handle = this;
197
+ // Sync when the SDK loaded with require(); a promise when it had to be
198
+ // import()ed (see loadSdkQuery). The caller sees the same object either
199
+ // way: iteration awaits the stream, and control methods return promises
200
+ // in the SDK too, so forwarding them through a promise changes nothing.
201
+ const query = this.original;
202
+ const streamOrPromise = typeof query === 'function'
203
+ ? query(forwardedParams)
204
+ : query.then((q) => {
205
+ this.original = q;
206
+ return q(forwardedParams);
207
+ });
208
+ async function* capturing() {
209
+ const stream = await streamOrPromise;
210
+ for await (const message of stream) {
211
+ try {
212
+ switch (message === null || message === void 0 ? void 0 : message.type) {
213
+ case 'assistant':
214
+ turn.observeAssistant(message);
215
+ break;
216
+ case 'user':
217
+ turn.observeUser(message);
218
+ break;
219
+ default:
220
+ if (isResult(message)) {
221
+ await handle.enqueueEmit(userMessage, forwardedParams.options, turn, message);
222
+ }
223
+ }
224
+ }
225
+ catch (error) {
226
+ // eslint-disable-next-line no-console
227
+ console.error(`chatsee-claude-agent-sdk capture failed: ${String(error)}`);
228
+ }
229
+ yield message; // forwarded unchanged, in order
230
+ }
231
+ }
232
+ const generator = capturing();
233
+ // The SDK's Query is an async generator that also carries control methods
234
+ // (`interrupt()`, `setPermissionMode()`, ...). Iterate through ours and
235
+ // forward everything else to the original, so the caller loses nothing.
236
+ const iteration = generator;
237
+ if (typeof streamOrPromise.then !== 'function') {
238
+ const stream = streamOrPromise;
239
+ return new Proxy(stream, {
240
+ get(target, prop, receiver) {
241
+ if (prop === Symbol.asyncIterator)
242
+ return () => generator;
243
+ if (prop === 'next' || prop === 'return' || prop === 'throw') {
244
+ return iteration[prop].bind(generator);
245
+ }
246
+ const value = Reflect.get(target, prop, receiver);
247
+ return typeof value === 'function' ? value.bind(target) : value;
248
+ },
249
+ });
250
+ }
251
+ const pending = streamOrPromise;
252
+ return new Proxy({}, {
253
+ get(_target, prop) {
254
+ if (prop === Symbol.asyncIterator)
255
+ return () => generator;
256
+ if (prop === 'next' || prop === 'return' || prop === 'throw') {
257
+ return iteration[prop].bind(generator);
258
+ }
259
+ if (prop === 'then')
260
+ return undefined; // not itself a promise
261
+ return (...args) => pending.then((stream) => {
262
+ const value = stream[prop];
263
+ return typeof value === 'function' ? value.apply(stream, args) : value;
264
+ });
265
+ },
266
+ });
267
+ }
268
+ // -- emitting ------------------------------------------------------------
269
+ metadata() {
270
+ return {
271
+ framework: capture_1.FRAMEWORK,
272
+ framework_version: frameworkVersion(),
273
+ connector_version: exports.__version__,
274
+ };
275
+ }
276
+ enqueueEmit(userMessage, options, turn, result) {
277
+ const run = this.emitChain.then(() => this.emit(userMessage, options, turn, result));
278
+ // Keep the chain alive past a failed emit; the failure is reported by the
279
+ // caller's catch, never to the agent.
280
+ this.emitChain = run.catch(() => undefined);
281
+ return run;
282
+ }
283
+ /**
284
+ * All four tracker calls, together, when the turn is known to be over.
285
+ *
286
+ * Emitted in one go rather than opening the turn when the stream starts:
287
+ * the tracker holds a single turn at a time, so a startTurn left open across
288
+ * an await would be clobbered by any concurrent query.
289
+ */
290
+ async emit(userMessage, options, turn, result) {
291
+ var _a;
292
+ const tracker = this.tracker;
293
+ const answer = (typeof result.result === 'string' && result.result) ||
294
+ turn.assistantText() ||
295
+ (Array.isArray(result.errors) ? result.errors.join('\n') : '');
296
+ tracker.startTurn(userMessage, this.metadata(), (0, capture_1.systemPromptOf)(options), this.userId);
297
+ for (const call of turn.resolvedToolCalls()) {
298
+ tracker.logToolCall(call.tool_name, call.arguments, call.result);
299
+ }
300
+ // One entry per model the turn used. The SDK states a real dollar cost,
301
+ // so it is passed through rather than estimated -- nothing here invents a
302
+ // price. duration_api_ms is the time actually spent in the API, which is
303
+ // the honest per-call latency.
304
+ const apiMs = typeof result.duration_api_ms === 'number' && Number.isFinite(result.duration_api_ms)
305
+ ? Math.round(result.duration_api_ms)
306
+ : undefined;
307
+ // modelUsage and total_cost_usd are running totals for the query() call,
308
+ // not this turn's share. One turn per call makes them the same number,
309
+ // which is why it never shows in the ordinary case; a streaming-input or
310
+ // resumed session runs several turns through one total, and recording it
311
+ // raw makes turn three report turns one, two and three.
312
+ const { entries, cumulative } = (0, capture_1.modelCallsFrom)(result);
313
+ const sessionId = result.session_id;
314
+ const calls = cumulative ? this.ledger.perTurnModels(sessionId, entries) : entries;
315
+ const totalCost = cumulative
316
+ ? undefined // already carried per model
317
+ : this.ledger.perTurnCost(sessionId, result.total_cost_usd);
318
+ for (const entry of calls) {
319
+ tracker.logModelCall({
320
+ model: (_a = entry.model) !== null && _a !== void 0 ? _a : turn.model,
321
+ provider: 'anthropic',
322
+ prompt_tokens: entry.prompt_tokens,
323
+ completion_tokens: entry.completion_tokens,
324
+ cached_tokens: entry.cached_tokens,
325
+ // A per-model cost when the SDK broke it down, otherwise the turn's
326
+ // cost on the single entry. Never divided up: a split we invented
327
+ // would look like measurement.
328
+ cost_usd: entry.cost_usd !== undefined ? entry.cost_usd : calls.length === 1 ? totalCost : undefined,
329
+ duration_ms: calls.length === 1 ? apiMs : undefined,
330
+ });
331
+ }
332
+ if (result.is_error) {
333
+ tracker.logException(`claude-agent-sdk reported subtype=${result.subtype}`);
334
+ }
335
+ await tracker.endTurn(answer);
336
+ }
337
+ }
338
+ exports.ChatseeClaudeAgentSdk = ChatseeClaudeAgentSdk;
339
+ /**
340
+ * Capture every turn the Claude Agent SDK runs.
341
+ *
342
+ * `agentId` and `tenantId` say which ChatSee agent the data belongs to, and are
343
+ * all that is required. `_tracker` and `_query` are private and exist so the
344
+ * contract tests can inject a fake tracker and a stub SDK. Customers never
345
+ * pass them.
346
+ *
347
+ * Calling this twice does not capture twice: a `_query` that is already a
348
+ * wrapped query is unwrapped to the original before wrapping again.
349
+ */
350
+ function instrumentClaudeAgentSdk(options = {}) {
351
+ var _a;
352
+ let tracker = options._tracker;
353
+ if (!tracker) {
354
+ if (!options.agentId || !options.tenantId) {
355
+ throw new Error('agentId and tenantId are required.');
356
+ }
357
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
358
+ const { ChatseeTracker } = require('chatsee-sdk');
359
+ tracker = new ChatseeTracker({
360
+ agent_id: options.agentId,
361
+ tenant_id: options.tenantId,
362
+ user_id: options.userId,
363
+ session_id: options.sessionId,
364
+ api_base_url: options.apiBaseUrl,
365
+ });
366
+ }
367
+ let query = (_a = options._query) !== null && _a !== void 0 ? _a : loadSdkQuery();
368
+ // Instrumenting twice must not wrap twice.
369
+ while (typeof query === 'function' && query[WRAPPED]) {
370
+ query = query[WRAPPED];
371
+ }
372
+ return new ChatseeClaudeAgentSdk(tracker, query, options.userId);
373
+ }
374
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The slice of the Claude Agent SDK's message types this connector reads.
3
+ *
4
+ * Deliberately structural and deliberately narrow: importing the SDK's own
5
+ * types would make the connector uncompilable without the SDK installed,
6
+ * which the offline tests rely on not being the case (the Python connector
7
+ * identifies `ResultMessage` by shape for the same reason). Field names match
8
+ * `@anthropic-ai/claude-agent-sdk` exactly, so a real `SDKMessage` satisfies
9
+ * these without a cast.
10
+ */
11
+ export interface AssistantLike {
12
+ type: 'assistant';
13
+ /** The API message: `content` blocks and `model` live here, not on the top level. */
14
+ message: unknown;
15
+ parent_tool_use_id?: string | null;
16
+ }
17
+ export interface UserLike {
18
+ type: 'user';
19
+ /** `MessageParam` from the Anthropic SDK: tool results are `content` blocks here. */
20
+ message: unknown;
21
+ parent_tool_use_id?: string | null;
22
+ }
23
+ export interface ResultLike {
24
+ type: 'result';
25
+ subtype: string;
26
+ session_id?: string;
27
+ is_error?: boolean;
28
+ /** Present on `subtype: 'success'` only. */
29
+ result?: string;
30
+ /** Present on the error subtypes only. */
31
+ errors?: string[];
32
+ total_cost_usd?: number;
33
+ duration_ms?: number;
34
+ duration_api_ms?: number;
35
+ num_turns?: number;
36
+ /** Aggregate usage, Anthropic API shape (snake_case). */
37
+ usage?: unknown;
38
+ /** Per-model breakdown, SDK shape (camelCase: inputTokens, costUSD, ...). */
39
+ modelUsage?: Record<string, unknown>;
40
+ }
41
+ /** Any message the stream yields. Everything not listed is forwarded untouched. */
42
+ export type MessageLike = AssistantLike | UserLike | ResultLike | {
43
+ type: string;
44
+ };
45
+ /** What the SDK's `query()` takes. `options` is passed through untouched. */
46
+ export interface QueryParams {
47
+ prompt: string | AsyncIterable<unknown>;
48
+ options?: Record<string, unknown>;
49
+ }
50
+ /**
51
+ * What the SDK's `query()` returns: an async iterable of messages that also
52
+ * carries control methods (`interrupt()`, `setPermissionMode()`, ...). The
53
+ * wrapped stream keeps those reachable.
54
+ */
55
+ export type QueryLike = AsyncIterable<MessageLike> & Record<string, unknown>;
56
+ export type QueryFn = (params: QueryParams) => QueryLike;
57
+ /** The four tracker calls a connector is allowed to make. */
58
+ export interface TrackerLike {
59
+ startTurn(userMessage: string, metadata?: Record<string, unknown>, systemPrompt?: string, userId?: string): void;
60
+ logToolCall(toolName: string, args: Record<string, unknown>, result?: unknown, error?: string, durationMs?: number): void;
61
+ logModelCall(options: {
62
+ model?: string;
63
+ provider?: string;
64
+ prompt_tokens?: number;
65
+ completion_tokens?: number;
66
+ cached_tokens?: number;
67
+ cost_usd?: number;
68
+ duration_ms?: number;
69
+ }): void;
70
+ logException(exception: Error | string): void;
71
+ endTurn(botMessage: string, isFinalTurn?: boolean): Promise<void>;
72
+ }
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,qFAAqF;IACrF,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,mFAAmF;AACnF,MAAM,MAAM,WAAW,GAAG,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnF,6EAA6E;AAC7E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,aAAa,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE7E,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,SAAS,CAAC;AAEzD,6DAA6D;AAC7D,MAAM,WAAW,WAAW;IAC1B,SAAS,CACP,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,YAAY,CAAC,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,MAAM,GACd,IAAI,CAAC;IACR,WAAW,CACT,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,CAAC,EAAE,OAAO,EAChB,KAAK,CAAC,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI,CAAC;IACR,YAAY,CAAC,OAAO,EAAE;QACpB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,IAAI,CAAC;IACT,YAAY,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9C,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnE"}
package/dist/types.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ /**
3
+ * The slice of the Claude Agent SDK's message types this connector reads.
4
+ *
5
+ * Deliberately structural and deliberately narrow: importing the SDK's own
6
+ * types would make the connector uncompilable without the SDK installed,
7
+ * which the offline tests rely on not being the case (the Python connector
8
+ * identifies `ResultMessage` by shape for the same reason). Field names match
9
+ * `@anthropic-ai/claude-agent-sdk` exactly, so a real `SDKMessage` satisfies
10
+ * these without a cast.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG"}
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
- {
2
- "name": "@chatsee/claude-agent-sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "@chatsee/claude-agent-sdk",
3
+ "version": "0.1.0",
4
+ "description": "ChatSee connector for the Claude Agent SDK — capture every turn with one line.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepare": "npm run build",
10
+ "test": "jest"
11
+ },
12
+ "keywords": ["chatsee", "claude", "claude-agent-sdk", "anthropic", "agent", "monitoring", "tracing"],
13
+ "author": "ChatSee <contact@chatsee.ai>",
14
+ "license": "MIT",
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "dependencies": {
19
+ "chatsee-sdk": "^2.3.0",
20
+ "@anthropic-ai/claude-agent-sdk": ">=0.1.0"
21
+ },
22
+ "devDependencies": {
23
+ "@chatsee/connector-testing": "file:../../_shared",
24
+ "@types/jest": "^29.5.0",
25
+ "@types/node": "^20.0.0",
26
+ "jest": "^29.7.0",
27
+ "ts-jest": "^29.1.0",
28
+ "typescript": "^5.3.0"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18.0.0"
36
+ }
37
+ }