Guarantees
Monitoring that can take down the thing it monitors is worse than no monitoring.
The SDK is built around one constraint: it must be impossible for AgentWatch to affect your agent's behaviour. Everything below follows from that.
It cannot add latency
Traces are POSTed outside the awaited path. Your wrapped function resolves, the value returns to your caller, and the delivery happens after that. The request your user is waiting on never waits on ours.
- The
timeoutMsceiling exists so a hung connection cannot accumulate open sockets in a long-lived process — not to bound anything your caller is waiting on. - Payloads are capped at
maxPreviewLengthcharacters per field, so a pathologically long output cannot turn into a slow upload.
It cannot throw
Delivery failures are swallowed. A network partition, an expired key, an outage on our side — none of it produces an exception in your process. Your agent keeps serving traffic untraced, which is the correct failure mode.
That applies to wrapped executions, where nothing is waiting for a result to report. aw.trace() is the deliberate exception: you awaited it, so it resolves to { accepted, reason } and tells you what happened. It still never rejects.
The cost of that choice
debug: true to break that silence, or send one manual trace and read its reason.It cannot change behaviour
watch() returns a function with the same signature as the one you gave it. Arguments pass through untouched, the resolved value is returned unmodified, and thrown errors are re-thrown as-is — the same object, with its stack intact.
const runAgent = watch(myAgentFn, { apiKey, agentId })
// Both of these hold, before and after wrapping:
await runAgent(query) === await myAgentFn(query) // same value
await runAgent(bad).catch(e => e) // same error objectWhat leaves your process
Per traced call, one POST containing:
- The captured input and output, truncated to
maxPreviewLength. - Latency, status, and — where available — model, token counts, and tool names.
- Whatever you put in
metadataandagentInstructions.
Nothing is read from the environment beyond what you pass in, and no other arguments to your function are captured. If an input carries data you would rather not send, lower maxPreviewLength or redact before the wrapped boundary — see Configuration.
License
MIT. The scoring engine is not in the package — CUSUM and EWMA change-point detection, the LLM-judge correctness gate, and semantic-drift scoring all run server-side on the traces this client sends.