@sentrial/sdk 0.4.4 → 0.5.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 +369 -28
- package/dist/cli.cjs +1147 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.cts +35 -0
- package/dist/cli.d.ts +35 -0
- package/dist/cli.js +1108 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +4961 -1033
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +941 -468
- package/dist/index.d.ts +941 -468
- package/dist/index.js +4944 -1033
- package/dist/index.js.map +1 -1
- package/dist/types-BEf8OATP.d.cts +612 -0
- package/dist/types-BEf8OATP.d.ts +612 -0
- package/package.json +26 -12
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
+
import { C as CostParams, a as SentrialClientConfig, b as CreateSessionParams, G as GetVariantParams, V as VariantAssignment, R as RecordExperimentExposureParams, c as ExperimentExposureReceipt, T as TrackToolCallParams, d as Event, e as TrackDecisionParams, f as TrackErrorParams, g as Session, h as CompleteSessionParams, D as DeliveryDiagnostics, B as BeginParams, F as FinishParams } from './types-BEf8OATP.cjs';
|
|
2
|
+
export { A as ApiResponse, i as BatcherConfig, j as EventBatcher, k as EventType, L as LargeToolContentConfig, P as PiiBuiltinPatterns, l as PiiConfig, m as PiiCustomPattern, n as PiiField, o as PiiMode, p as ProductionVariantConfig, q as SessionStatus, r as hashValue, s as redactPayload, t as redactString, u as redactValue, v as replaceMatch } from './types-BEf8OATP.cjs';
|
|
3
|
+
|
|
4
|
+
/** The five builtin pattern categories, in their canonical (load-bearing) order. */
|
|
5
|
+
type PiiPatternKey = 'emails' | 'creditCards' | 'ssns' | 'phones' | 'ipAddresses';
|
|
6
|
+
/**
|
|
7
|
+
* Map of builtin pattern keys to their regex and label. ORDER MATTERS: consumers apply these in
|
|
8
|
+
* insertion order, replacing each match before the next pattern runs. Longer/more-specific number
|
|
9
|
+
* patterns MUST come before shorter ones — creditCards + ssns BEFORE phones — otherwise the PHONE
|
|
10
|
+
* pattern's separator alternative can eat a phone-shaped tail of a separator-formatted card (e.g.
|
|
11
|
+
* "411-111-111-1111" -> "411-[PHONE]"), leaking the leading digits; and for a bare 16-digit card
|
|
12
|
+
* the PHONE pattern would eat the trailing 10 digits, leaking the leading 6 (e.g.
|
|
13
|
+
* "4111111111111111" -> "411111[PHONE]"). Bare digit runs are already immune via PHONE_PATTERN's
|
|
14
|
+
* (?<!\d) guard. Kept in lockstep with the Python `_BUILTIN_PATTERNS`.
|
|
15
|
+
*/
|
|
16
|
+
declare const BUILTIN_PATTERNS: Record<PiiPatternKey, {
|
|
17
|
+
pattern: RegExp;
|
|
18
|
+
label: string;
|
|
19
|
+
validate?: (match: string) => boolean;
|
|
20
|
+
}>;
|
|
21
|
+
|
|
22
|
+
/** Process-local OTLP transport observations. No URLs, bodies, IDs or credentials retained. */
|
|
23
|
+
declare const SERVER_STAGES: readonly ["body", "decode", "ingest", "resolve", "transaction", "postprocess", "total"];
|
|
24
|
+
type Stage = (typeof SERVER_STAGES)[number];
|
|
25
|
+
type AttemptOutcome = 'httpSuccess' | 'httpError' | 'transportError' | 'timeout' | 'cancelled' | 'bodyError';
|
|
26
|
+
type DurationSummary = {
|
|
27
|
+
measured: number;
|
|
28
|
+
unavailable: number;
|
|
29
|
+
totalMs: number;
|
|
30
|
+
minMs: number | null;
|
|
31
|
+
maxMs: number | null;
|
|
32
|
+
};
|
|
33
|
+
type ServerDurations = Record<Stage, number | null>;
|
|
34
|
+
type TransportAttempt = {
|
|
35
|
+
retry: boolean;
|
|
36
|
+
outcome: AttemptOutcome;
|
|
37
|
+
status: number | null;
|
|
38
|
+
elapsedMs: number | null;
|
|
39
|
+
serverTiming: ServerDurations;
|
|
40
|
+
acknowledgment: {
|
|
41
|
+
acceptedSpans: number | null;
|
|
42
|
+
rejectedSpans: number | null;
|
|
43
|
+
} | null;
|
|
44
|
+
};
|
|
45
|
+
type TransportDiagnosticsSnapshot = {
|
|
46
|
+
scope: 'otlp';
|
|
47
|
+
recentLimit: 32;
|
|
48
|
+
requests: {
|
|
49
|
+
started: number;
|
|
50
|
+
inFlight: number;
|
|
51
|
+
returned: number;
|
|
52
|
+
failed: number;
|
|
53
|
+
cancelled: number;
|
|
54
|
+
duration: DurationSummary;
|
|
55
|
+
};
|
|
56
|
+
attempts: {
|
|
57
|
+
started: number;
|
|
58
|
+
inFlight: number;
|
|
59
|
+
retries: number;
|
|
60
|
+
outcomes: Record<AttemptOutcome, number>;
|
|
61
|
+
duration: DurationSummary;
|
|
62
|
+
};
|
|
63
|
+
acknowledgments: {
|
|
64
|
+
complete: number;
|
|
65
|
+
partial: number;
|
|
66
|
+
unknown: number;
|
|
67
|
+
};
|
|
68
|
+
serverTiming: Record<Stage, DurationSummary>;
|
|
69
|
+
recent: TransportAttempt[];
|
|
70
|
+
};
|
|
71
|
+
|
|
1
72
|
/**
|
|
2
73
|
* Async-safe context variables for the Sentrial SDK.
|
|
3
74
|
*
|
|
@@ -99,28 +170,6 @@ declare function getSessionContext(): string | null;
|
|
|
99
170
|
* Set the default client for wrappers.
|
|
100
171
|
*/
|
|
101
172
|
declare function setDefaultClient(client: SentrialClient): void;
|
|
102
|
-
/**
|
|
103
|
-
* Wrap an OpenAI client to automatically track all LLM calls.
|
|
104
|
-
*
|
|
105
|
-
* @param client - OpenAI client instance
|
|
106
|
-
* @param options - Wrapper options
|
|
107
|
-
* @returns The same client, now with auto-tracking enabled
|
|
108
|
-
*
|
|
109
|
-
* @example
|
|
110
|
-
* ```ts
|
|
111
|
-
* import OpenAI from 'openai';
|
|
112
|
-
* import { wrapOpenAI, configure } from '@sentrial/sdk';
|
|
113
|
-
*
|
|
114
|
-
* configure({ apiKey: 'sentrial_live_xxx' });
|
|
115
|
-
* const openai = wrapOpenAI(new OpenAI());
|
|
116
|
-
*
|
|
117
|
-
* // Now use client normally - all calls are tracked!
|
|
118
|
-
* const response = await openai.chat.completions.create({
|
|
119
|
-
* model: 'gpt-4o',
|
|
120
|
-
* messages: [{ role: 'user', content: 'Hello' }],
|
|
121
|
-
* });
|
|
122
|
-
* ```
|
|
123
|
-
*/
|
|
124
173
|
declare function wrapOpenAI<T extends object>(client: T, options?: {
|
|
125
174
|
trackWithoutSession?: boolean;
|
|
126
175
|
}): T;
|
|
@@ -189,421 +238,36 @@ declare function wrapGoogle<T extends object>(model: T, options?: {
|
|
|
189
238
|
*/
|
|
190
239
|
declare function wrapLLM<T extends object>(client: T, provider?: 'openai' | 'anthropic' | 'google'): T;
|
|
191
240
|
|
|
192
|
-
/**
|
|
193
|
-
*
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
/** How redacted values are replaced */
|
|
199
|
-
type PiiMode = 'label' | 'hash' | 'remove';
|
|
200
|
-
/** Fields that can be redacted in SDK payloads */
|
|
201
|
-
type PiiField = 'userInput' | 'assistantOutput' | 'toolInput' | 'toolOutput' | 'metadata' | 'reasoning' | 'failureReason';
|
|
202
|
-
/** A custom regex pattern with a human-readable label */
|
|
203
|
-
interface PiiCustomPattern {
|
|
204
|
-
pattern: RegExp;
|
|
205
|
-
label: string;
|
|
206
|
-
}
|
|
207
|
-
/** Toggle individual builtin pattern categories */
|
|
208
|
-
interface PiiBuiltinPatterns {
|
|
209
|
-
emails?: boolean;
|
|
210
|
-
phones?: boolean;
|
|
211
|
-
ssns?: boolean;
|
|
212
|
-
creditCards?: boolean;
|
|
213
|
-
ipAddresses?: boolean;
|
|
214
|
-
}
|
|
215
|
-
/** Full PII redaction configuration */
|
|
216
|
-
interface PiiConfig {
|
|
217
|
-
/** Enable PII redaction (default: false) */
|
|
218
|
-
enabled: boolean;
|
|
219
|
-
/** Which payload fields to redact (default: DEFAULT_FIELDS) */
|
|
220
|
-
fields?: PiiField[];
|
|
221
|
-
/** Replacement mode (default: 'label') */
|
|
222
|
-
mode?: PiiMode;
|
|
223
|
-
/** Enable enhanced detection heuristics (reserved for future use) */
|
|
224
|
-
enhancedDetection?: boolean;
|
|
225
|
-
/** Toggle builtin patterns (default: all enabled) */
|
|
226
|
-
builtinPatterns?: PiiBuiltinPatterns;
|
|
227
|
-
/** Additional custom patterns to apply */
|
|
228
|
-
customPatterns?: PiiCustomPattern[];
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Hash a value using SHA-256 and return the first 6 hex characters.
|
|
232
|
-
* Used in 'hash' mode to produce a short, deterministic pseudonym.
|
|
233
|
-
*/
|
|
234
|
-
declare function hashValue(value: string): string;
|
|
235
|
-
/**
|
|
236
|
-
* Produce the replacement string for a matched PII value.
|
|
237
|
-
*
|
|
238
|
-
* - 'label' mode: [LABEL]
|
|
239
|
-
* - 'hash' mode: [PII:abcdef]
|
|
240
|
-
* - 'remove' mode: '' (empty string)
|
|
241
|
-
*/
|
|
242
|
-
declare function replaceMatch(match: string, label: string, mode: PiiMode): string;
|
|
243
|
-
/**
|
|
244
|
-
* Run all enabled builtin and custom patterns against a single string,
|
|
245
|
-
* replacing every match according to the specified mode.
|
|
246
|
-
*/
|
|
247
|
-
declare function redactString(value: string, mode: PiiMode, builtinPatterns: Required<PiiBuiltinPatterns>, customPatterns: PiiCustomPattern[]): string;
|
|
248
|
-
/**
|
|
249
|
-
* Recursively redact PII from a value. Handles strings, arrays, plain objects,
|
|
250
|
-
* and passes through primitives (numbers, booleans, null, undefined) unchanged.
|
|
251
|
-
*/
|
|
252
|
-
declare function redactValue(value: unknown, mode: PiiMode, builtinPatterns: Required<PiiBuiltinPatterns>, customPatterns: PiiCustomPattern[]): unknown;
|
|
253
|
-
/**
|
|
254
|
-
* Main entry point: redact configured fields in a payload object.
|
|
255
|
-
*
|
|
256
|
-
* Only fields listed in the PiiConfig (or DEFAULT_FIELDS) are redacted.
|
|
257
|
-
* Other fields are returned unchanged.
|
|
258
|
-
*
|
|
259
|
-
* @param payload - The data payload (e.g., session params, tool call params)
|
|
260
|
-
* @param config - PII configuration from the client
|
|
261
|
-
* @returns A new payload object with PII redacted from the configured fields
|
|
262
|
-
*/
|
|
263
|
-
declare function redactPayload(payload: Record<string, unknown>, config: PiiConfig): Record<string, unknown>;
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* EventBatcher — fire-and-forget event queue with periodic flushing.
|
|
267
|
-
*
|
|
268
|
-
* Buffers tracking events (tool calls, decisions, errors, generic events)
|
|
269
|
-
* and flushes them in batches to reduce HTTP overhead. Session lifecycle
|
|
270
|
-
* calls (createSession, completeSession) bypass the batcher entirely
|
|
271
|
-
* because they need synchronous responses.
|
|
272
|
-
*
|
|
273
|
-
* Flush triggers:
|
|
274
|
-
* 1. Timer — every `flushIntervalMs` (default 1 000 ms)
|
|
275
|
-
* 2. Queue size — when queue reaches `flushThreshold` (default 10)
|
|
276
|
-
* 3. Manual — `flush()` or `shutdown()`
|
|
277
|
-
* 4. Process exit — `beforeExit` handler
|
|
278
|
-
*
|
|
279
|
-
* Back-pressure: queue is capped at `maxQueueSize` (default 1 000).
|
|
280
|
-
* When full, oldest events are dropped and a warning is logged.
|
|
281
|
-
*/
|
|
282
|
-
interface BatcherConfig {
|
|
283
|
-
/** Whether batching is enabled (default: false — all calls are immediate) */
|
|
284
|
-
enabled?: boolean;
|
|
285
|
-
/** Milliseconds between automatic flushes (default: 1000) */
|
|
286
|
-
flushIntervalMs?: number;
|
|
287
|
-
/** Flush when the queue reaches this many items (default: 10) */
|
|
288
|
-
flushThreshold?: number;
|
|
289
|
-
/** Maximum queue size before dropping oldest events (default: 1000) */
|
|
290
|
-
maxQueueSize?: number;
|
|
291
|
-
}
|
|
292
|
-
/** Signature for the function that actually sends an event to the API. */
|
|
293
|
-
type SendFn = (method: string, url: string, body: unknown) => Promise<unknown>;
|
|
294
|
-
declare class EventBatcher {
|
|
295
|
-
private readonly queue;
|
|
296
|
-
private readonly flushIntervalMs;
|
|
297
|
-
private readonly flushThreshold;
|
|
298
|
-
private readonly maxQueueSize;
|
|
299
|
-
private timer;
|
|
300
|
-
private sendFn;
|
|
301
|
-
private flushing;
|
|
302
|
-
private shutdownCalled;
|
|
303
|
-
private readonly exitHandler;
|
|
304
|
-
constructor(sendFn: SendFn, config?: BatcherConfig);
|
|
305
|
-
/**
|
|
306
|
-
* Enqueue an event for batched delivery.
|
|
307
|
-
*
|
|
308
|
-
* If the queue hits `flushThreshold`, an automatic flush is triggered.
|
|
309
|
-
* If the queue is full (`maxQueueSize`), the oldest event is dropped.
|
|
310
|
-
*/
|
|
311
|
-
enqueue(method: string, url: string, body: unknown): void;
|
|
312
|
-
/**
|
|
313
|
-
* Flush all queued events to the API.
|
|
314
|
-
*
|
|
315
|
-
* Drains the queue and fires all requests in parallel. Safe to call
|
|
316
|
-
* concurrently — only one flush runs at a time.
|
|
317
|
-
*/
|
|
318
|
-
flush(): Promise<void>;
|
|
319
|
-
/**
|
|
320
|
-
* Stop the batcher: clear the timer, flush remaining events, remove exit handler.
|
|
321
|
-
*/
|
|
322
|
-
shutdown(): Promise<void>;
|
|
323
|
-
/** Number of events currently queued. */
|
|
324
|
-
get size(): number;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Type definitions for the Sentrial TypeScript SDK
|
|
329
|
-
*/
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Event types that can be tracked in a session
|
|
333
|
-
*/
|
|
334
|
-
declare enum EventType {
|
|
335
|
-
TOOL_CALL = "tool_call",
|
|
336
|
-
LLM_DECISION = "llm_decision",
|
|
337
|
-
STATE_CHANGE = "state_change",
|
|
338
|
-
ERROR = "error"
|
|
339
|
-
}
|
|
340
|
-
/**
|
|
341
|
-
* Session status values
|
|
342
|
-
*/
|
|
343
|
-
type SessionStatus = 'running' | 'completed' | 'failed' | 'cancelled';
|
|
344
|
-
/**
|
|
345
|
-
* Configuration options for SentrialClient
|
|
346
|
-
*/
|
|
347
|
-
interface SentrialClientConfig {
|
|
348
|
-
/** API key for authentication (defaults to SENTRIAL_API_KEY env var) */
|
|
349
|
-
apiKey?: string;
|
|
350
|
-
/** URL of the Sentrial API server (defaults to SENTRIAL_API_URL env var or production) */
|
|
351
|
-
apiUrl?: string;
|
|
352
|
-
/**
|
|
353
|
-
* If true (default), SDK errors are logged but won't crash your app.
|
|
354
|
-
* Set to false during development to see full errors.
|
|
355
|
-
*/
|
|
356
|
-
failSilently?: boolean;
|
|
357
|
-
/**
|
|
358
|
-
* PII redaction configuration.
|
|
359
|
-
* - Pass a PiiConfig object for full control.
|
|
360
|
-
* - Pass `true` to fetch the org's PII config from the server automatically.
|
|
361
|
-
*/
|
|
362
|
-
pii?: PiiConfig | boolean;
|
|
363
|
-
/**
|
|
364
|
-
* Event batching configuration.
|
|
365
|
-
* When enabled, fire-and-forget tracking calls (trackToolCall, trackDecision,
|
|
366
|
-
* trackError, trackEvent) are queued and flushed periodically instead of
|
|
367
|
-
* being sent immediately. Session lifecycle calls (createSession,
|
|
368
|
-
* completeSession) always bypass the batcher.
|
|
369
|
-
*/
|
|
370
|
-
batching?: BatcherConfig;
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Parameters for creating a new session
|
|
374
|
-
*/
|
|
375
|
-
interface CreateSessionParams {
|
|
376
|
-
/** Name of the session */
|
|
377
|
-
name: string;
|
|
378
|
-
/** Identifier for the agent type (used for grouping) */
|
|
379
|
-
agentName: string;
|
|
380
|
-
/** External user ID (for grouping sessions by end-user) */
|
|
381
|
-
userId: string;
|
|
382
|
-
/** Optional parent session ID for multi-agent hierarchies */
|
|
383
|
-
parentSessionId?: string;
|
|
384
|
-
/** Optional conversation ID to group related sessions into a thread */
|
|
385
|
-
convoId?: string;
|
|
386
|
-
/** Optional metadata */
|
|
387
|
-
metadata?: Record<string, unknown>;
|
|
241
|
+
/** Portable, payload-free context for a separately instrumented worker session.
|
|
242
|
+
* This is correlation metadata, never an authorization token or proof of outcome. */
|
|
243
|
+
interface ActionIdentity {
|
|
244
|
+
actionId: string;
|
|
245
|
+
attemptId?: string;
|
|
246
|
+
retryOfAttemptId?: string;
|
|
388
247
|
}
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
interface CompleteSessionParams {
|
|
393
|
-
/** Session ID */
|
|
394
|
-
sessionId: string;
|
|
395
|
-
/** Whether the session successfully completed its goal (default: true) */
|
|
396
|
-
success?: boolean;
|
|
397
|
-
/** If success=false, why did it fail? */
|
|
398
|
-
failureReason?: string;
|
|
399
|
-
/** Total estimated cost in USD for this session */
|
|
400
|
-
estimatedCost?: number;
|
|
401
|
-
/** Custom metrics (e.g., { customer_satisfaction: 4.5, steps_taken: 3 }) */
|
|
402
|
-
customMetrics?: Record<string, number>;
|
|
403
|
-
/** Duration in milliseconds (auto-calculated if not provided) */
|
|
404
|
-
durationMs?: number;
|
|
405
|
-
/** Number of prompt/input tokens used */
|
|
406
|
-
promptTokens?: number;
|
|
407
|
-
/** Number of completion/output tokens used */
|
|
408
|
-
completionTokens?: number;
|
|
409
|
-
/** Total tokens used (prompt + completion) */
|
|
410
|
-
totalTokens?: number;
|
|
411
|
-
/** The user's original query/input for this session */
|
|
412
|
-
userInput?: string;
|
|
413
|
-
/** The final assistant response for this session */
|
|
414
|
-
assistantOutput?: string;
|
|
415
|
-
/** Alias for assistantOutput - the final output text */
|
|
416
|
-
output?: string;
|
|
248
|
+
interface HandoffContext extends ActionIdentity {
|
|
249
|
+
version: 'sentrial-handoff-v1';
|
|
250
|
+
parentSessionId: string;
|
|
417
251
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
id: string;
|
|
423
|
-
agentId?: string;
|
|
424
|
-
parentSessionId?: string;
|
|
425
|
-
convoId?: string;
|
|
426
|
-
name: string;
|
|
427
|
-
agentName: string;
|
|
428
|
-
userId: string;
|
|
429
|
-
status: SessionStatus;
|
|
430
|
-
totalEvents: number;
|
|
431
|
-
userInput?: string;
|
|
432
|
-
assistantOutput?: string;
|
|
433
|
-
success?: boolean;
|
|
434
|
-
failureReason?: string;
|
|
435
|
-
estimatedCost?: number;
|
|
436
|
-
customMetrics?: Record<string, unknown>;
|
|
437
|
-
score?: number;
|
|
438
|
-
metricScores?: Record<string, unknown>;
|
|
439
|
-
metricReasonings?: Record<string, string>;
|
|
440
|
-
promptTokens?: number;
|
|
441
|
-
completionTokens?: number;
|
|
442
|
-
totalTokens?: number;
|
|
443
|
-
durationMs?: number;
|
|
444
|
-
metadata?: Record<string, unknown>;
|
|
445
|
-
createdAt: Date;
|
|
446
|
-
updatedAt: Date;
|
|
447
|
-
completedAt?: Date;
|
|
448
|
-
}
|
|
449
|
-
/**
|
|
450
|
-
* Parameters for tracking a tool call event
|
|
451
|
-
*/
|
|
452
|
-
interface TrackToolCallParams {
|
|
453
|
-
/** Session ID */
|
|
454
|
-
sessionId: string;
|
|
455
|
-
/** Name of the tool */
|
|
456
|
-
toolName: string;
|
|
457
|
-
/** Tool input data */
|
|
458
|
-
toolInput: Record<string, unknown>;
|
|
459
|
-
/** Tool output data */
|
|
460
|
-
toolOutput: Record<string, unknown>;
|
|
461
|
-
/** Optional reasoning for why this tool was called */
|
|
462
|
-
reasoning?: string;
|
|
463
|
-
/** Estimated cost in USD for this tool call */
|
|
464
|
-
estimatedCost?: number;
|
|
465
|
-
/** Error details if the tool failed */
|
|
466
|
-
toolError?: Record<string, unknown>;
|
|
467
|
-
/** Number of tokens used by this tool call (for LLM-based tools) */
|
|
468
|
-
tokenCount?: number;
|
|
469
|
-
/** OpenTelemetry trace ID for distributed tracing */
|
|
470
|
-
traceId?: string;
|
|
471
|
-
/** OpenTelemetry span ID for distributed tracing */
|
|
472
|
-
spanId?: string;
|
|
473
|
-
/** Additional metadata */
|
|
474
|
-
metadata?: Record<string, unknown>;
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
|
-
* Parameters for tracking an LLM decision event
|
|
478
|
-
*/
|
|
479
|
-
interface TrackDecisionParams {
|
|
480
|
-
/** Session ID */
|
|
481
|
-
sessionId: string;
|
|
482
|
-
/** Decision reasoning */
|
|
483
|
-
reasoning: string;
|
|
484
|
-
/** Alternative options considered */
|
|
485
|
-
alternatives?: string[];
|
|
486
|
-
/** Confidence score (0.0 to 1.0) */
|
|
487
|
-
confidence?: number;
|
|
488
|
-
/** Estimated cost in USD for this decision */
|
|
489
|
-
estimatedCost?: number;
|
|
490
|
-
/** Number of tokens used */
|
|
491
|
-
tokenCount?: number;
|
|
492
|
-
/** OpenTelemetry trace ID for distributed tracing */
|
|
493
|
-
traceId?: string;
|
|
494
|
-
/** OpenTelemetry span ID for distributed tracing */
|
|
495
|
-
spanId?: string;
|
|
496
|
-
/** Additional metadata */
|
|
497
|
-
metadata?: Record<string, unknown>;
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Parameters for tracking an error event
|
|
501
|
-
*/
|
|
502
|
-
interface TrackErrorParams {
|
|
503
|
-
/** Session ID */
|
|
504
|
-
sessionId: string;
|
|
505
|
-
/** Error message */
|
|
506
|
-
errorMessage: string;
|
|
507
|
-
/** Type of error (e.g., "ValueError", "APIError") */
|
|
508
|
-
errorType?: string;
|
|
509
|
-
/** Name of the tool that caused the error (if applicable) */
|
|
510
|
-
toolName?: string;
|
|
511
|
-
/** Stack trace for debugging */
|
|
512
|
-
stackTrace?: string;
|
|
513
|
-
/** OpenTelemetry trace ID for distributed tracing */
|
|
514
|
-
traceId?: string;
|
|
515
|
-
/** OpenTelemetry span ID for distributed tracing */
|
|
516
|
-
spanId?: string;
|
|
517
|
-
/** Additional metadata */
|
|
518
|
-
metadata?: Record<string, unknown>;
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* Event data returned from the API
|
|
522
|
-
*/
|
|
523
|
-
interface Event {
|
|
524
|
-
id: string;
|
|
525
|
-
sessionId: string;
|
|
526
|
-
eventType: EventType;
|
|
527
|
-
timestamp: Date;
|
|
528
|
-
sequenceNumber: number;
|
|
529
|
-
toolName?: string;
|
|
530
|
-
toolInput?: Record<string, unknown>;
|
|
531
|
-
toolOutput?: Record<string, unknown>;
|
|
532
|
-
toolError?: Record<string, unknown>;
|
|
533
|
-
reasoning?: string;
|
|
534
|
-
alternativesConsidered?: string[];
|
|
535
|
-
confidence?: number;
|
|
536
|
-
stateBefore?: Record<string, unknown>;
|
|
537
|
-
stateAfter?: Record<string, unknown>;
|
|
538
|
-
stateDiff?: Record<string, unknown>;
|
|
539
|
-
estimatedCost?: number;
|
|
540
|
-
tokenCount?: number;
|
|
541
|
-
metadata?: Record<string, unknown>;
|
|
542
|
-
traceId?: string;
|
|
543
|
-
spanId?: string;
|
|
544
|
-
}
|
|
545
|
-
/**
|
|
546
|
-
* Parameters for beginning an interaction
|
|
547
|
-
*/
|
|
548
|
-
interface BeginParams {
|
|
549
|
-
/** External user ID for grouping sessions */
|
|
550
|
-
userId: string;
|
|
551
|
-
/** Event type/name (e.g., "chat_message", "search_query") */
|
|
552
|
-
event: string;
|
|
553
|
-
/** Optional input data for the interaction */
|
|
554
|
-
input?: string;
|
|
555
|
-
/** Optional custom event ID (auto-generated UUID if not provided) */
|
|
556
|
-
eventId?: string;
|
|
557
|
-
/** Optional conversation ID to group related interactions */
|
|
558
|
-
convoId?: string;
|
|
559
|
-
/** Optional additional metadata */
|
|
560
|
-
metadata?: Record<string, unknown>;
|
|
561
|
-
}
|
|
562
|
-
/**
|
|
563
|
-
* Parameters for finishing an interaction
|
|
564
|
-
*/
|
|
565
|
-
interface FinishParams {
|
|
566
|
-
/** Output/response from the interaction */
|
|
567
|
-
output?: string;
|
|
568
|
-
/** Whether the interaction succeeded (default: true) */
|
|
569
|
-
success?: boolean;
|
|
570
|
-
/** Reason for failure if success=false */
|
|
571
|
-
failureReason?: string;
|
|
572
|
-
/** Total estimated cost in USD */
|
|
573
|
-
estimatedCost?: number;
|
|
574
|
-
/** Custom metrics */
|
|
575
|
-
customMetrics?: Record<string, number>;
|
|
576
|
-
/** Number of prompt/input tokens used */
|
|
577
|
-
promptTokens?: number;
|
|
578
|
-
/** Number of completion/output tokens used */
|
|
579
|
-
completionTokens?: number;
|
|
580
|
-
/** Total tokens used */
|
|
581
|
-
totalTokens?: number;
|
|
582
|
-
/** Duration in milliseconds (auto-calculated from begin/finish if not provided) */
|
|
583
|
-
durationMs?: number;
|
|
584
|
-
}
|
|
585
|
-
/**
|
|
586
|
-
* Parameters for cost calculation
|
|
587
|
-
*/
|
|
588
|
-
interface CostParams {
|
|
589
|
-
/** Model name (e.g., "gpt-4", "claude-3-sonnet") */
|
|
590
|
-
model: string;
|
|
591
|
-
/** Number of input tokens */
|
|
592
|
-
inputTokens: number;
|
|
593
|
-
/** Number of output tokens */
|
|
594
|
-
outputTokens: number;
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* Standard API response wrapper
|
|
598
|
-
*/
|
|
599
|
-
interface ApiResponse<T> {
|
|
600
|
-
data?: T;
|
|
601
|
-
error?: {
|
|
602
|
-
code: string;
|
|
603
|
-
message: string;
|
|
604
|
-
details?: Record<string, unknown>;
|
|
605
|
-
};
|
|
252
|
+
interface DispatchContext extends ActionIdentity {
|
|
253
|
+
version: 'sentrial-handoff-v2';
|
|
254
|
+
parentSessionId: string;
|
|
255
|
+
dispatchId: string;
|
|
606
256
|
}
|
|
257
|
+
type HandoffOutcome = 'completed' | 'failed' | 'cancelled';
|
|
258
|
+
declare function actionMetadata(identity: ActionIdentity, metadata?: Record<string, unknown>): Record<string, unknown>;
|
|
259
|
+
declare function createHandoffContext(input: ActionIdentity & {
|
|
260
|
+
parentSessionId: string;
|
|
261
|
+
}): string;
|
|
262
|
+
declare function resumeHandoffContext(carrier: string, metadata?: Record<string, unknown>): {
|
|
263
|
+
parentSessionId: string;
|
|
264
|
+
metadata: Record<string, unknown>;
|
|
265
|
+
};
|
|
266
|
+
/** Use a new dispatchId for each execution; keep it stable only for transport retries. */
|
|
267
|
+
declare function createDispatchContext(input: ActionIdentity & {
|
|
268
|
+
parentSessionId: string;
|
|
269
|
+
dispatchId: string;
|
|
270
|
+
}): string;
|
|
607
271
|
|
|
608
272
|
/**
|
|
609
273
|
* Cost calculation utilities for various LLM providers
|
|
@@ -660,15 +324,140 @@ declare function calculateAnthropicCost(params: CostParams): number;
|
|
|
660
324
|
* ```
|
|
661
325
|
*/
|
|
662
326
|
declare function calculateGoogleCost(params: CostParams): number;
|
|
663
|
-
|
|
664
327
|
/**
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
669
|
-
* -
|
|
670
|
-
* -
|
|
671
|
-
|
|
328
|
+
* Provider → cost dispatch. SINGLE source shared by the Vercel AI SDK wrapper (vercel.ts) and the
|
|
329
|
+
* Mastra SDK so a provider is billed identically across integrations — previously each carried its
|
|
330
|
+
* own switch and the Mastra copy dropped flat-rate providers to $0 (#488/#559).
|
|
331
|
+
*
|
|
332
|
+
* UNIFIED CROSS-SDK FALLBACK POLICY (lockstep with the Python SDK's `resolve_provider_cost` in
|
|
333
|
+
* packages/python-sdk/sentrial/pricing.py, guarded by tests/test_pricing_sync.py). The chain,
|
|
334
|
+
* identical in both SDKs:
|
|
335
|
+
* 1. Resolve the provider HINT tolerantly, by SUBSTRING — OTel-semconv spellings like
|
|
336
|
+
* 'az.ai.openai' or 'mistral_ai' resolve to the correct table (openai/anthropic/google first,
|
|
337
|
+
* then the flat-rate providers). A hint that resolves nothing falls back to inferring the
|
|
338
|
+
* provider from the model id (guessProvider), so an empty/'unknown'/'gateway' hint never
|
|
339
|
+
* forces the default block when the model id names its family.
|
|
340
|
+
* 2. A gateway 'creator/model' id whose creator is a FLAT-rate provider bills that flat rate
|
|
341
|
+
* regardless of the hint (an Anthropic-compatible gateway may carry a keyset hint).
|
|
342
|
+
* 3. openai/anthropic/google bill on their per-model tables; a gateway creator naming one of
|
|
343
|
+
* them wins the table choice and the slash prefix is always stripped for the key match. An
|
|
344
|
+
* UNKNOWN model within a known provider bills the provider's cheap-modern
|
|
345
|
+
* `fallback.{provider}` key (never an expensive legacy flagship).
|
|
346
|
+
* 4. Flat-rate providers (deepseek/cohere/mistral) bill their canonical `flatProviders` rate.
|
|
347
|
+
* 5. A GENUINELY unknown provider (including recognized-but-unpriced families like meta/llama
|
|
348
|
+
* and alibaba/qwen) bills the shared cheap `fallback.default` block — NEVER a silent $0,
|
|
349
|
+
* which would understate real spend. (Unified Jul 2026; this dispatch previously returned $0
|
|
350
|
+
* here while Python billed the default block.)
|
|
351
|
+
* All rates come from the canonical pricing.json (fallback.default included).
|
|
352
|
+
*/
|
|
353
|
+
declare function calculateCostByProvider(provider: string, model: string, inputTokens: number, outputTokens: number): number;
|
|
354
|
+
/**
|
|
355
|
+
* Usage shape accepted by {@link normalizeUsage} — supports both AI SDK v3/v4
|
|
356
|
+
* (promptTokens/completionTokens, OpenAI naming) and v5/v6 (inputTokens/outputTokens,
|
|
357
|
+
* generic naming). All fields optional; a `totalTokens` may be present on either.
|
|
358
|
+
*/
|
|
359
|
+
type NormalizableUsage = {
|
|
360
|
+
inputTokens?: number;
|
|
361
|
+
outputTokens?: number;
|
|
362
|
+
promptTokens?: number;
|
|
363
|
+
completionTokens?: number;
|
|
364
|
+
totalTokens?: number;
|
|
365
|
+
};
|
|
366
|
+
type NormalizedUsage = {
|
|
367
|
+
promptTokens: number;
|
|
368
|
+
completionTokens: number;
|
|
369
|
+
totalTokens: number;
|
|
370
|
+
};
|
|
371
|
+
/**
|
|
372
|
+
* Normalize LLM usage from any Vercel AI SDK version (and Mastra, which follows AI SDK naming)
|
|
373
|
+
* into a consistent { promptTokens, completionTokens, totalTokens } shape; returns undefined when
|
|
374
|
+
* there is no usage or every count is zero.
|
|
375
|
+
*
|
|
376
|
+
* AI SDK v3/v4 uses promptTokens/completionTokens (OpenAI naming); v5/v6 uses
|
|
377
|
+
* inputTokens/outputTokens (generic naming). This is the SINGLE canonical implementation shared by
|
|
378
|
+
* the Vercel AI SDK wrapper (vercel.ts) and the Mastra SDK, which previously each carried a
|
|
379
|
+
* divergent copy: vercel.ts coerced with `|| 0` (NaN/non-numeric -> 0) while mastra used `?? 0`
|
|
380
|
+
* (nullish-only, so a NaN or a non-numeric field could leak straight through). We adopt the SAFER
|
|
381
|
+
* `|| 0`-style semantics here via `firstTokenCount` (an explicit Number.isFinite guard applied to
|
|
382
|
+
* BOTH the field selection and the final value) so a malformed usage object can never surface
|
|
383
|
+
* NaN/garbage token counts or costs, and a NaN primary field (e.g. inputTokens) falls through to a
|
|
384
|
+
* valid secondary (promptTokens) rather than masking it — this does not change behavior for real
|
|
385
|
+
* numeric inputs, and only tightens the previously looser mastra path.
|
|
386
|
+
*/
|
|
387
|
+
declare function normalizeUsage(usage: NormalizableUsage | Record<string, unknown> | undefined): NormalizedUsage | undefined;
|
|
388
|
+
declare function guessProvider(modelId: string): string;
|
|
389
|
+
/**
|
|
390
|
+
* Canonical "normalize raw AI-SDK usage → prompt/completion/total token counts → cost via
|
|
391
|
+
* calculateCostByProvider(provider, model, ...)" derivation, shared by BOTH AI-SDK wrappers
|
|
392
|
+
* (Vercel `resolveTokensAndCost` call sites and the Mastra integration). Lives here — beside
|
|
393
|
+
* normalizeUsage + calculateCostByProvider — so the two wrappers can never drift on how they
|
|
394
|
+
* fold provider-extra token fields; previously only the Vercel copy folded Anthropic
|
|
395
|
+
* cachedInputTokens / Google reasoningTokens, so @sentrial/mastra silently under-billed
|
|
396
|
+
* cache-read-heavy Anthropic and thinking-heavy Google calls.
|
|
397
|
+
*
|
|
398
|
+
* totalTokens comes from normalizeUsage, which already falls back to promptTokens+completionTokens,
|
|
399
|
+
* so this preserves the stricter sum-fallback everywhere (no caller regresses to `?? 0`). The
|
|
400
|
+
* containment arithmetic gates each fold so subset-reporting providers (OpenAI) and AI-SDK v7
|
|
401
|
+
* (fields moved into inputTokenDetails/outputTokenDetails) can never double-count.
|
|
402
|
+
*/
|
|
403
|
+
/**
|
|
404
|
+
* Fold AI-SDK provider-extra token fields into flat prompt/completion/total counts.
|
|
405
|
+
*
|
|
406
|
+
* AI SDK v5/v6 report provider extras as separate top-level fields that our flat reads drop:
|
|
407
|
+
* - Anthropic: inputTokens EXCLUDES cache reads (cachedInputTokens is EXTRA; total = in+out)
|
|
408
|
+
* - Google: outputTokens EXCLUDES thinking (reasoningTokens; total = in+out+reasoning)
|
|
409
|
+
* OpenAI reports both as SUBSETS (already inside the flat counts), and v7 moved the fields into
|
|
410
|
+
* inputTokenDetails/outputTokenDetails (absent here) — so each fold is gated on the containment
|
|
411
|
+
* arithmetic so subset-reporting providers and v7 can never double-count. Shared by BOTH AI-SDK
|
|
412
|
+
* wrappers (resolveTokensAndCost + the Mastra per-step aggregator) so they can never drift.
|
|
413
|
+
*/
|
|
414
|
+
declare function foldProviderExtras(rawUsage: NormalizableUsage | Record<string, unknown> | undefined, provider: string, base: {
|
|
415
|
+
promptTokens: number;
|
|
416
|
+
completionTokens: number;
|
|
417
|
+
totalTokens: number;
|
|
418
|
+
}): {
|
|
419
|
+
promptTokens: number;
|
|
420
|
+
completionTokens: number;
|
|
421
|
+
totalTokens: number;
|
|
422
|
+
/**
|
|
423
|
+
* The Anthropic cache-READ tokens folded into `promptTokens` (0 when no fold applied). Reported
|
|
424
|
+
* token COUNTS stay cache-inclusive, but cost must weight this slice at
|
|
425
|
+
* {@link ANTHROPIC_CACHE_READ_MULTIPLIER} (0.1x), matching the direct wrapAnthropic path — so
|
|
426
|
+
* callers computing cost subtract 0.9x of this back out. See resolveTokensAndCost.
|
|
427
|
+
*/
|
|
428
|
+
foldedCachedInputTokens: number;
|
|
429
|
+
};
|
|
430
|
+
declare function resolveTokensAndCost(rawUsage: NormalizableUsage | Record<string, unknown> | undefined, provider: string, model: string,
|
|
431
|
+
/**
|
|
432
|
+
* Anthropic cache-READ tokens already folded into `rawUsage`'s prompt count by an UPSTREAM
|
|
433
|
+
* per-step pass (the Mastra aggregator folds each step before summing, then discards the raw
|
|
434
|
+
* `cachedInputTokens` field so foldProviderExtras here can't re-derive it). Pass the summed
|
|
435
|
+
* folded count so the 0.1x cache-read discount is still applied to the cost basis. Omit (0)
|
|
436
|
+
* for the normal single-usage path, where foldProviderExtras below computes the fold itself.
|
|
437
|
+
*/
|
|
438
|
+
preFoldedCachedInputTokens?: number): {
|
|
439
|
+
promptTokens: number;
|
|
440
|
+
completionTokens: number;
|
|
441
|
+
totalTokens: number;
|
|
442
|
+
cost: number;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
declare const AGENT_DIAGNOSTIC_CATEGORIES: readonly ["missing_context", "tool_problem", "missing_capability", "uncertain_completion"];
|
|
446
|
+
interface AgentDiagnosticInput {
|
|
447
|
+
/** Opaque identifier reused for subsequent reports within this session. */
|
|
448
|
+
reportId: string;
|
|
449
|
+
category: (typeof AGENT_DIAGNOSTIC_CATEGORIES)[number];
|
|
450
|
+
/** Both states are assertions by the agent, including claimed recovery. */
|
|
451
|
+
state: 'reported' | 'recovered';
|
|
452
|
+
description: string;
|
|
453
|
+
/** Optional related OTel span IDs in this trace; these are unverified references. */
|
|
454
|
+
relatedSpanIds?: readonly string[];
|
|
455
|
+
}
|
|
456
|
+
interface AgentDiagnostic extends Omit<AgentDiagnosticInput, 'relatedSpanIds'> {
|
|
457
|
+
revision: 'agent-diagnostic-v1';
|
|
458
|
+
origin: 'agent_report';
|
|
459
|
+
relatedSpanIds: readonly string[];
|
|
460
|
+
}
|
|
672
461
|
|
|
673
462
|
/**
|
|
674
463
|
* Sentrial Client for agent observability.
|
|
@@ -709,6 +498,18 @@ declare function calculateGoogleCost(params: CostParams): number;
|
|
|
709
498
|
* By default, the SDK fails silently (failSilently=true) to ensure
|
|
710
499
|
* monitoring never crashes your application. Set failSilently=false
|
|
711
500
|
* during development to see errors.
|
|
501
|
+
*
|
|
502
|
+
* Containment seam (the never-crash contract's enforcement point): every public tracking
|
|
503
|
+
* entrypoint on this client (and on Interaction) is wrapped by `containMethod` (see
|
|
504
|
+
* contained.ts and the application block at the bottom of this file) — the ENTIRE method
|
|
505
|
+
* body runs inside one try honoring `failSilently`. Any exception escaping a method body
|
|
506
|
+
* is caught at the method boundary: with failSilently=true (default) it is logged once per
|
|
507
|
+
* method and the method's degraded value is returned (null / undefined / a degraded
|
|
508
|
+
* Interaction); with failSilently=false it rethrows/rejects unchanged. Interior guards
|
|
509
|
+
* (toText, safeJsonStringify, capStateSnapshot, accumulate's safeNum, the toolError field
|
|
510
|
+
* accessors) remain for DATA QUALITY: they let a tracking call still succeed with degraded
|
|
511
|
+
* values instead of the seam dropping the whole call. Mirrors the Python SDK's
|
|
512
|
+
* `_wire.contained` decorations.
|
|
712
513
|
*/
|
|
713
514
|
declare class SentrialClient {
|
|
714
515
|
private readonly apiUrl;
|
|
@@ -719,8 +520,26 @@ declare class SentrialClient {
|
|
|
719
520
|
private piiHydrationPromise?;
|
|
720
521
|
private readonly _stateVar;
|
|
721
522
|
private readonly batcher?;
|
|
523
|
+
private readonly contentUploader?;
|
|
524
|
+
private readonly transportDiagnostics;
|
|
525
|
+
private readonly otlpSessions;
|
|
526
|
+
/** Every ambient OTel traceId this client has EVER consumed for a session — permanent (bounded,
|
|
527
|
+
* FIFO) because the server's otel-sess:<traceId> fallback key is permanent; see claimTraceId. */
|
|
528
|
+
private readonly consumedAmbientTraceIds;
|
|
529
|
+
private pendingExperimentAssignmentIds;
|
|
722
530
|
/** Per-session cost/token accumulator — populated by trackToolCall/trackDecision */
|
|
723
531
|
private sessionAccumulators;
|
|
532
|
+
/**
|
|
533
|
+
* Accumulators for sessions that never call completeSession() — e.g. a mastra stream the caller
|
|
534
|
+
* abandons without consuming, so its onFinish never fires — are evicted after this TTL so their
|
|
535
|
+
* bookkeeping can't linger. The TTL is measured from LAST ACTIVITY (accumulate() refreshes the
|
|
536
|
+
* entry's timestamp), so an actively-tracking long-running session is never dropped — only
|
|
537
|
+
* sessions idle for 2h expire. The accumulator is only a fallback for callers that don't pass
|
|
538
|
+
* explicit cost/tokens to finish (#561).
|
|
539
|
+
*/
|
|
540
|
+
private static readonly ACCUMULATOR_TTL_MS;
|
|
541
|
+
/** Hard cap on live accumulator entries — backstop for the TTL sweep (#561). */
|
|
542
|
+
private static readonly MAX_ACCUMULATORS;
|
|
724
543
|
private get currentState();
|
|
725
544
|
private set currentState(value);
|
|
726
545
|
constructor(config?: SentrialClientConfig);
|
|
@@ -737,7 +556,14 @@ declare class SentrialClient {
|
|
|
737
556
|
* Retries on transient failures (network errors, timeouts, 429/5xx).
|
|
738
557
|
* Up to MAX_RETRIES attempts with exponential backoff.
|
|
739
558
|
*/
|
|
559
|
+
/**
|
|
560
|
+
* Public wrapper around the retrying transport (exponential backoff, 10s timeout,
|
|
561
|
+
* PII redaction, structured ApiError/NetworkError) so callers like Experiment reuse
|
|
562
|
+
* it instead of hand-rolling a bare fetch (#482).
|
|
563
|
+
*/
|
|
564
|
+
apiRequest<T>(method: string, url: string, body?: unknown): Promise<T | null>;
|
|
740
565
|
private safeRequest;
|
|
566
|
+
private requestWithRetries;
|
|
741
567
|
private sleep;
|
|
742
568
|
private accumulate;
|
|
743
569
|
/**
|
|
@@ -746,26 +572,38 @@ declare class SentrialClient {
|
|
|
746
572
|
* @param params - Session creation parameters
|
|
747
573
|
* @returns Session ID, or null if the request failed and failSilently is true
|
|
748
574
|
*/
|
|
575
|
+
/** Build the dependency bundle the OTLP transport needs (private members accessed here). */
|
|
576
|
+
private otlpDeps;
|
|
749
577
|
createSession(params: CreateSessionParams): Promise<string | null>;
|
|
578
|
+
/**
|
|
579
|
+
* Get a sticky production experiment variant for this user/conversation.
|
|
580
|
+
*
|
|
581
|
+
* The returned assignment id is automatically linked to the next session
|
|
582
|
+
* created by this client. You can also pass `sessionId` to link immediately.
|
|
583
|
+
*/
|
|
584
|
+
getVariant(params: GetVariantParams): Promise<VariantAssignment>;
|
|
585
|
+
/** Report only after using this configuration. getVariant/createSession never imply exposure.
|
|
586
|
+
* Returns null on fail-silent delivery failure; safe to retry with the same fields. */
|
|
587
|
+
recordExperimentExposure(params: RecordExperimentExposureParams): Promise<ExperimentExposureReceipt | null>;
|
|
750
588
|
/**
|
|
751
589
|
* Track a tool call event
|
|
752
590
|
*
|
|
753
591
|
* @param params - Tool call parameters
|
|
754
|
-
* @returns
|
|
592
|
+
* @returns `null` — the OTLP transport emits a span and does not return a synchronous event
|
|
755
593
|
*/
|
|
756
594
|
trackToolCall(params: TrackToolCallParams): Promise<Event | null>;
|
|
757
595
|
/**
|
|
758
596
|
* Track an LLM decision event
|
|
759
597
|
*
|
|
760
598
|
* @param params - Decision parameters
|
|
761
|
-
* @returns
|
|
599
|
+
* @returns `null` — the OTLP transport emits a span and does not return a synchronous event
|
|
762
600
|
*/
|
|
763
601
|
trackDecision(params: TrackDecisionParams): Promise<Event | null>;
|
|
764
602
|
/**
|
|
765
603
|
* Track an error event
|
|
766
604
|
*
|
|
767
605
|
* @param params - Error parameters
|
|
768
|
-
* @returns
|
|
606
|
+
* @returns `null` — the OTLP transport emits a span and does not return a synchronous event
|
|
769
607
|
*/
|
|
770
608
|
trackError(params: TrackErrorParams): Promise<Event | null>;
|
|
771
609
|
/**
|
|
@@ -778,17 +616,47 @@ declare class SentrialClient {
|
|
|
778
616
|
/**
|
|
779
617
|
* Set the user input for a session
|
|
780
618
|
*
|
|
619
|
+
* Re-posts the session's root span (start-only, no end time) with the updated
|
|
620
|
+
* `gen_ai.input.messages` so the server refreshes `sessions.userInput` WITHOUT finalizing the
|
|
621
|
+
* session — it stays running. Always resolves `null` under the OTLP transport (no synchronous session row).
|
|
622
|
+
*
|
|
781
623
|
* @param sessionId - Session ID
|
|
782
624
|
* @param input - User input text
|
|
783
|
-
* @returns
|
|
625
|
+
* @returns `null` — the OTLP transport does not return a synchronous session row
|
|
784
626
|
*/
|
|
785
627
|
setInput(sessionId: string, input: string): Promise<Session | null>;
|
|
786
628
|
/**
|
|
787
|
-
*
|
|
629
|
+
* Merge additional metadata into a running session (post-begin).
|
|
630
|
+
*
|
|
631
|
+
* No network call: the merged metadata is carried on the finalize re-post, where the server
|
|
632
|
+
* merges it into the session's metadata (user keys top-level, later values win on a key
|
|
633
|
+
* collision; the reserved `otel` diagnostics key is preserved server-side). String values are
|
|
634
|
+
* redacted like begin()'s metadata.
|
|
788
635
|
*
|
|
789
|
-
* @param
|
|
790
|
-
* @
|
|
636
|
+
* @param sessionId - Session ID
|
|
637
|
+
* @param metadata - Metadata keys to merge into the session metadata
|
|
791
638
|
*/
|
|
639
|
+
setSessionMetadata(sessionId: string, metadata: Record<string, unknown>): void;
|
|
640
|
+
/**
|
|
641
|
+
* Set/override the session-level model for a running session (post-begin).
|
|
642
|
+
*
|
|
643
|
+
* No network call: carried on the finalize re-post as gen_ai.request.model and under the
|
|
644
|
+
* stable `model` key in the session metadata (the sessions table has no model column).
|
|
645
|
+
*
|
|
646
|
+
* @param sessionId - Session ID
|
|
647
|
+
* @param model - Model identifier (e.g. 'claude-sonnet-4-5')
|
|
648
|
+
*/
|
|
649
|
+
setSessionModel(sessionId: string, model: string): void;
|
|
650
|
+
/**
|
|
651
|
+
* Record an optional agent assertion, including claimed recovery. No model call,
|
|
652
|
+
* automatic error status, state snapshot, or verified failure label is created.
|
|
653
|
+
* Reuse reportId within this session for later observations; each is retained.
|
|
654
|
+
*/
|
|
655
|
+
reportDiagnostic(params: {
|
|
656
|
+
sessionId: string;
|
|
657
|
+
report: AgentDiagnosticInput;
|
|
658
|
+
}): Promise<null>;
|
|
659
|
+
/** Track a generic event. */
|
|
792
660
|
trackEvent(params: {
|
|
793
661
|
sessionId: string;
|
|
794
662
|
eventType: string;
|
|
@@ -801,7 +669,8 @@ declare class SentrialClient {
|
|
|
801
669
|
* This is the recommended way to close sessions for performance monitoring.
|
|
802
670
|
*
|
|
803
671
|
* @param params - Session completion parameters
|
|
804
|
-
* @returns
|
|
672
|
+
* @returns `null` — the OTLP transport re-posts the final root span and the server finalizes
|
|
673
|
+
* the session asynchronously, so no session row is returned synchronously.
|
|
805
674
|
*
|
|
806
675
|
* @example
|
|
807
676
|
* ```ts
|
|
@@ -825,14 +694,41 @@ declare class SentrialClient {
|
|
|
825
694
|
/**
|
|
826
695
|
* Flush any queued events immediately.
|
|
827
696
|
*
|
|
828
|
-
*
|
|
697
|
+
* Waits for pending content uploads before draining queued telemetry.
|
|
829
698
|
*/
|
|
830
699
|
flush(): Promise<void>;
|
|
700
|
+
/** Queue delivery health; null when batching is disabled. Contains no payloads. */
|
|
701
|
+
getDeliveryDiagnostics(): Readonly<DeliveryDiagnostics> | null;
|
|
702
|
+
/** Content upload counts only; submitted bytes may still await server verification. */
|
|
703
|
+
getContentUploadDiagnostics(): {
|
|
704
|
+
attempted: number;
|
|
705
|
+
submitted: number;
|
|
706
|
+
missing: number;
|
|
707
|
+
active: number;
|
|
708
|
+
pendingEvents: number;
|
|
709
|
+
} | null;
|
|
710
|
+
/** Bounded process-local OTLP request/response diagnostics, independent of batching. */
|
|
711
|
+
getTransportDiagnostics(): TransportDiagnosticsSnapshot;
|
|
712
|
+
/** Record delegation before dispatch, then pass the returned carrier to the worker. */
|
|
713
|
+
handoff(identity: ActionIdentity & {
|
|
714
|
+
sessionId: string;
|
|
715
|
+
}): Promise<string>;
|
|
716
|
+
/** Emit before queueing work. This records intent, not queue delivery acknowledgment. */
|
|
717
|
+
dispatchHandoff(identity: ActionIdentity & {
|
|
718
|
+
sessionId: string;
|
|
719
|
+
dispatchId: string;
|
|
720
|
+
}): Promise<string>;
|
|
721
|
+
acceptHandoff(sessionId: string, carrier: string): Promise<void>;
|
|
722
|
+
/** Call before finishing the worker session. Outcome describes execution, not business success. */
|
|
723
|
+
finishHandoff(sessionId: string, carrier: string, outcome: HandoffOutcome): Promise<void>;
|
|
724
|
+
/** Call in the parent only after receiving this worker's terminal result. */
|
|
725
|
+
receiveHandoff(sessionId: string, carrier: string, workerSessionId: string, outcome: HandoffOutcome): Promise<void>;
|
|
726
|
+
private recordHandoff;
|
|
831
727
|
/**
|
|
832
728
|
* Shut down the event batcher, flushing remaining events.
|
|
833
729
|
*
|
|
834
730
|
* Call this before your process exits for a clean shutdown.
|
|
835
|
-
*
|
|
731
|
+
* Cancels pending content uploads, then drains their fallback events and queued telemetry.
|
|
836
732
|
*/
|
|
837
733
|
shutdown(): Promise<void>;
|
|
838
734
|
/**
|
|
@@ -887,7 +783,6 @@ declare class Interaction {
|
|
|
887
783
|
readonly userId: string;
|
|
888
784
|
/** Event name for this interaction */
|
|
889
785
|
readonly event: string;
|
|
890
|
-
private readonly startTime;
|
|
891
786
|
private finished;
|
|
892
787
|
private success;
|
|
893
788
|
private failureReason?;
|
|
@@ -906,11 +801,30 @@ declare class Interaction {
|
|
|
906
801
|
* @param output - The output/response text
|
|
907
802
|
*/
|
|
908
803
|
setOutput(output: string): void;
|
|
804
|
+
/**
|
|
805
|
+
* Merge additional metadata into this interaction's session (post-begin).
|
|
806
|
+
*
|
|
807
|
+
* Carried on the finish() re-post: the server merges the keys into the session's metadata
|
|
808
|
+
* top-level (later values win; the reserved `otel` diagnostics key is preserved).
|
|
809
|
+
*
|
|
810
|
+
* @param metadata - Metadata keys to merge into the session metadata
|
|
811
|
+
*/
|
|
812
|
+
setMetadata(metadata: Record<string, unknown>): void;
|
|
813
|
+
/**
|
|
814
|
+
* Set/override the session-level model for this interaction (post-begin).
|
|
815
|
+
*
|
|
816
|
+
* Carried on the finish() re-post as gen_ai.request.model and under the stable `model` key in
|
|
817
|
+
* the session metadata.
|
|
818
|
+
*
|
|
819
|
+
* @param model - Model identifier (e.g. 'claude-sonnet-4-5')
|
|
820
|
+
*/
|
|
821
|
+
setModel(model: string): void;
|
|
909
822
|
/**
|
|
910
823
|
* Finish the interaction and record metrics
|
|
911
824
|
*
|
|
912
825
|
* @param params - Finish parameters
|
|
913
|
-
* @returns
|
|
826
|
+
* @returns `null` — the OTLP transport finalizes asynchronously server-side (also `null`
|
|
827
|
+
* if degraded/already finished).
|
|
914
828
|
*
|
|
915
829
|
* @example
|
|
916
830
|
* ```ts
|
|
@@ -926,6 +840,8 @@ declare class Interaction {
|
|
|
926
840
|
* Track a tool call within this interaction
|
|
927
841
|
*/
|
|
928
842
|
trackToolCall(params: Omit<TrackToolCallParams, 'sessionId'>): Promise<Event | null>;
|
|
843
|
+
/** Report an agent observation without changing this interaction's success or failure. */
|
|
844
|
+
reportDiagnostic(report: AgentDiagnosticInput): Promise<null>;
|
|
929
845
|
/**
|
|
930
846
|
* Track an LLM decision within this interaction
|
|
931
847
|
*/
|
|
@@ -986,6 +902,8 @@ declare function configure(config: SentrialClientConfig): void;
|
|
|
986
902
|
* ```
|
|
987
903
|
*/
|
|
988
904
|
declare function begin(params: BeginParams): Promise<Interaction>;
|
|
905
|
+
declare function getVariant(params: GetVariantParams): Promise<VariantAssignment>;
|
|
906
|
+
declare function recordExperimentExposure(params: RecordExperimentExposureParams): Promise<ExperimentExposureReceipt | null>;
|
|
989
907
|
/**
|
|
990
908
|
* Simple API namespace for module-level usage
|
|
991
909
|
*/
|
|
@@ -994,6 +912,8 @@ declare function shutdown(): Promise<void>;
|
|
|
994
912
|
declare const sentrial: {
|
|
995
913
|
configure: typeof configure;
|
|
996
914
|
begin: typeof begin;
|
|
915
|
+
getVariant: typeof getVariant;
|
|
916
|
+
recordExperimentExposure: typeof recordExperimentExposure;
|
|
997
917
|
flush: typeof flush;
|
|
998
918
|
shutdown: typeof shutdown;
|
|
999
919
|
};
|
|
@@ -1047,7 +967,11 @@ declare class NetworkError extends SentrialError {
|
|
|
1047
967
|
constructor(message: string, cause?: Error);
|
|
1048
968
|
}
|
|
1049
969
|
/**
|
|
1050
|
-
* Error thrown when validation fails
|
|
970
|
+
* Error thrown when validation fails.
|
|
971
|
+
*
|
|
972
|
+
* @deprecated The SDK no longer throws this internally. Retained as a public export for backwards
|
|
973
|
+
* compatibility with downstream consumers that import it; slated for removal in a future MINOR/MAJOR
|
|
974
|
+
* release (not a patch), per the SDK compatibility policy.
|
|
1051
975
|
*/
|
|
1052
976
|
declare class ValidationError extends SentrialError {
|
|
1053
977
|
constructor(message: string, details?: Record<string, unknown>);
|
|
@@ -1057,7 +981,7 @@ declare class ValidationError extends SentrialError {
|
|
|
1057
981
|
* Vercel AI SDK Integration for Sentrial
|
|
1058
982
|
*
|
|
1059
983
|
* Automatically traces AI SDK calls with full input/output logging, metrics, and tool execution.
|
|
1060
|
-
* Supports Vercel AI SDK v3
|
|
984
|
+
* Supports Vercel AI SDK v3 through v7.
|
|
1061
985
|
*
|
|
1062
986
|
* @example Simple Usage
|
|
1063
987
|
* ```ts
|
|
@@ -1093,13 +1017,16 @@ declare class ValidationError extends SentrialError {
|
|
|
1093
1017
|
* @packageDocumentation
|
|
1094
1018
|
*/
|
|
1095
1019
|
|
|
1020
|
+
type AnyFunction = (...args: unknown[]) => unknown;
|
|
1096
1021
|
type AIModule = {
|
|
1097
|
-
generateText?:
|
|
1098
|
-
streamText?:
|
|
1099
|
-
generateObject?:
|
|
1100
|
-
streamObject?:
|
|
1101
|
-
experimental_generateText?:
|
|
1102
|
-
experimental_streamText?:
|
|
1022
|
+
generateText?: AnyFunction;
|
|
1023
|
+
streamText?: AnyFunction;
|
|
1024
|
+
generateObject?: AnyFunction;
|
|
1025
|
+
streamObject?: AnyFunction;
|
|
1026
|
+
experimental_generateText?: AnyFunction;
|
|
1027
|
+
experimental_streamText?: AnyFunction;
|
|
1028
|
+
experimental_generateObject?: AnyFunction;
|
|
1029
|
+
experimental_streamObject?: AnyFunction;
|
|
1103
1030
|
};
|
|
1104
1031
|
type GenerateTextParams = {
|
|
1105
1032
|
model: {
|
|
@@ -1113,7 +1040,7 @@ type GenerateTextParams = {
|
|
|
1113
1040
|
}>;
|
|
1114
1041
|
system?: string;
|
|
1115
1042
|
tools?: Record<string, {
|
|
1116
|
-
execute?:
|
|
1043
|
+
execute?: AnyFunction;
|
|
1117
1044
|
} & Record<string, unknown>>;
|
|
1118
1045
|
maxTokens?: number;
|
|
1119
1046
|
maxSteps?: number;
|
|
@@ -1143,6 +1070,16 @@ type StepResult = {
|
|
|
1143
1070
|
}>;
|
|
1144
1071
|
usage?: UsageInfo;
|
|
1145
1072
|
finishReason?: string;
|
|
1073
|
+
/** Per-step response metadata — v5+ modelId, v3/v4 model. */
|
|
1074
|
+
response?: {
|
|
1075
|
+
modelId?: string;
|
|
1076
|
+
model?: string;
|
|
1077
|
+
};
|
|
1078
|
+
/** v6+: the model that produced this step (prepareStep can switch mid-call). */
|
|
1079
|
+
model?: {
|
|
1080
|
+
provider?: string;
|
|
1081
|
+
modelId?: string;
|
|
1082
|
+
};
|
|
1146
1083
|
};
|
|
1147
1084
|
type GenerateTextResult = {
|
|
1148
1085
|
text: string;
|
|
@@ -1217,10 +1154,10 @@ declare function configureVercel(config: {
|
|
|
1217
1154
|
convoId?: StringOrFn;
|
|
1218
1155
|
failSilently?: boolean;
|
|
1219
1156
|
}): void;
|
|
1220
|
-
declare function wrapGenerateText(originalFn:
|
|
1221
|
-
declare function wrapStreamText(originalFn:
|
|
1222
|
-
declare function wrapGenerateObject(originalFn:
|
|
1223
|
-
declare function wrapStreamObject(originalFn:
|
|
1157
|
+
declare function wrapGenerateText(originalFn: AnyFunction, client: SentrialClient, config: WrapperConfig): (params: GenerateTextParams) => Promise<GenerateTextResult>;
|
|
1158
|
+
declare function wrapStreamText(originalFn: AnyFunction, client: SentrialClient, config: WrapperConfig): (params: GenerateTextParams) => StreamTextResult;
|
|
1159
|
+
declare function wrapGenerateObject(originalFn: AnyFunction, client: SentrialClient, config: WrapperConfig): (params: GenerateTextParams) => Promise<unknown>;
|
|
1160
|
+
declare function wrapStreamObject(originalFn: AnyFunction, client: SentrialClient, config: WrapperConfig): (params: GenerateTextParams) => unknown;
|
|
1224
1161
|
/**
|
|
1225
1162
|
* Wrap the Vercel AI SDK to automatically trace all AI calls.
|
|
1226
1163
|
*
|
|
@@ -1257,6 +1194,61 @@ declare function wrapAISDK(ai: AIModule, options?: {
|
|
|
1257
1194
|
streamObject: ReturnType<typeof wrapStreamObject>;
|
|
1258
1195
|
};
|
|
1259
1196
|
|
|
1197
|
+
/**
|
|
1198
|
+
* Options shape accepted by {@link getVariantForAISDK} — a permissive subset of
|
|
1199
|
+
* an AI SDK `generateText` / `streamText` call. Extra keys are preserved, so it
|
|
1200
|
+
* composes with whatever options you already build.
|
|
1201
|
+
*/
|
|
1202
|
+
type AISDKVariantOptions = {
|
|
1203
|
+
model?: unknown;
|
|
1204
|
+
system?: string;
|
|
1205
|
+
prompt?: string;
|
|
1206
|
+
messages?: unknown;
|
|
1207
|
+
experimental_telemetry?: {
|
|
1208
|
+
isEnabled?: boolean;
|
|
1209
|
+
metadata?: Record<string, unknown>;
|
|
1210
|
+
};
|
|
1211
|
+
[key: string]: unknown;
|
|
1212
|
+
};
|
|
1213
|
+
/**
|
|
1214
|
+
* Resolve a production A/B variant and merge it into Vercel AI SDK call options.
|
|
1215
|
+
*
|
|
1216
|
+
* Pairs `client.getVariant()` with a `generateText` / `streamText` call: it
|
|
1217
|
+
* fetches the sticky assignment for this user/convo, applies the variant's
|
|
1218
|
+
* `systemPrompt`, `prompt`, and (via `modelResolver`, since the AI SDK needs a
|
|
1219
|
+
* model *object*, not a string) `model`, and tags the call's
|
|
1220
|
+
* `experimental_telemetry.metadata` with the experiment + variant so the
|
|
1221
|
+
* exposure is visible on the resulting session. Returns the merged options
|
|
1222
|
+
* (spread straight into your `generateText` call) and the raw assignment so you
|
|
1223
|
+
* can branch on `assignment.variant`.
|
|
1224
|
+
*
|
|
1225
|
+
* The assignment is recorded server-side by `getVariant` regardless of whether a
|
|
1226
|
+
* variant config is applied, so a `reason: 'holdout'` / `'not_found'` result is
|
|
1227
|
+
* safe — the base options pass through unchanged (sticky control).
|
|
1228
|
+
*
|
|
1229
|
+
* @example
|
|
1230
|
+
* ```ts
|
|
1231
|
+
* import { openai } from '@ai-sdk/openai';
|
|
1232
|
+
* import { wrapAISDK, getVariantForAISDK } from '@sentrial/sdk';
|
|
1233
|
+
* import * as ai from 'ai';
|
|
1234
|
+
*
|
|
1235
|
+
* const { generateText } = wrapAISDK(ai);
|
|
1236
|
+
* const { options, assignment } = await getVariantForAISDK(
|
|
1237
|
+
* { experimentName: 'support-prompt-v2', userId, convoId },
|
|
1238
|
+
* { model: openai('gpt-4o-mini'), system: 'You are a support agent.', prompt: userQuery },
|
|
1239
|
+
* { modelResolver: (id) => openai(id) }
|
|
1240
|
+
* );
|
|
1241
|
+
* const { text } = await generateText(options); // runs the assigned variant, tracked
|
|
1242
|
+
* ```
|
|
1243
|
+
*/
|
|
1244
|
+
declare function getVariantForAISDK<T extends AISDKVariantOptions>(variantParams: GetVariantParams, baseOptions: T, opts?: {
|
|
1245
|
+
modelResolver?: (modelId: string) => unknown;
|
|
1246
|
+
client?: SentrialClient;
|
|
1247
|
+
}): Promise<{
|
|
1248
|
+
options: T;
|
|
1249
|
+
assignment: VariantAssignment;
|
|
1250
|
+
}>;
|
|
1251
|
+
|
|
1260
1252
|
/**
|
|
1261
1253
|
* Sentrial wrapper for the Claude Agent SDK (Claude Code).
|
|
1262
1254
|
*
|
|
@@ -1321,6 +1313,487 @@ interface WrapClaudeAgentOptions {
|
|
|
1321
1313
|
*/
|
|
1322
1314
|
declare function wrapClaudeAgent(queryFn: QueryFunction, wrapOptions: WrapClaudeAgentOptions): QueryFunction;
|
|
1323
1315
|
|
|
1316
|
+
/**
|
|
1317
|
+
* OpenAI Agents SDK Integration for Sentrial
|
|
1318
|
+
*
|
|
1319
|
+
* Observes the OpenAI Agents SDK's (`@openai/agents`) tracing pipeline and maps
|
|
1320
|
+
* every trace onto a Sentrial session via the `begin()`/Interaction API:
|
|
1321
|
+
*
|
|
1322
|
+
* - **trace start** → `begin()` (agent/event name from the root agent span,
|
|
1323
|
+
* falling back to the trace's workflow name; session title from the trace name)
|
|
1324
|
+
* - **generation / response spans** → per-call LLM events (`llm:{model}` tool name
|
|
1325
|
+
* convention, prompt/completion tokens from usage, cost via the pricing tables,
|
|
1326
|
+
* duration from the span's start/end timestamps)
|
|
1327
|
+
* - **function spans** → `trackToolCall` (name, parsed args, result, errors)
|
|
1328
|
+
* - **handoff spans** → `trackDecision` ("Handoff from X to Y")
|
|
1329
|
+
* - **guardrail spans** → `trackError` when triggered, `trackDecision` when passed
|
|
1330
|
+
* - **trace end** → `finish()` (aggregated tokens/cost, last LLM output as the
|
|
1331
|
+
* session output, failure state from root agent span errors / triggered guardrails)
|
|
1332
|
+
*
|
|
1333
|
+
* Distinction from {@link wrapOpenAI}:
|
|
1334
|
+
* `wrapOpenAI` wraps a RAW `openai` client instance so each LLM call is tracked
|
|
1335
|
+
* inside a session YOU manage with `begin()`. This module instead observes the
|
|
1336
|
+
* OpenAI **Agents SDK** runtime (`Agent` + `run`) from the outside via its
|
|
1337
|
+
* documented tracing-processor hook — each agent run becomes a full Sentrial
|
|
1338
|
+
* session automatically, including tool calls, handoffs, and guardrails. If you
|
|
1339
|
+
* use the Agents SDK, prefer this module; don't combine both on the same run or
|
|
1340
|
+
* LLM calls will be double-counted.
|
|
1341
|
+
*
|
|
1342
|
+
* Verified against the official OpenAI Agents JS SDK
|
|
1343
|
+
* (https://openai.github.io/openai-agents-js/ and the `@openai/agents-core`
|
|
1344
|
+
* tracing sources): `TracingProcessor` is `onTraceStart/onTraceEnd/onSpanStart/
|
|
1345
|
+
* onSpanEnd(…): Promise<void>` + `shutdown(timeout?)`/`forceFlush()`; `Trace`
|
|
1346
|
+
* exposes camelCase `traceId/name/groupId/metadata`; `Span` exposes
|
|
1347
|
+
* `traceId/spanId/parentId/startedAt/endedAt/error/spanData` with ISO-string
|
|
1348
|
+
* timestamps and a `SpanError { message, data? }`; `SpanData` fields are
|
|
1349
|
+
* snake_case (`from_agent`, `to_agent`, `mcp_data`, usage `input_tokens`/
|
|
1350
|
+
* `output_tokens`; `ResponseSpanData` carries the raw Responses API payload on
|
|
1351
|
+
* `_response`). Registration is `addTraceProcessor(processor)` (additive — the
|
|
1352
|
+
* default OpenAI-dashboard exporter keeps running alongside).
|
|
1353
|
+
*
|
|
1354
|
+
* The `@openai/agents` package stays an optional peer dependency: like
|
|
1355
|
+
* `wrapAISDK(ai)`, the module never imports it — you hand `addTraceProcessor`
|
|
1356
|
+
* (or the module namespace that carries it) to {@link instrumentOpenAIAgents}.
|
|
1357
|
+
*
|
|
1358
|
+
* @example
|
|
1359
|
+
* ```ts
|
|
1360
|
+
* import { Agent, run, addTraceProcessor } from '@openai/agents';
|
|
1361
|
+
* import { sentrial, instrumentOpenAIAgents } from '@sentrial/sdk';
|
|
1362
|
+
*
|
|
1363
|
+
* sentrial.configure({ apiKey: process.env.SENTRIAL_API_KEY });
|
|
1364
|
+
* instrumentOpenAIAgents({ addTraceProcessor }, { userId: 'user_123' });
|
|
1365
|
+
*
|
|
1366
|
+
* const agent = new Agent({ name: 'Assistant', instructions: 'Be helpful.' });
|
|
1367
|
+
* const result = await run(agent, 'Write a haiku about recursion.');
|
|
1368
|
+
* // The run is now a Sentrial session with LLM events, tool calls, handoffs.
|
|
1369
|
+
* ```
|
|
1370
|
+
*
|
|
1371
|
+
* @packageDocumentation
|
|
1372
|
+
*/
|
|
1373
|
+
|
|
1374
|
+
/** The subset of `Trace` this processor reads (camelCase, per agents-core). */
|
|
1375
|
+
type AgentsTrace = {
|
|
1376
|
+
traceId?: string;
|
|
1377
|
+
name?: string;
|
|
1378
|
+
groupId?: string | null;
|
|
1379
|
+
metadata?: Record<string, unknown>;
|
|
1380
|
+
};
|
|
1381
|
+
/** `SpanError` shape (`span.error`). */
|
|
1382
|
+
type AgentsSpanError = {
|
|
1383
|
+
message?: string;
|
|
1384
|
+
data?: Record<string, unknown>;
|
|
1385
|
+
};
|
|
1386
|
+
/** The subset of `Span` this processor reads (camelCase getters, ISO timestamps). */
|
|
1387
|
+
type AgentsSpan = {
|
|
1388
|
+
traceId?: string;
|
|
1389
|
+
spanId?: string;
|
|
1390
|
+
parentId?: string | null;
|
|
1391
|
+
startedAt?: string | null;
|
|
1392
|
+
endedAt?: string | null;
|
|
1393
|
+
error?: AgentsSpanError | null;
|
|
1394
|
+
spanData?: Record<string, unknown> & {
|
|
1395
|
+
type?: string;
|
|
1396
|
+
};
|
|
1397
|
+
};
|
|
1398
|
+
/** The one entry point we need from `@openai/agents` / `@openai/agents-core`. */
|
|
1399
|
+
type AgentsModuleLike = {
|
|
1400
|
+
addTraceProcessor: (processor: unknown) => void;
|
|
1401
|
+
};
|
|
1402
|
+
interface OpenAIAgentsProcessorOptions {
|
|
1403
|
+
/** Explicit SentrialClient. Defaults to a client from the environment (SENTRIAL_API_KEY). */
|
|
1404
|
+
client?: SentrialClient;
|
|
1405
|
+
/** Explicit agent/event name override (defaults to the root agent span's name per run). */
|
|
1406
|
+
defaultAgent?: string;
|
|
1407
|
+
/** User ID to associate with sessions (default 'anonymous'). */
|
|
1408
|
+
userId?: string;
|
|
1409
|
+
/** Extra metadata merged into every session. */
|
|
1410
|
+
extraMetadata?: Record<string, unknown>;
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Duck-typed `TracingProcessor` (from `@openai/agents-core`) that mirrors OpenAI
|
|
1414
|
+
* Agents SDK runs into Sentrial sessions.
|
|
1415
|
+
*
|
|
1416
|
+
* Register it via {@link instrumentOpenAIAgents}, or directly:
|
|
1417
|
+
* ```ts
|
|
1418
|
+
* import { addTraceProcessor } from '@openai/agents';
|
|
1419
|
+
* addTraceProcessor(new SentrialOpenAIAgentsProcessor({ client }));
|
|
1420
|
+
* ```
|
|
1421
|
+
*
|
|
1422
|
+
* Every callback body is containment-wrapped: the Agents SDK awaits these
|
|
1423
|
+
* methods inline on the user's run path, so a tracking failure must never
|
|
1424
|
+
* reject into the host agent — it is swallowed instead (the SentrialClient/
|
|
1425
|
+
* Interaction methods additionally carry their own containment seam).
|
|
1426
|
+
*
|
|
1427
|
+
* Performance tip: construct the client with `batching: { enabled: true }` so
|
|
1428
|
+
* `track*` calls enqueue instead of awaiting a network round-trip per span.
|
|
1429
|
+
* (The TS BatcherConfig takes an object — a bare `batching: true` is silently
|
|
1430
|
+
* ignored; the Python SDK's `batching=True` bool shorthand has no TS twin.)
|
|
1431
|
+
*/
|
|
1432
|
+
declare class SentrialOpenAIAgentsProcessor {
|
|
1433
|
+
private clientRef?;
|
|
1434
|
+
private readonly defaultAgent?;
|
|
1435
|
+
private readonly userId;
|
|
1436
|
+
private readonly extraMetadata;
|
|
1437
|
+
private readonly traces;
|
|
1438
|
+
private tracesCapWarned;
|
|
1439
|
+
constructor(options?: OpenAIAgentsProcessorOptions);
|
|
1440
|
+
/** Optional TracingProcessor lifecycle hook — nothing to start. */
|
|
1441
|
+
start(): void;
|
|
1442
|
+
onTraceStart(trace: AgentsTrace): Promise<void>;
|
|
1443
|
+
onTraceEnd(trace: AgentsTrace): Promise<void>;
|
|
1444
|
+
onSpanStart(span: AgentsSpan): Promise<void>;
|
|
1445
|
+
onSpanEnd(span: AgentsSpan): Promise<void>;
|
|
1446
|
+
/**
|
|
1447
|
+
* Called at application stop: finishes any still-open sessions as interrupted
|
|
1448
|
+
* and flushes the client's event queue (if batching).
|
|
1449
|
+
*/
|
|
1450
|
+
shutdown(_timeout?: number): Promise<void>;
|
|
1451
|
+
/** Forces immediate processing of queued events (no-op without batching). */
|
|
1452
|
+
forceFlush(): Promise<void>;
|
|
1453
|
+
private resolveClient;
|
|
1454
|
+
private flushClient;
|
|
1455
|
+
private handleTraceStart;
|
|
1456
|
+
private stateFor;
|
|
1457
|
+
/**
|
|
1458
|
+
* Begin the Sentrial session for this trace exactly once. Event/agent name
|
|
1459
|
+
* preference: explicit `defaultAgent` > root agent span name > trace workflow
|
|
1460
|
+
* name > 'openai-agents'. The session title is the trace's workflow name.
|
|
1461
|
+
*
|
|
1462
|
+
* DELIBERATE cross-SDK divergence on begin failure: the rejected promise is
|
|
1463
|
+
* cached, so a trace whose begin() failed never retries (avoids a partial
|
|
1464
|
+
* session whose earlier spans were lost). The Python twin leaves
|
|
1465
|
+
* `interaction=None` and retries at the next span (favoring late recovery).
|
|
1466
|
+
* Both are documented choices, not drift.
|
|
1467
|
+
*/
|
|
1468
|
+
private ensureInteraction;
|
|
1469
|
+
private handleSpanStart;
|
|
1470
|
+
private handleSpanEnd;
|
|
1471
|
+
private trackLLMEvent;
|
|
1472
|
+
private trackFunction;
|
|
1473
|
+
private trackHandoff;
|
|
1474
|
+
private trackGuardrail;
|
|
1475
|
+
private handleTraceEnd;
|
|
1476
|
+
private finishState;
|
|
1477
|
+
}
|
|
1478
|
+
/**
|
|
1479
|
+
* Construct a {@link SentrialOpenAIAgentsProcessor} and register it with the
|
|
1480
|
+
* OpenAI Agents SDK. Pass the SDK's `addTraceProcessor` (or any object carrying
|
|
1481
|
+
* it, e.g. the whole `@openai/agents` module namespace) — the peer dependency is
|
|
1482
|
+
* never imported here, mirroring `wrapAISDK(ai)`.
|
|
1483
|
+
*
|
|
1484
|
+
* Idempotent per (agents module, client) pair: calling this again with the same
|
|
1485
|
+
* `addTraceProcessor` and the same `options.client` (or again with no client —
|
|
1486
|
+
* the env-default) returns the FIRST registered processor instead of registering
|
|
1487
|
+
* a duplicate, which would mirror every run into two sessions. The first
|
|
1488
|
+
* duplicate call emits a console warning (silent thereafter).
|
|
1489
|
+
*
|
|
1490
|
+
* @example
|
|
1491
|
+
* ```ts
|
|
1492
|
+
* import * as agents from '@openai/agents';
|
|
1493
|
+
* import { instrumentOpenAIAgents } from '@sentrial/sdk';
|
|
1494
|
+
*
|
|
1495
|
+
* instrumentOpenAIAgents(agents, { userId: 'user_123' });
|
|
1496
|
+
* ```
|
|
1497
|
+
*
|
|
1498
|
+
* @param agents - The `@openai/agents` module namespace (or `{ addTraceProcessor }`).
|
|
1499
|
+
* @param options - Processor options (client, defaultAgent, userId, extraMetadata).
|
|
1500
|
+
* @returns The registered processor (keep it to call `shutdown()`/`forceFlush()`).
|
|
1501
|
+
*/
|
|
1502
|
+
declare function instrumentOpenAIAgents(agents: AgentsModuleLike, options?: OpenAIAgentsProcessorOptions): SentrialOpenAIAgentsProcessor;
|
|
1503
|
+
|
|
1504
|
+
/**
|
|
1505
|
+
* Google Agent Development Kit (ADK) Integration for Sentrial — TypeScript twin
|
|
1506
|
+
* of the Python `sentrial.google_adk` module, ported for `adk-js` (`@google/adk`).
|
|
1507
|
+
*
|
|
1508
|
+
* Maps a Google ADK **run** (one `Runner.runAsync` invocation) onto a single
|
|
1509
|
+
* Sentrial session via the `begin()`/Interaction API. The attachment surface is a
|
|
1510
|
+
* Runner-level **Plugin** — ADK's `BasePlugin` fires globally for the *entire*
|
|
1511
|
+
* invocation across *every* agent, tool, and model call, so one run → one Sentrial
|
|
1512
|
+
* session automatically (the ADK equivalent of the OpenAI Agents SDK's global
|
|
1513
|
+
* trace processor in {@link SentrialOpenAIAgentsProcessor}).
|
|
1514
|
+
*
|
|
1515
|
+
* Mapping (mirrors the Python google_adk.py):
|
|
1516
|
+
*
|
|
1517
|
+
* - **run start** (`beforeRunCallback`) → `begin()` — agent/event name from the
|
|
1518
|
+
* root agent, `convoId` from the ADK session id, `userId` from the ADK session
|
|
1519
|
+
* (overridable), input from the run's `userContent`.
|
|
1520
|
+
* - **model calls** (`beforeModelCallback` → `afterModelCallback`) → per-call LLM
|
|
1521
|
+
* event via `trackToolCall` (`llm:{model}` convention, model from
|
|
1522
|
+
* `LlmRequest.model`, prompt/completion tokens from `LlmResponse.usageMetadata`
|
|
1523
|
+
* — `promptTokenCount` / `candidatesTokenCount` — cost via the pricing tables,
|
|
1524
|
+
* duration from the before/after timestamps). Streaming *partial* responses are
|
|
1525
|
+
* skipped so tokens aren't double-counted; the final response carries the usage.
|
|
1526
|
+
* - **tool calls** (`beforeToolCallback` → `afterToolCallback`) → `trackToolCall`
|
|
1527
|
+
* (tool name/args/result, duration from before/after).
|
|
1528
|
+
* - **errors** (`onToolErrorCallback` / `onModelErrorCallback` and any
|
|
1529
|
+
* `LlmResponse.errorCode`) → `trackError` (the session finishes `success=false`).
|
|
1530
|
+
* - **run end** (`afterRunCallback`) → `finish()` with rolled-up tokens/cost and
|
|
1531
|
+
* the last model output; custom metrics `llm_calls` / `tool_calls`.
|
|
1532
|
+
*
|
|
1533
|
+
* Distinction from {@link wrapGoogle}:
|
|
1534
|
+
* `wrapGoogle` wraps a RAW `@google/genai` model so each `generateContent` call is
|
|
1535
|
+
* tracked inside a session YOU manage with `begin()`. This module instead observes
|
|
1536
|
+
* the ADK **runtime** (`Agent` + `Runner`) from the outside via its documented
|
|
1537
|
+
* Plugin hooks — each ADK run becomes a full Sentrial session automatically. If you
|
|
1538
|
+
* use ADK, prefer this module; don't combine both on the same run (LLM calls would
|
|
1539
|
+
* be double-counted).
|
|
1540
|
+
*
|
|
1541
|
+
* The `@google/adk` package stays an OPTIONAL peer dependency: this module NEVER
|
|
1542
|
+
* imports it. ADK's `PluginManager` dispatches to plugin callbacks BY NAME and
|
|
1543
|
+
* dedups by `.name`, and a plain object structurally satisfies `BasePlugin`, so a
|
|
1544
|
+
* duck-typed class needs no `BasePlugin` subclass — the module imports cleanly with
|
|
1545
|
+
* or without ADK installed. You attach it via `plugins: [new SentrialAdkPlugin()]`
|
|
1546
|
+
* at Runner construction, or after the fact with {@link instrumentAdk}.
|
|
1547
|
+
*
|
|
1548
|
+
* Verified against the official ADK-JS source (github.com/google/adk-js, package
|
|
1549
|
+
* `@google/adk`):
|
|
1550
|
+
* - `core/src/plugins/base_plugin.ts` — plugin callbacks take a SINGLE camelCase
|
|
1551
|
+
* params object (NOT Python's keyword args): `beforeRunCallback({invocationContext})`,
|
|
1552
|
+
* `afterRunCallback({invocationContext})`, `beforeModelCallback({callbackContext, llmRequest})`,
|
|
1553
|
+
* `afterModelCallback({callbackContext, llmResponse})`,
|
|
1554
|
+
* `onModelErrorCallback({callbackContext, llmRequest, error})`,
|
|
1555
|
+
* `beforeToolCallback({tool, toolArgs, toolContext})`,
|
|
1556
|
+
* `afterToolCallback({tool, toolArgs, toolContext, result})`,
|
|
1557
|
+
* `onToolErrorCallback({tool, toolArgs, toolContext, error})`. Returning a
|
|
1558
|
+
* non-`undefined` value SHORT-CIRCUITS the run, so every callback returns
|
|
1559
|
+
* `undefined` (pure observer).
|
|
1560
|
+
* - `core/src/plugins/plugin_manager.ts` — `runCallbacks` CATCHES a throwing
|
|
1561
|
+
* callback and RE-THROWS a wrapped error that HALTS the agent run; containment
|
|
1562
|
+
* inside every callback body is therefore mandatory. `registerPlugin` throws on a
|
|
1563
|
+
* duplicate `.name`; `getPlugin(name)` retrieves by name.
|
|
1564
|
+
* - `core/src/models/llm_response.ts` — `content?: Content` (`.parts[].text`),
|
|
1565
|
+
* `usageMetadata` (`promptTokenCount` / `candidatesTokenCount`), `partial`,
|
|
1566
|
+
* `errorCode` / `errorMessage`.
|
|
1567
|
+
* - `core/src/models/llm_request.ts` — `model?: string`.
|
|
1568
|
+
* - `core/src/agents/invocation_context.ts` — `invocationId`, `agent` (`.name`),
|
|
1569
|
+
* `session` (`.id` / `.appName` / `.userId`), `appName`/`userId` getters, `userContent`.
|
|
1570
|
+
* - `core/src/agents/readonly_context.ts` — the callback `Context` exposes getters
|
|
1571
|
+
* `invocationId` / `agentName` / `sessionId` / `userId` / `userContent` and the
|
|
1572
|
+
* public `invocationContext`.
|
|
1573
|
+
* - `core/src/runner/runner.ts` — `plugins?: BasePlugin[]` at construction;
|
|
1574
|
+
* `runner.pluginManager.registerPlugin(plugin)`.
|
|
1575
|
+
*
|
|
1576
|
+
* @example
|
|
1577
|
+
* ```ts
|
|
1578
|
+
* import { InMemoryRunner } from '@google/adk';
|
|
1579
|
+
* import { LlmAgent } from '@google/adk';
|
|
1580
|
+
* import { sentrial, SentrialAdkPlugin } from '@sentrial/sdk';
|
|
1581
|
+
*
|
|
1582
|
+
* sentrial.configure({ apiKey: process.env.SENTRIAL_API_KEY });
|
|
1583
|
+
*
|
|
1584
|
+
* const agent = new LlmAgent({ name: 'assistant', model: 'gemini-2.0-flash' });
|
|
1585
|
+
* const runner = new InMemoryRunner({ agent, plugins: [new SentrialAdkPlugin()] });
|
|
1586
|
+
* // Every runner.runAsync(...) is now a Sentrial session with LLM events,
|
|
1587
|
+
* // tool calls, and token/cost rollups.
|
|
1588
|
+
* ```
|
|
1589
|
+
*
|
|
1590
|
+
* @packageDocumentation
|
|
1591
|
+
*/
|
|
1592
|
+
|
|
1593
|
+
/** The subset of ADK `Content` (`@google/genai`) this plugin reads. */
|
|
1594
|
+
type AdkContent = {
|
|
1595
|
+
parts?: Array<{
|
|
1596
|
+
text?: unknown;
|
|
1597
|
+
} | unknown>;
|
|
1598
|
+
} | string | unknown;
|
|
1599
|
+
/** The subset of ADK `InvocationContext` this plugin reads (run lifecycle). */
|
|
1600
|
+
type AdkInvocationContext = {
|
|
1601
|
+
invocationId?: string;
|
|
1602
|
+
agent?: {
|
|
1603
|
+
name?: string;
|
|
1604
|
+
};
|
|
1605
|
+
session?: {
|
|
1606
|
+
id?: string;
|
|
1607
|
+
appName?: string;
|
|
1608
|
+
userId?: string;
|
|
1609
|
+
};
|
|
1610
|
+
appName?: string;
|
|
1611
|
+
userId?: string;
|
|
1612
|
+
userContent?: AdkContent;
|
|
1613
|
+
};
|
|
1614
|
+
/** The subset of the ADK callback `Context` this plugin reads (model/tool calls). */
|
|
1615
|
+
type AdkCallbackContext = {
|
|
1616
|
+
invocationId?: string;
|
|
1617
|
+
agentName?: string;
|
|
1618
|
+
sessionId?: string;
|
|
1619
|
+
userId?: string;
|
|
1620
|
+
userContent?: AdkContent;
|
|
1621
|
+
invocationContext?: AdkInvocationContext;
|
|
1622
|
+
};
|
|
1623
|
+
/** The subset of ADK `LlmRequest` this plugin reads. */
|
|
1624
|
+
type AdkLlmRequest = {
|
|
1625
|
+
model?: string;
|
|
1626
|
+
};
|
|
1627
|
+
/** The subset of ADK `LlmResponse` this plugin reads. */
|
|
1628
|
+
type AdkLlmResponse = {
|
|
1629
|
+
content?: AdkContent;
|
|
1630
|
+
usageMetadata?: {
|
|
1631
|
+
promptTokenCount?: unknown;
|
|
1632
|
+
candidatesTokenCount?: unknown;
|
|
1633
|
+
};
|
|
1634
|
+
partial?: unknown;
|
|
1635
|
+
errorCode?: unknown;
|
|
1636
|
+
errorMessage?: unknown;
|
|
1637
|
+
};
|
|
1638
|
+
/** The subset of ADK `BaseTool` this plugin reads. */
|
|
1639
|
+
type AdkTool = {
|
|
1640
|
+
name?: string;
|
|
1641
|
+
};
|
|
1642
|
+
/** The Runner surface {@link instrumentAdk} needs (`runner.pluginManager.registerPlugin`). */
|
|
1643
|
+
type AdkRunnerLike = {
|
|
1644
|
+
pluginManager?: {
|
|
1645
|
+
registerPlugin?: (plugin: unknown) => void;
|
|
1646
|
+
getPlugin?: (name: string) => unknown;
|
|
1647
|
+
};
|
|
1648
|
+
};
|
|
1649
|
+
interface AdkPluginOptions {
|
|
1650
|
+
/** Explicit SentrialClient. Defaults to a client from the environment (SENTRIAL_API_KEY). */
|
|
1651
|
+
client?: SentrialClient;
|
|
1652
|
+
/** ADK plugin name (default 'sentrial'); ADK requires unique plugin names. */
|
|
1653
|
+
name?: string;
|
|
1654
|
+
/** Explicit agent/event name override (defaults to the root agent's name per run). */
|
|
1655
|
+
defaultAgent?: string;
|
|
1656
|
+
/** User ID override (defaults to the ADK session's userId, then 'anonymous'). */
|
|
1657
|
+
userId?: string;
|
|
1658
|
+
/** Extra metadata merged into every session. */
|
|
1659
|
+
extraMetadata?: Record<string, unknown>;
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Duck-typed Google ADK `BasePlugin` that mirrors ADK runs into Sentrial sessions.
|
|
1663
|
+
*
|
|
1664
|
+
* Register it on a `Runner`:
|
|
1665
|
+
* ```ts
|
|
1666
|
+
* const runner = new InMemoryRunner({ agent, plugins: [new SentrialAdkPlugin()] });
|
|
1667
|
+
* ```
|
|
1668
|
+
* or attach it to an existing runner with {@link instrumentAdk}.
|
|
1669
|
+
*
|
|
1670
|
+
* This is a *plain* class implementing the `BasePlugin` callback protocol — it is
|
|
1671
|
+
* intentionally NOT a `BasePlugin` subclass, because ADK's `PluginManager`
|
|
1672
|
+
* dispatches to callbacks by name and dedups by `.name`, so subclassing would force
|
|
1673
|
+
* a hard `@google/adk` import (breaking the "optional peer" contract and
|
|
1674
|
+
* import-without-ADK). Every callback body is containment-wrapped: ADK's
|
|
1675
|
+
* PluginManager re-throws a callback error and HALTS the run, so a tracking failure
|
|
1676
|
+
* must never escape — it is swallowed instead. Every callback returns `undefined`
|
|
1677
|
+
* (a pure observer never short-circuits the model/tool call).
|
|
1678
|
+
*
|
|
1679
|
+
* Performance tip: construct the client with `batching: { enabled: true }` so
|
|
1680
|
+
* `track*` calls enqueue instead of awaiting a network round-trip per event. ADK
|
|
1681
|
+
* invokes these callbacks inline on the run's event loop.
|
|
1682
|
+
*/
|
|
1683
|
+
declare class SentrialAdkPlugin {
|
|
1684
|
+
/** PluginManager reads `.name` for dedup/logging — a real string, always present. */
|
|
1685
|
+
readonly name: string;
|
|
1686
|
+
private clientRef?;
|
|
1687
|
+
private readonly defaultAgent?;
|
|
1688
|
+
private readonly userIdOption?;
|
|
1689
|
+
private readonly extraMetadata;
|
|
1690
|
+
private readonly runs;
|
|
1691
|
+
/** One-time guard so the runs-cap eviction warns at most once (like the duplicate-instrument warn). */
|
|
1692
|
+
private runsCapWarned;
|
|
1693
|
+
constructor(options?: AdkPluginOptions);
|
|
1694
|
+
beforeRunCallback(params: {
|
|
1695
|
+
invocationContext: AdkInvocationContext;
|
|
1696
|
+
}): Promise<undefined>;
|
|
1697
|
+
afterRunCallback(params: {
|
|
1698
|
+
invocationContext: AdkInvocationContext;
|
|
1699
|
+
}): Promise<void>;
|
|
1700
|
+
onRunErrorCallback(params: {
|
|
1701
|
+
invocationContext: AdkInvocationContext;
|
|
1702
|
+
error: unknown;
|
|
1703
|
+
}): Promise<void>;
|
|
1704
|
+
beforeModelCallback(params: {
|
|
1705
|
+
callbackContext: AdkCallbackContext;
|
|
1706
|
+
llmRequest: AdkLlmRequest;
|
|
1707
|
+
}): Promise<undefined>;
|
|
1708
|
+
afterModelCallback(params: {
|
|
1709
|
+
callbackContext: AdkCallbackContext;
|
|
1710
|
+
llmResponse: AdkLlmResponse;
|
|
1711
|
+
}): Promise<undefined>;
|
|
1712
|
+
onModelErrorCallback(params: {
|
|
1713
|
+
callbackContext: AdkCallbackContext;
|
|
1714
|
+
llmRequest: AdkLlmRequest;
|
|
1715
|
+
error: unknown;
|
|
1716
|
+
}): Promise<undefined>;
|
|
1717
|
+
beforeToolCallback(params: {
|
|
1718
|
+
tool: AdkTool;
|
|
1719
|
+
toolArgs: Record<string, unknown>;
|
|
1720
|
+
toolContext: AdkCallbackContext;
|
|
1721
|
+
}): Promise<undefined>;
|
|
1722
|
+
afterToolCallback(params: {
|
|
1723
|
+
tool: AdkTool;
|
|
1724
|
+
toolArgs: Record<string, unknown>;
|
|
1725
|
+
toolContext: AdkCallbackContext;
|
|
1726
|
+
result: unknown;
|
|
1727
|
+
}): Promise<undefined>;
|
|
1728
|
+
onToolErrorCallback(params: {
|
|
1729
|
+
tool: AdkTool;
|
|
1730
|
+
toolArgs: Record<string, unknown>;
|
|
1731
|
+
toolContext: AdkCallbackContext;
|
|
1732
|
+
error: unknown;
|
|
1733
|
+
}): Promise<undefined>;
|
|
1734
|
+
private resolveClient;
|
|
1735
|
+
/**
|
|
1736
|
+
* Fetch (or lazily create) the per-run state for an invocation id, back-filling
|
|
1737
|
+
* any run-identity fields not known when it was first created (a model/tool
|
|
1738
|
+
* callback can reach a run before beforeRun in some ADK flows).
|
|
1739
|
+
*/
|
|
1740
|
+
private getOrCreateState;
|
|
1741
|
+
private stateFromInvocationContext;
|
|
1742
|
+
private stateFromCallbackContext;
|
|
1743
|
+
/**
|
|
1744
|
+
* Begin the Sentrial session for this run exactly once. Event/agent name
|
|
1745
|
+
* preference: explicit `defaultAgent` > root agent name > 'google-adk'. The ADK
|
|
1746
|
+
* session id becomes `convoId`; the ADK session `userId` (or the configured
|
|
1747
|
+
* override) becomes the Sentrial user.
|
|
1748
|
+
*
|
|
1749
|
+
* DELIBERATE (matches the openai-agents.ts TS twin): the interaction promise is
|
|
1750
|
+
* cached, so a run whose `begin()` REJECTED never retries (avoids a partial
|
|
1751
|
+
* session whose earlier events were lost). The Python google_adk leaves the
|
|
1752
|
+
* interaction unset and retries at the next callback — both are documented,
|
|
1753
|
+
* per-SDK choices, not drift.
|
|
1754
|
+
*/
|
|
1755
|
+
private ensureInteraction;
|
|
1756
|
+
private handleRunStart;
|
|
1757
|
+
private handleRunEnd;
|
|
1758
|
+
private handleRunError;
|
|
1759
|
+
private handleModelStart;
|
|
1760
|
+
private handleModelEnd;
|
|
1761
|
+
private handleModelError;
|
|
1762
|
+
private emitLlmEvent;
|
|
1763
|
+
private handleToolStart;
|
|
1764
|
+
private popToolTiming;
|
|
1765
|
+
private handleToolEnd;
|
|
1766
|
+
private handleToolError;
|
|
1767
|
+
private finishState;
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1770
|
+
* Register a {@link SentrialAdkPlugin} on an already-constructed ADK `Runner`.
|
|
1771
|
+
*
|
|
1772
|
+
* Convenience for when you didn't pass `plugins: [new SentrialAdkPlugin()]` at
|
|
1773
|
+
* Runner construction. Pass the runner (its `pluginManager.registerPlugin` is the
|
|
1774
|
+
* only surface used) — the `@google/adk` peer is never imported here, mirroring
|
|
1775
|
+
* `instrumentOpenAIAgents(agents)`.
|
|
1776
|
+
*
|
|
1777
|
+
* Idempotent per runner: calling it again on the same runner returns the FIRST
|
|
1778
|
+
* registered plugin instead of registering a duplicate (which ADK would reject on
|
|
1779
|
+
* the duplicate name anyway, and which would mirror every run into two sessions).
|
|
1780
|
+
* The first duplicate call emits a console warning (silent thereafter).
|
|
1781
|
+
*
|
|
1782
|
+
* @example
|
|
1783
|
+
* ```ts
|
|
1784
|
+
* import { InMemoryRunner } from '@google/adk';
|
|
1785
|
+
* import { instrumentAdk } from '@sentrial/sdk';
|
|
1786
|
+
*
|
|
1787
|
+
* const runner = new InMemoryRunner({ agent });
|
|
1788
|
+
* instrumentAdk(runner, { userId: 'user_123' });
|
|
1789
|
+
* ```
|
|
1790
|
+
*
|
|
1791
|
+
* @param runner - An ADK `Runner` exposing `pluginManager.registerPlugin`.
|
|
1792
|
+
* @param options - Plugin options (client, name, defaultAgent, userId, extraMetadata).
|
|
1793
|
+
* @returns The registered plugin.
|
|
1794
|
+
*/
|
|
1795
|
+
declare function instrumentAdk(runner: AdkRunnerLike, options?: AdkPluginOptions): SentrialAdkPlugin;
|
|
1796
|
+
|
|
1324
1797
|
/**
|
|
1325
1798
|
* Sentrial Decorators - Easy instrumentation for AI agents
|
|
1326
1799
|
*
|
|
@@ -1889,4 +2362,4 @@ declare class Experiment {
|
|
|
1889
2362
|
getResults(): Promise<ExperimentResults | null>;
|
|
1890
2363
|
}
|
|
1891
2364
|
|
|
1892
|
-
export {
|
|
2365
|
+
export { type AISDKVariantOptions, type ActionIdentity, type AdkCallbackContext, type AdkContent, type AdkInvocationContext, type AdkLlmRequest, type AdkLlmResponse, type AdkPluginOptions, type AdkRunnerLike, type AdkTool, type AgentDiagnostic, type AgentDiagnosticInput, type AgentsModuleLike, type AgentsSpan, type AgentsSpanError, type AgentsTrace, ApiError, BUILTIN_PATTERNS, BeginParams, CompleteSessionParams, type ContextToken, type ContextVar, CostParams, CreateSessionParams, DeliveryDiagnostics, type DispatchContext, type DurationSummary, Event, Experiment, type ExperimentContext, ExperimentExposureReceipt, type ExperimentResults, type ExperimentRunResult, ExperimentRunTracker, type ExperimentTestCase, type ExperimentVariant, FinishParams, type GenerateTextParams, type GenerateTextResult, GetVariantParams, type HandoffContext, type HandoffOutcome, Interaction, NetworkError, type NormalizableUsage, type NormalizedUsage, type OpenAIAgentsProcessorOptions, RecordExperimentExposureParams, SentrialAdkPlugin, SentrialClient, SentrialClientConfig, SentrialError, SentrialOpenAIAgentsProcessor, type ServerDurations, Session, SessionContext, type StreamTextResult, Tool, TrackDecisionParams, TrackErrorParams, TrackSession, TrackToolCallParams, type TransportAttempt, type TransportDiagnosticsSnapshot, ValidationError, VariantAssignment, type WrapClaudeAgentOptions, actionMetadata, begin, calculateAnthropicCost, calculateCostByProvider, calculateGoogleCost, calculateOpenAICost, clearExperimentContext, clearSessionContext, configure, configureVercel, createContextVar, createDispatchContext, createHandoffContext, foldProviderExtras, getCurrentInteraction, getCurrentSessionId, getExperimentContext, getExperimentId, getSessionContext, getSystemPrompt, getVariant, getVariantForAISDK, getVariantName, guessProvider, instrumentAdk, instrumentOpenAIAgents, isExperimentMode, normalizeUsage, recordExperimentExposure, resolveTokensAndCost, resumeHandoffContext, sentrial, setClient, setDefaultClient, setExperimentContext, setSessionContext, withSession, withTool, wrapAISDK, wrapAnthropic, wrapClaudeAgent, wrapGoogle, wrapLLM, wrapOpenAI };
|