@tracehatch/sdk 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/GUIDE.md +387 -0
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/dist/auto.cjs +2 -0
- package/dist/auto.cjs.map +1 -0
- package/dist/auto.d.cts +2 -0
- package/dist/auto.d.ts +2 -0
- package/dist/auto.js +2 -0
- package/dist/auto.js.map +1 -0
- package/dist/chunk-KHVJY566.js +9 -0
- package/dist/chunk-KHVJY566.js.map +1 -0
- package/dist/chunk-U5REKR34.cjs +9 -0
- package/dist/chunk-U5REKR34.cjs.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +243 -0
- package/dist/index.d.ts +243 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/package.json +69 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
- Use only the Tracehatch client, environment variables and request headers.
|
|
6
|
+
- Remove retired class aliases and configuration fallbacks.
|
|
7
|
+
- Use Tracehatch names for process state, identifier hashes, conversation
|
|
8
|
+
fingerprints, capture metadata and product-specific span attributes.
|
|
9
|
+
- Keep server-only capture, ESM/CommonJS shared context and the hosted API default.
|
package/GUIDE.md
ADDED
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
# Tracehatch SDK guide
|
|
2
|
+
|
|
3
|
+
Record agent runs, model calls, tool steps and sessions in Tracehatch from Node.js
|
|
4
|
+
18 or newer. The package supports ESM and CommonJS and has no runtime
|
|
5
|
+
dependencies.
|
|
6
|
+
|
|
7
|
+
## Install and configure
|
|
8
|
+
|
|
9
|
+
Install the SDK in the **server package that makes model calls**:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @tracehatch/sdk@0.3.0
|
|
13
|
+
# Or: pnpm add @tracehatch/sdk@0.3.0
|
|
14
|
+
# Or: yarn add @tracehatch/sdk@0.3.0
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The package is MIT licensed. Your dashboard at [tracehatch.com](https://tracehatch.com)
|
|
18
|
+
also provides the same SDK as a versioned archive when you need that installation path:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm install https://tracehatch.com/downloads/tracehatch-sdk-0.3.0.tgz
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Create a project API key in Tracehatch. The key selects the project and environment.
|
|
25
|
+
Configuration comes from the environment:
|
|
26
|
+
|
|
27
|
+
| Variable | Purpose |
|
|
28
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
29
|
+
| `TRACEHATCH_API_KEY` | Required. Without it the SDK stays disabled and sends nothing. |
|
|
30
|
+
| `TRACEHATCH_BASE_URL` | Optional. Your API origin for a self-hosted or local Tracehatch; `/api/v1` bases work too. Defaults to `https://api.tracehatch.com`. |
|
|
31
|
+
| `TRACEHATCH_AGENT` | Optional. Agent name; defaults to the nearest `package.json` name. |
|
|
32
|
+
| `TRACEHATCH_RELEASE` | Optional. Release; defaults to `VERCEL_GIT_COMMIT_SHA`, `GITHUB_SHA` and similar, then git `HEAD`. |
|
|
33
|
+
|
|
34
|
+
Keep the key in the server environment and never expose it in browser code.
|
|
35
|
+
|
|
36
|
+
Load the SDK before anything else so provider clients are created after it:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
node --import @tracehatch/sdk/auto server.js
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// or, as the first line of the entry file
|
|
44
|
+
import "@tracehatch/sdk/auto"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
CommonJS: `require("@tracehatch/sdk/auto")`. Next.js: create
|
|
48
|
+
`instrumentation.ts` beside `app` or `pages`, inside `src` when the application
|
|
49
|
+
uses `src/app` or `src/pages`:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
export async function register() {
|
|
53
|
+
if (process.env.NEXT_RUNTIME === "nodejs")
|
|
54
|
+
await import("@tracehatch/sdk/auto")
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The auto entry calls `init()` with the environment. Call `init()` yourself
|
|
59
|
+
instead when you want options:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { init } from "@tracehatch/sdk"
|
|
63
|
+
|
|
64
|
+
init({
|
|
65
|
+
agent: "support-agent",
|
|
66
|
+
captureBodies: false,
|
|
67
|
+
redact: { rules: [{ name: "customer", pattern: /CUSTOMER-\d+/g }] },
|
|
68
|
+
sampling: { rate: 0.25 },
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`init` options: `apiKey`, `baseUrl`, `agent`, `release`, `captureBodies`
|
|
73
|
+
(default `true`), `redact` (`false` or `{ builtIns?, rules? }`), `sampling`
|
|
74
|
+
(`{ rate }`, head sampling per run), `debug`, `flushOnExit` (default `true`).
|
|
75
|
+
Unknown options are rejected. `init()` replaces an earlier client and installs
|
|
76
|
+
the capture hooks; `shutdown()` removes them.
|
|
77
|
+
|
|
78
|
+
## Check the connection
|
|
79
|
+
|
|
80
|
+
Save this as `tracehatch-check.mjs` in the consuming package, load
|
|
81
|
+
`TRACEHATCH_API_KEY` (and `TRACEHATCH_BASE_URL` for a local API) into the process
|
|
82
|
+
with your existing configuration, then run `node tracehatch-check.mjs`. On Node 22
|
|
83
|
+
or newer `node --env-file=.env tracehatch-check.mjs` also works.
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
import "@tracehatch/sdk/auto"
|
|
87
|
+
import { trace, flushWithResult, shutdown } from "@tracehatch/sdk"
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const traceId = await trace("tracehatch-setup-check", (run) => run.id)
|
|
91
|
+
const result = await flushWithResult()
|
|
92
|
+
if (result.status !== "accepted")
|
|
93
|
+
throw new Error(`Delivery was not confirmed: ${JSON.stringify(result)}`)
|
|
94
|
+
console.log({ traceId, delivery: result.status })
|
|
95
|
+
} finally {
|
|
96
|
+
await shutdown()
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`accepted` requires the API's `202` receipt with a matching span count. It
|
|
101
|
+
proves ingestion acceptance; the project's trace view confirms processing. A
|
|
102
|
+
web page at the wrong URL, a rejected key, a timeout and disabled tracing cannot
|
|
103
|
+
be mistaken for a successful check.
|
|
104
|
+
|
|
105
|
+
## What is captured
|
|
106
|
+
|
|
107
|
+
Capture happens at the HTTP boundary: the SDK observes the runtime's global
|
|
108
|
+
`fetch`, so any client or framework that uses it is recorded without wrapping.
|
|
109
|
+
The request body names the model call; the response is read from a clone while
|
|
110
|
+
your code consumes the original. A capture failure never changes the response
|
|
111
|
+
or the error your application receives.
|
|
112
|
+
|
|
113
|
+
| Request | Recorded as |
|
|
114
|
+
| ------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
115
|
+
| `POST …/chat/completions` | `generation` span, `chat.completions` |
|
|
116
|
+
| `POST …/responses`, `GET …/responses/{id}`, `POST …/responses/{id}/cancel` | `generation` span, `responses`; polls complete a background response |
|
|
117
|
+
| `POST …/embeddings` | `embedding` span |
|
|
118
|
+
| `POST …/messages` on `api.anthropic.com`, or with an `anthropic-version` header | `generation` span, `messages` |
|
|
119
|
+
|
|
120
|
+
`gen_ai.system` is `openai` or `anthropic` on the official hosts, a known name
|
|
121
|
+
for Azure OpenAI, Groq, Mistral, Together, OpenRouter, DeepSeek, xAI,
|
|
122
|
+
Perplexity, Fireworks, Cerebras, Gemini's OpenAI-compatible endpoint and
|
|
123
|
+
Ollama, and the hostname otherwise. Pricing follows the model name when the
|
|
124
|
+
system is unknown.
|
|
125
|
+
|
|
126
|
+
Recorded per call: model, request settings, reported token usage (including
|
|
127
|
+
cached and cache-creation input and reasoning tokens), finish reasons, tool
|
|
128
|
+
call counts, time to first token and tokens per second for streams, the HTTP
|
|
129
|
+
status, provider error types (`rate_limit`, `timeout`, `auth`,
|
|
130
|
+
`invalid_request`, `provider_error`, `incomplete_stream`,
|
|
131
|
+
`incomplete_response`) and, with body capture on, the request, messages and
|
|
132
|
+
assembled output. A provider client's retry of the same request within a minute
|
|
133
|
+
of a failure is recorded as attempt 2, 3 and so on.
|
|
134
|
+
|
|
135
|
+
Streams: the SDK reads its own copy while your code reads the response, so a
|
|
136
|
+
stream returned out of a `trace()` callback and consumed later is still
|
|
137
|
+
recorded; the run's end waits for it, bounded by two minutes, and `flush()`
|
|
138
|
+
waits for such ends too. A stream your code neither reads nor cancels is
|
|
139
|
+
recorded as cancelled when that bound passes. Cancelling through an `AbortSignal` records a
|
|
140
|
+
cancelled span. For OpenAI chat streams `stream_options.include_usage: true` is
|
|
141
|
+
added when you have not set the option, so usage is reported; an explicit
|
|
142
|
+
`false` stays false.
|
|
143
|
+
|
|
144
|
+
Not captured: clients constructed before the SDK loaded (they keep the original
|
|
145
|
+
`fetch`), clients given their own `fetch` option, non-JSON request bodies such
|
|
146
|
+
as audio uploads, and endpoints outside the table.
|
|
147
|
+
|
|
148
|
+
## Runs, sessions and tools without code
|
|
149
|
+
|
|
150
|
+
**Runs.** Inside an inbound HTTP request handled by `http.Server` or
|
|
151
|
+
`https.Server` (Express, Fastify, NestJS, Next.js) every model call belongs to
|
|
152
|
+
one run named `METHOD /path`; the run is sent only when a model call or an
|
|
153
|
+
explicit span happened, ends when the response finishes, and is marked failed
|
|
154
|
+
on a 5xx status. Outside a request (scripts, queues, workers) a model call
|
|
155
|
+
starts a run. If the response asks for tools, the run stays open for the tool
|
|
156
|
+
step and the follow-up call that carries the tool results; a response without
|
|
157
|
+
tool calls, a failure, a new user message, ten idle minutes or process exit
|
|
158
|
+
ends it. Explicit `trace()` runs take precedence over both.
|
|
159
|
+
|
|
160
|
+
Automatic runs also populate their transcript with the current user message
|
|
161
|
+
from the first provider call and the latest textual model answer. Tool
|
|
162
|
+
arguments and reasoning remain on the generation span. Explicit traces keep
|
|
163
|
+
application-supplied input and output. Inferred transcripts use the same
|
|
164
|
+
redaction, truncation, and `captureBodies: false` policy as other bodies.
|
|
165
|
+
|
|
166
|
+
**Sessions and users.** In order: the `x-tracehatch-session-id` and
|
|
167
|
+
`x-tracehatch-user-id` request headers, `setSession()` / `setUser()`, the OpenAI
|
|
168
|
+
Responses `conversation` id or `previous_response_id`, then a fingerprint of
|
|
169
|
+
the conversation's system prompt and first user message, which every later
|
|
170
|
+
turn resends. Derived session ids look like `auto-` followed by 32 hex
|
|
171
|
+
characters and are identical across processes, restarts and the gateway. The
|
|
172
|
+
OpenAI `user` field and Anthropic `metadata.user_id` name the end user.
|
|
173
|
+
|
|
174
|
+
**Tools.** Tool calls in a response are paired with the tool results in the
|
|
175
|
+
next request (chat `tool` messages, Responses `function_call_output` items,
|
|
176
|
+
Anthropic `tool_result` blocks) and recorded as `tool` spans with the call id,
|
|
177
|
+
name, arguments, output, error flag and the time between the two calls.
|
|
178
|
+
|
|
179
|
+
When timing that same provider-requested tool with `tool()`, include
|
|
180
|
+
`attributes: { "tool.call_id": call.id }` in its options. The explicit span then
|
|
181
|
+
takes precedence over the inferred span, retaining its real timing and retry
|
|
182
|
+
events. A matching name alone cannot identify one tool execution reliably.
|
|
183
|
+
|
|
184
|
+
**Identity.** The run's `agent` is the nearest `package.json` name and the
|
|
185
|
+
`release` is the deployment's commit unless `init` or the environment says
|
|
186
|
+
otherwise. Automatic runs carry `metadata["tracehatch.capture"] = "auto"`.
|
|
187
|
+
|
|
188
|
+
## Precision when you want it
|
|
189
|
+
|
|
190
|
+
The explicit API works alongside automatic capture; model calls made inside a
|
|
191
|
+
`trace()` become children of it.
|
|
192
|
+
|
|
193
|
+
```ts
|
|
194
|
+
import OpenAI from "openai"
|
|
195
|
+
import { trace, tool, setSession, setUser, flush } from "@tracehatch/sdk"
|
|
196
|
+
|
|
197
|
+
const openai = new OpenAI()
|
|
198
|
+
|
|
199
|
+
await trace("answer question", async (run) => {
|
|
200
|
+
setSession("conversation-42")
|
|
201
|
+
setUser({ id: "user-7" })
|
|
202
|
+
const context = await tool("search docs", async () => "Refunds take 5 days")
|
|
203
|
+
const answer = await openai.chat.completions.create({
|
|
204
|
+
model: "YOUR_EXISTING_MODEL_ID",
|
|
205
|
+
messages: [{ role: "user", content: `Explain: ${context}` }],
|
|
206
|
+
})
|
|
207
|
+
run.addTags("support")
|
|
208
|
+
return answer
|
|
209
|
+
})
|
|
210
|
+
await flush()
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Both `trace` and `span` return promises; await them even for synchronous
|
|
214
|
+
callbacks. For an explicit lifetime:
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import { startTrace, span, shutdown } from "@tracehatch/sdk"
|
|
218
|
+
|
|
219
|
+
const run = startTrace("import documents", { tags: ["batch"] })
|
|
220
|
+
try {
|
|
221
|
+
await run.run(() => span("parse", "custom", async () => "parsed"))
|
|
222
|
+
run.end({ output: { imported: 1 } })
|
|
223
|
+
} catch (error) {
|
|
224
|
+
run.end({ outcome: "error", error })
|
|
225
|
+
throw error
|
|
226
|
+
} finally {
|
|
227
|
+
await shutdown()
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Trace handles support `setInput`, `setOutput`, `setUser`, `setSession`,
|
|
232
|
+
`addTags`, `setMetadata` and `score`. Span handles support `setOutput`,
|
|
233
|
+
`setAttributes` and `addEvent(name, attributes?, level?)`. Top-level context
|
|
234
|
+
helpers update the active run. Concurrent spans keep their own parents. Nested
|
|
235
|
+
traces receive `metadata.parent_trace_id`. A `span()` or `tool()` recorded
|
|
236
|
+
outside any run becomes a run of its own.
|
|
237
|
+
|
|
238
|
+
`tool(name, fn, { retries: 2, backoffMs: 100 })` makes up to three attempts in
|
|
239
|
+
one span, with an event per attempt. Retries default to zero. Only opt into
|
|
240
|
+
retries when the tool operation can safely be repeated. Provider client
|
|
241
|
+
retries are recorded as separate attempts of the generation.
|
|
242
|
+
|
|
243
|
+
For a provider the SDK does not recognise, record the call yourself:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
import { trace, span } from "@tracehatch/sdk"
|
|
247
|
+
|
|
248
|
+
await trace("answer question", () =>
|
|
249
|
+
span("model call", "generation", async (step) => {
|
|
250
|
+
const result = await existingModelCall()
|
|
251
|
+
step.setAttributes({
|
|
252
|
+
"gen_ai.system": existingProviderName,
|
|
253
|
+
"gen_ai.request.model": existingModelId,
|
|
254
|
+
})
|
|
255
|
+
return result
|
|
256
|
+
})
|
|
257
|
+
)
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Scores
|
|
261
|
+
|
|
262
|
+
Record numeric or categorical scores while the run is open:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
import { trace, span, score } from "@tracehatch/sdk"
|
|
266
|
+
|
|
267
|
+
await trace("answer question", async (run) => {
|
|
268
|
+
await span("check answer", "custom", async (step) => {
|
|
269
|
+
score({ name: "correctness", value: "pass", spanId: step.id })
|
|
270
|
+
})
|
|
271
|
+
run.score({ name: "confidence", value: 0.95, comment: "Evidence matched" })
|
|
272
|
+
})
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
`score({ name, value, comment?, spanId? })` uses the current run;
|
|
276
|
+
`run.score(...)` works with a manual `startTrace` handle too. Without `spanId`,
|
|
277
|
+
the score belongs to the run. Call before `run.end()` or before the trace
|
|
278
|
+
callback returns. Calls outside a recording run, after end, or with invalid
|
|
279
|
+
values are ignored. There are at most 100 SDK scores per run; each gets a
|
|
280
|
+
stable `scr_` id so retries cannot create duplicates. Numeric values must be
|
|
281
|
+
finite; categorical values must be nonempty. Names, categories and comments
|
|
282
|
+
are redacted and bounded to 128, 256 and 2,048 characters respectively.
|
|
283
|
+
|
|
284
|
+
Scores travel with subsequent completed spans, including the final root. Flush
|
|
285
|
+
after the run ends to send its final scores. The worker keeps a span-targeted
|
|
286
|
+
score pending until that span arrives and its project, environment and run
|
|
287
|
+
match. Scores are private project data available in the trace scores API;
|
|
288
|
+
public share links omit them.
|
|
289
|
+
|
|
290
|
+
## Data and delivery
|
|
291
|
+
|
|
292
|
+
Body capture defaults to on. Built-in redaction masks email addresses, phone
|
|
293
|
+
numbers, valid card-shaped strings, IBANs, and common API key/secret patterns.
|
|
294
|
+
Credential fields are masked too. It runs on bodies, attributes, metadata,
|
|
295
|
+
errors, score text, and user details before they enter the queue. Detection is
|
|
296
|
+
heuristic; choose `captureBodies: false` when prompts and outputs must be
|
|
297
|
+
excluded entirely.
|
|
298
|
+
|
|
299
|
+
Session and user ids need stable grouping. If redaction would change an id, or
|
|
300
|
+
the id exceeds the wire's 128-character limit, the SDK sends a deterministic
|
|
301
|
+
SHA-256 pseudonym of the complete original id instead of a shared marker or
|
|
302
|
+
truncated prefix. Session and user pseudonyms use separate hash domains; safe,
|
|
303
|
+
bounded ids remain unchanged. These are pseudonyms, not anonymous values: the
|
|
304
|
+
hashes are unkeyed, so guessed low-entropy identifiers can be checked against
|
|
305
|
+
them. Prefer opaque identifiers.
|
|
306
|
+
|
|
307
|
+
With body capture off, span byte counts remain available; input, output and
|
|
308
|
+
messages are omitted, including trace-level bodies. `redact: false` explicitly
|
|
309
|
+
disables masking. Large bodies are truncated to a bounded head and tail.
|
|
310
|
+
Cycles, unsupported values and throwing getters cannot break your code.
|
|
311
|
+
Streamed output is assembled up to a bound; beyond it the span is marked
|
|
312
|
+
truncated and keeps the full byte count.
|
|
313
|
+
|
|
314
|
+
The queue holds at most 10 MB, including trace envelopes. It sends up to 100
|
|
315
|
+
spans per batch after two seconds, retries transient failures, honours
|
|
316
|
+
`Retry-After`, and splits oversized batches. Oldest queued spans are dropped
|
|
317
|
+
when full. `init()` returns a client with `stats()` to inspect queued and
|
|
318
|
+
dropped spans.
|
|
319
|
+
|
|
320
|
+
`await flushWithResult(timeoutMs?)` returns `status`, `acceptedSpans`,
|
|
321
|
+
`droppedSpans`, `pendingSpans` and optional `lastError: { code, httpStatus? }`.
|
|
322
|
+
The status is `disabled` without an initialised client, `empty` before any
|
|
323
|
+
recorded delivery, `accepted` only when all queued work has been acknowledged
|
|
324
|
+
without loss, `timeout` while work is pending, or `failed` after any loss.
|
|
325
|
+
|
|
326
|
+
`sampling.rate` is head sampling: the decision is made per run when it starts,
|
|
327
|
+
so spans of an unsampled run are never sent. While a workspace is over its
|
|
328
|
+
monthly trace allowance the API answers each batch with a lower advisory rate;
|
|
329
|
+
the SDK applies it to new runs, logs a warning once, and returns to the
|
|
330
|
+
configured rate as soon as the hint stops. At three times the allowance the API
|
|
331
|
+
rejects batches with `quota_exceeded` and a `Retry-After`; the SDK retries with
|
|
332
|
+
backoff and eventually drops those spans.
|
|
333
|
+
|
|
334
|
+
Exit: the queue is flushed on `beforeExit` and, with a two-second bound, on
|
|
335
|
+
`SIGTERM` and `SIGINT`; when nothing else handles the signal the SDK re-raises
|
|
336
|
+
it afterwards so the process still exits. Open automatic runs are ended first.
|
|
337
|
+
Vercel functions receive the final flush through `waitUntil`. On AWS Lambda,
|
|
338
|
+
Netlify and similar hosts `await flush()` before the handler returns. `flush()`
|
|
339
|
+
never rejects and waits up to five seconds by default. Call `shutdown()` only
|
|
340
|
+
when the process is finished: it flushes, stops timers and removes the capture
|
|
341
|
+
hooks.
|
|
342
|
+
|
|
343
|
+
## Prompt for your coding assistant
|
|
344
|
+
|
|
345
|
+
Copy this prompt into the AI working in your application repository. Do not
|
|
346
|
+
paste an API key into a prompt; give it only the package version and, for a
|
|
347
|
+
self-hosted API, the API origin.
|
|
348
|
+
|
|
349
|
+
```text
|
|
350
|
+
Integrate Tracehatch observability into this application. First inspect the
|
|
351
|
+
runtime, server entry point and package manager/lockfile. Preserve our current
|
|
352
|
+
provider, model, credentials, retries and behavior.
|
|
353
|
+
|
|
354
|
+
Install @tracehatch/sdk@0.3.0 from npm and verify the exact package name and
|
|
355
|
+
version. If the application uses the dashboard-hosted archive instead, preserve
|
|
356
|
+
that installation method and use its versioned SDK URL.
|
|
357
|
+
|
|
358
|
+
For Node.js, install it in the server package that makes model calls. Read
|
|
359
|
+
TRACEHATCH_API_KEY (and TRACEHATCH_BASE_URL only for a self-hosted API) through our
|
|
360
|
+
existing secret configuration; never print or commit a key or put it in
|
|
361
|
+
client-side/public environment variables. Load the SDK before provider clients
|
|
362
|
+
are created: `node --import @tracehatch/sdk/auto` or
|
|
363
|
+
`import "@tracehatch/sdk/auto"` as the first import of the entry
|
|
364
|
+
file; in Next.js use instrumentation.ts with the nodejs runtime check.
|
|
365
|
+
Model calls, inbound requests, sessions and tool steps are recorded without
|
|
366
|
+
further changes; add trace()/tool()/setSession() only where we want explicit
|
|
367
|
+
naming. Await flush() before short-lived processes and Lambda handlers return.
|
|
368
|
+
|
|
369
|
+
Start with the connection check from the SDK guide, require status "accepted",
|
|
370
|
+
print the trace id and verify that exact trace in the selected Tracehatch project.
|
|
371
|
+
Keep body capture on unless our data policy says otherwise; init({ captureBodies:
|
|
372
|
+
false }) turns it off. Run focused checks, show the changed files and report
|
|
373
|
+
receipt evidence and anything you could not verify.
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Maintainers
|
|
377
|
+
|
|
378
|
+
`pnpm --filter @tracehatch/sdk test:package:isolated` rebuilds,
|
|
379
|
+
packs, installs the archive into a clean offline consumer with install scripts
|
|
380
|
+
disabled, and verifies the ESM, CommonJS and auto entries share one client. It
|
|
381
|
+
does not publish anything or call a model or API deployment.
|
|
382
|
+
|
|
383
|
+
From this repository, `pnpm --filter @tracehatch/sdk sample` runs a
|
|
384
|
+
small agent without any external model dependency. With an API key configured
|
|
385
|
+
(and `TRACEHATCH_BASE_URL` for a local API), it sends a root, a tool and a
|
|
386
|
+
retrieval span to your API. `pnpm --filter @tracehatch/sdk fixture`
|
|
387
|
+
regenerates the API's e2e fixture from the public surface.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Unravel contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Tracehatch SDK
|
|
2
|
+
|
|
3
|
+
Record supported model calls, tool steps and sessions from your Node.js agent.
|
|
4
|
+
See each run in Tracehatch: what called what, where it failed, how long it
|
|
5
|
+
took and what it cost.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
For guided installation, open your project in Tracehatch and copy its setup command.
|
|
10
|
+
Run it in your existing server application. The installer detects the framework
|
|
11
|
+
and package manager, connects through your browser, and previews its file changes.
|
|
12
|
+
It starts with prompt/output capture disabled and lets you choose what to record.
|
|
13
|
+
|
|
14
|
+
For manual installation, install in the **server package that makes model calls**:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install @tracehatch/sdk@0.3.0
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The hosted API default is `https://api.tracehatch.com`. Open your project at
|
|
21
|
+
[tracehatch.com](https://tracehatch.com) to view its runs.
|
|
22
|
+
|
|
23
|
+
For an installation that uses the dashboard-hosted archive:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm install https://tracehatch.com/downloads/tracehatch-sdk-0.3.0.tgz
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Create a project API key in Tracehatch and set it in your server's environment:
|
|
30
|
+
|
|
31
|
+
```dotenv
|
|
32
|
+
TRACEHATCH_API_KEY=your-project-api-key
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Use `.env.local` for Next.js. Other Node apps need their existing environment
|
|
36
|
+
loader or Node.js 22+ `--env-file` option; writing an `.env` file alone does not
|
|
37
|
+
load it. Keep the file ignored by Git and restart the server after changing it.
|
|
38
|
+
|
|
39
|
+
Start a plain Node app with the SDK loaded first:
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
node --import @tracehatch/sdk/auto server.js
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
or make `import "@tracehatch/sdk/auto"` the first line of your entry
|
|
46
|
+
file (`require("@tracehatch/sdk/auto")` in CommonJS). For a
|
|
47
|
+
self-hosted or local Tracehatch API, also set `TRACEHATCH_BASE_URL`. Hosted Tracehatch
|
|
48
|
+
uses the default API address.
|
|
49
|
+
|
|
50
|
+
Next.js initializes the SDK in `instrumentation.ts`, beside `app` or `pages`
|
|
51
|
+
(inside `src` when applicable). See `GUIDE.md` for the Node-runtime recipe.
|
|
52
|
+
The automatic import captures bodies by default; use explicit
|
|
53
|
+
`init({ captureBodies: false })` to omit prompts and outputs.
|
|
54
|
+
|
|
55
|
+
Restart your app, trigger one of its existing AI actions, and open the matching
|
|
56
|
+
run in **Traces**. A successful connection check confirms delivery separately;
|
|
57
|
+
it does not prove that your application initialized the SDK or captured a model.
|
|
58
|
+
|
|
59
|
+
## What is recorded
|
|
60
|
+
|
|
61
|
+
- **Model calls** made by any client or framework through `fetch`: OpenAI
|
|
62
|
+
chat, Responses and embeddings; Anthropic messages; OpenAI-compatible hosts
|
|
63
|
+
such as Azure OpenAI, Groq, Mistral, Together, OpenRouter, DeepSeek, xAI,
|
|
64
|
+
Fireworks, Cerebras, Gemini's compatible endpoint and Ollama. Streams are
|
|
65
|
+
assembled; tokens, time to first token, finish reasons, provider errors and
|
|
66
|
+
client retries are captured.
|
|
67
|
+
- **Runs**: one per inbound HTTP request that made a model call, or one per
|
|
68
|
+
agent loop for calls outside a request. A response that asks for tools and
|
|
69
|
+
the follow-up call with the tool results belong to the same run.
|
|
70
|
+
- **Sessions and users**: the `x-tracehatch-session-id` and `x-tracehatch-user-id`
|
|
71
|
+
request headers, OpenAI `conversation` / `previous_response_id`, the `user`
|
|
72
|
+
field or Anthropic `metadata.user_id`, otherwise a fingerprint of the system
|
|
73
|
+
prompt and first user message, so every turn of a conversation lands in one
|
|
74
|
+
session.
|
|
75
|
+
- **Tool steps**: tool calls in a response paired with the tool results in the
|
|
76
|
+
next request become tool spans with arguments, output and duration.
|
|
77
|
+
- **Agent and release**: the nearest `package.json` name (or `TRACEHATCH_AGENT`)
|
|
78
|
+
and the deployment's commit (platform variables, git `HEAD`, or
|
|
79
|
+
`TRACEHATCH_RELEASE`).
|
|
80
|
+
|
|
81
|
+
## Add precision where you want it
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { trace, tool, setSession, flush } from "@tracehatch/sdk"
|
|
85
|
+
|
|
86
|
+
await trace("answer question", async (run) => {
|
|
87
|
+
setSession("conversation-42")
|
|
88
|
+
const docs = await tool("search docs", () => search(question))
|
|
89
|
+
// Model calls made in here are children of this run.
|
|
90
|
+
const answer = await answerWith(docs)
|
|
91
|
+
run.score({ name: "confidence", value: 0.9 })
|
|
92
|
+
return answer
|
|
93
|
+
})
|
|
94
|
+
await flush() // before a short-lived process or Lambda invocation returns
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Check the connection
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import "@tracehatch/sdk/auto"
|
|
101
|
+
import { trace, flushWithResult, shutdown } from "@tracehatch/sdk"
|
|
102
|
+
|
|
103
|
+
const traceId = await trace("tracehatch-setup-check", (run) => run.id)
|
|
104
|
+
console.log(await flushWithResult(), traceId)
|
|
105
|
+
await shutdown()
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`status: "accepted"` means the API stored the batch; open the trace id in your
|
|
109
|
+
project to confirm processing.
|
|
110
|
+
|
|
111
|
+
## Deploy after local verification
|
|
112
|
+
|
|
113
|
+
Commit the reviewed initialization, package manifest and lockfile. Create a
|
|
114
|
+
production environment key in Tracehatch and add it to your hosting provider's
|
|
115
|
+
private server settings. Local environment files are not deployed by the
|
|
116
|
+
installer. Redeploy, trigger a real AI action and inspect it in Tracehatch with
|
|
117
|
+
the production environment selected.
|
|
118
|
+
|
|
119
|
+
## Things to know
|
|
120
|
+
|
|
121
|
+
- Load the SDK before provider clients are created; they take `fetch` when
|
|
122
|
+
constructed. A client given its own `fetch` option is not captured.
|
|
123
|
+
- Node.js 18+, ESM, CommonJS and TypeScript, no runtime dependencies. Server
|
|
124
|
+
code only; never ship the key or the SDK to a browser.
|
|
125
|
+
- Serverless: Vercel functions hand the final flush to `waitUntil`; on AWS
|
|
126
|
+
Lambda `await flush()` before returning.
|
|
127
|
+
- `init({ captureBodies: false })` records sizes and usage without prompts or
|
|
128
|
+
outputs. Built-in redaction masks common secrets and personal data before
|
|
129
|
+
send, but it can miss values.
|
|
130
|
+
|
|
131
|
+
Full configuration, capture rules and provider details are in `GUIDE.md`,
|
|
132
|
+
included in the installed package.
|
|
133
|
+
|
|
134
|
+
MIT licensed.
|
package/dist/auto.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auto.ts"],"names":["state","init"],"mappings":"mEASKA,mBAAAA,CAAM,QAAQC,mBAAAA,EAAK","file":"auto.cjs","sourcesContent":["/**\n * `@tracehatch/sdk/auto`: import first (or `node --import`) and\n * every model call, inbound request and tool step is recorded with the\n * settings in `TRACEHATCH_API_KEY`, `TRACEHATCH_BASE_URL`, `TRACEHATCH_AGENT` and\n * `TRACEHATCH_RELEASE`. An explicit `init()` made earlier is kept.\n */\nimport { state } from \"./context.js\"\nimport { init } from \"./init.js\"\n\nif (!state.client) init()\n"]}
|
package/dist/auto.d.cts
ADDED
package/dist/auto.d.ts
ADDED
package/dist/auto.js
ADDED
package/dist/auto.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auto.ts"],"names":["state","init"],"mappings":"sCASKA,CAAAA,CAAM,QAAQC,CAAAA,EAAK","file":"auto.js","sourcesContent":["/**\n * `@tracehatch/sdk/auto`: import first (or `node --import`) and\n * every model call, inbound request and tool step is recorded with the\n * settings in `TRACEHATCH_API_KEY`, `TRACEHATCH_BASE_URL`, `TRACEHATCH_AGENT` and\n * `TRACEHATCH_RELEASE`. An explicit `init()` made earlier is kept.\n */\nimport { state } from \"./context.js\"\nimport { init } from \"./init.js\"\n\nif (!state.client) init()\n"]}
|