@grafana/sigil-sdk-js 0.0.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -54,6 +54,42 @@ await client.startGeneration(
54
54
  await client.shutdown();
55
55
  ```
56
56
 
57
+ ## Pre-Ingest Redaction
58
+
59
+ Use `generationSanitizer` when you want to redact substrings from normalized generations before
60
+ validation, span sync, debug snapshots, and export.
61
+
62
+ ```ts
63
+ import {
64
+ SigilClient,
65
+ createSecretRedactionSanitizer,
66
+ } from "@grafana/sigil-sdk-js";
67
+
68
+ const client = new SigilClient({
69
+ generationSanitizer: createSecretRedactionSanitizer({
70
+ redactInputMessages: false,
71
+ redactEmailAddresses: true,
72
+ }),
73
+ });
74
+ ```
75
+
76
+ The built-in sanitizer:
77
+
78
+ - redacts high-confidence secret formats in assistant text and thinking
79
+ - redacts secret formats plus env-style secret values in tool call inputs and tool results
80
+ - redacts email addresses by default
81
+ - leaves user input unchanged unless `redactInputMessages: true` is set
82
+
83
+ To preserve email addresses, opt out explicitly:
84
+
85
+ ```ts
86
+ const client = new SigilClient({
87
+ generationSanitizer: createSecretRedactionSanitizer({
88
+ redactEmailAddresses: false,
89
+ }),
90
+ });
91
+ ```
92
+
57
93
  Configure OTEL exporters (traces/metrics) in your application OTEL SDK setup. You can optionally pass `tracer` and `meter` directly to `SigilClient`.
58
94
 
59
95
  Quick OTEL setup pattern before creating the Sigil client:
@@ -167,12 +203,14 @@ Use module subpath exports for framework callback integrations:
167
203
  - LlamaIndex: `@grafana/sigil-sdk-js/llamaindex`
168
204
  - Google ADK: `@grafana/sigil-sdk-js/google-adk`
169
205
  - Vercel AI SDK: `@grafana/sigil-sdk-js/vercel-ai-sdk`
206
+ - Strands Agents: `@grafana/sigil-sdk-js/strands`
170
207
  - LangChain guide: `docs/frameworks/langchain.md`
171
208
  - LangGraph guide: `docs/frameworks/langgraph.md`
172
209
  - OpenAI Agents guide: `docs/frameworks/openai-agents.md`
173
210
  - LlamaIndex guide: `docs/frameworks/llamaindex.md`
174
211
  - Google ADK guide: `docs/frameworks/google-adk.md`
175
212
  - Vercel AI SDK guide: `docs/frameworks/vercel-ai-sdk.md`
213
+ - Strands Agents guide: `docs/frameworks/strands.md`
176
214
 
177
215
  ```ts
178
216
  import { SigilClient } from "@grafana/sigil-sdk-js";
@@ -182,6 +220,7 @@ import { withSigilOpenAIAgentsHooks } from "@grafana/sigil-sdk-js/openai-agents"
182
220
  import { withSigilLlamaIndexCallbacks } from "@grafana/sigil-sdk-js/llamaindex";
183
221
  import { withSigilGoogleAdkPlugins } from "@grafana/sigil-sdk-js/google-adk";
184
222
  import { createSigilVercelAiSdk } from "@grafana/sigil-sdk-js/vercel-ai-sdk";
223
+ import { withSigilStrandsHooks } from "@grafana/sigil-sdk-js/strands";
185
224
  import { Runner } from "@openai/agents";
186
225
  import { CallbackManager } from "llamaindex";
187
226
 
@@ -194,13 +233,19 @@ const callbackManager = new CallbackManager();
194
233
  const llamaIndexConfig = withSigilLlamaIndexCallbacks({ callbackManager }, client, { providerResolver: "auto" });
195
234
  const googleAdkRunnerConfig = withSigilGoogleAdkPlugins(undefined, client, { providerResolver: "auto" });
196
235
  const vercelAiSdk = createSigilVercelAiSdk(client, { agentName: "vercel-agent" });
236
+ const strandsConfig = withSigilStrandsHooks(undefined, client, { conversationId: "chat-123" });
197
237
  ```
198
238
 
239
+ Framework handlers use the `SigilClient` instance you pass in. If that client is configured with
240
+ `generationSanitizer`, the same redaction policy applies automatically to generations recorded
241
+ through LangChain, LangGraph, OpenAI Agents, LlamaIndex, Google ADK, and Vercel AI SDK integrations.
242
+ The same redaction policy also applies to Strands Agents generations.
243
+
199
244
  Each framework handler injects:
200
245
 
201
- - `sigil.framework.name` (`langchain`, `langgraph`, `openai-agents`, `llamaindex`, `google-adk`, or `vercel-ai-sdk`)
202
- - `sigil.framework.source` (`handler` for existing callback handlers, `framework` for Vercel AI SDK hooks)
203
- - `sigil.framework.language` (`javascript` for existing callback handlers, `typescript` for Vercel AI SDK hooks)
246
+ - `sigil.framework.name` (`langchain`, `langgraph`, `openai-agents`, `llamaindex`, `google-adk`, `vercel-ai-sdk`, or `strands`)
247
+ - `sigil.framework.source` (`handler` for existing callback handlers, `framework` for Vercel AI SDK hooks, `hooks` for Strands)
248
+ - `sigil.framework.language` (`javascript` for existing callback handlers, `typescript` for Vercel AI SDK and Strands hooks)
204
249
  - `metadata["sigil.framework.run_id"]`
205
250
  - `metadata["sigil.framework.thread_id"]` (when present)
206
251
  - `metadata["sigil.framework.parent_run_id"]` (when available)
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type Span } from '@opentelemetry/api';
2
- import type { ContentCaptureMode, ConversationRatingInput, EmbeddingRecorder, EmbeddingResult, EmbeddingStart, Generation, GenerationMode, GenerationRecorder, GenerationStart, RecorderCallback, SigilDebugSnapshot, SigilSdkConfigInput, SubmitConversationRatingResponse, ToolExecution, ToolExecutionRecorder, ToolExecutionStart } from './types.js';
2
+ import type { ContentCaptureMode, ConversationRatingInput, EmbeddingRecorder, EmbeddingResult, EmbeddingStart, Generation, GenerationMode, GenerationRecorder, GenerationStart, HookEvaluateRequest, HookEvaluateResponse, HooksConfig, RecorderCallback, SigilDebugSnapshot, SigilSdkConfigInput, SubmitConversationRatingResponse, ToolExecution, ToolExecutionRecorder, ToolExecutionStart } from './types.js';
3
3
  export declare class SigilClient {
4
4
  private readonly config;
5
5
  private readonly nowFn;
@@ -63,6 +63,28 @@ export declare class SigilClient {
63
63
  startToolExecution<TResult>(start: ToolExecutionStart, callback: RecorderCallback<ToolExecutionRecorder, TResult>): Promise<TResult>;
64
64
  /** Submits a user-facing conversation rating through Sigil HTTP API. */
65
65
  submitConversationRating(conversationId: string, input: ConversationRatingInput): Promise<SubmitConversationRatingResponse>;
66
+ /**
67
+ * Returns the resolved hook configuration. Framework adapters use this to
68
+ * decide whether to invoke `evaluateHook` and which phases are configured.
69
+ */
70
+ get hooksConfig(): HooksConfig;
71
+ /**
72
+ * Evaluates synchronous hook rules for the given request.
73
+ *
74
+ * Use this to enforce preflight or postflight guardrails (PII, content
75
+ * policy, etc.) on the LLM call's critical path. The server returns
76
+ * `{ action: 'deny' }` to block; framework adapters typically translate that
77
+ * into a `HookDeniedError`.
78
+ *
79
+ * When `hooks.enabled` is false, this short-circuits to `allow`. When
80
+ * `hooks.failOpen` is true (default), network/timeout failures also resolve
81
+ * to `allow` so the LLM call can proceed.
82
+ *
83
+ * Framework adapters can pass `hooksConfigOverride` to override specific
84
+ * fields of the client's hooks config (e.g., force `enabled: true` when the
85
+ * adapter has its own `enableHooks` option).
86
+ */
87
+ evaluateHook(request: HookEvaluateRequest, hooksConfigOverride?: Partial<HooksConfig>): Promise<HookEvaluateResponse>;
66
88
  /** Forces immediate drain of queued generation exports. */
67
89
  flush(): Promise<void>;
68
90
  /** Flushes pending generations and shuts down the generation exporter. */
@@ -70,12 +92,18 @@ export declare class SigilClient {
70
92
  /** Returns a cloned in-memory snapshot for debugging and tests. */
71
93
  debugSnapshot(): SigilDebugSnapshot;
72
94
  internalNow(): Date;
95
+ internalAgentName(): string | undefined;
96
+ internalAgentVersion(): string | undefined;
97
+ internalUserId(): string | undefined;
98
+ internalTags(): Record<string, string> | undefined;
73
99
  internalRecordGeneration(generation: Generation): void;
74
100
  internalRecordToolExecution(toolExecution: ToolExecution): void;
75
101
  internalEnqueueGeneration(generation: Generation): void;
76
102
  internalLogWarn(message: string, error?: unknown): void;
77
103
  internalResolveGenerationContentCaptureMode(seed: GenerationStart): ContentCaptureMode;
78
104
  internalResolveToolIncludeContent(seed: ToolExecutionStart): boolean;
105
+ internalHasGenerationSanitizer(): boolean;
106
+ internalSanitizeGeneration(generation: Generation): Generation;
79
107
  internalStartGenerationSpan(seed: GenerationStart, mode: GenerationMode, startedAt: Date): Span;
80
108
  internalStartEmbeddingSpan(seed: EmbeddingStart, startedAt: Date): Span;
81
109
  internalStartToolExecutionSpan(seed: ToolExecutionStart, startedAt: Date): Span;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,IAAI,EAKV,MAAM,oBAAoB,CAAC;AAmB5B,OAAO,KAAK,EACV,kBAAkB,EAElB,uBAAuB,EAGvB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,UAAU,EAEV,cAAc,EACd,kBAAkB,EAElB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,WAAW,EAEX,gBAAgB,EAEhB,kBAAkB,EAGlB,mBAAmB,EACnB,gCAAgC,EAChC,aAAa,EACb,qBAAqB,EAErB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAuGpB,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwC;IAChE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAY;IACvD,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAY;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAY;IAC1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAY;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAoB;IAChD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAuB;IACtD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IAEvD,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAS;IAEvB;;;;OAIG;gBACS,WAAW,GAAE,mBAAwB;IA+BjD;;;;;;OAMG;IACH,eAAe,CAAC,KAAK,EAAE,eAAe,GAAG,kBAAkB;IAC3D,eAAe,CAAC,OAAO,EACrB,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,gBAAgB,CAAC,kBAAkB,EAAE,OAAO,CAAC,GACtD,OAAO,CAAC,OAAO,CAAC;IAQnB;;;;;;OAMG;IACH,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,kBAAkB;IACpE,wBAAwB,CAAC,OAAO,EAC9B,KAAK,EAAE,eAAe,EACtB,QAAQ,EAAE,gBAAgB,CAAC,kBAAkB,EAAE,OAAO,CAAC,GACtD,OAAO,CAAC,OAAO,CAAC;IAQnB;;;;;;OAMG;IACH,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,iBAAiB;IACxD,cAAc,CAAC,OAAO,EACpB,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,gBAAgB,CAAC,iBAAiB,EAAE,OAAO,CAAC,GACrD,OAAO,CAAC,OAAO,CAAC;IAgCnB;;;;OAIG;IACH,kBAAkB,CAAC,KAAK,EAAE,kBAAkB,GAAG,qBAAqB;IACpE,kBAAkB,CAAC,OAAO,EACxB,KAAK,EAAE,kBAAkB,EACzB,QAAQ,EAAE,gBAAgB,CAAC,qBAAqB,EAAE,OAAO,CAAC,GACzD,OAAO,CAAC,OAAO,CAAC;IAcnB,wEAAwE;IAClE,wBAAwB,CAC5B,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,uBAAuB,GAC7B,OAAO,CAAC,gCAAgC,CAAC;IA+D5C;;;OAGG;IACH,IAAI,WAAW,IAAI,WAAW,CAE7B;IAED;;;;;;;;;;;;;;;OAeG;IACG,YAAY,CAChB,OAAO,EAAE,mBAAmB,EAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GACzC,OAAO,CAAC,oBAAoB,CAAC;IAahC,2DAA2D;IACrD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,0EAA0E;IACpE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B/B,mEAAmE;IACnE,aAAa,IAAI,kBAAkB;IAQnC,WAAW,IAAI,IAAI;IAInB,iBAAiB,IAAI,MAAM,GAAG,SAAS;IAIvC,oBAAoB,IAAI,MAAM,GAAG,SAAS;IAI1C,cAAc,IAAI,MAAM,GAAG,SAAS;IAIpC,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS;IAIlD,wBAAwB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IAItD,2BAA2B,CAAC,aAAa,EAAE,aAAa,GAAG,IAAI;IAI/D,yBAAyB,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI;IA0BvD,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI;IAIvD,2CAA2C,CAAC,IAAI,EAAE,eAAe,GAAG,kBAAkB;IAQtF,iCAAiC,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO;IAUpE,8BAA8B,IAAI,OAAO;IAIzC,0BAA0B,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU;IAY9D,2BAA2B,CAAC,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI;IA2B/F,0BAA0B,CAAC,IAAI,EAAE,cAAc,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI;IASvE,8BAA8B,CAAC,IAAI,EAAE,kBAAkB,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI;IAU/E,iCAAiC,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI;IAU3E,0BAA0B,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI;IAIpE,kCAAkC,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAIpD,8BAA8B,CAC5B,IAAI,EAAE,IAAI,EACV,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,eAAe,EAAE,KAAK,GAAG,SAAS,EAClC,YAAY,EAAE,KAAK,GAAG,SAAS,EAC/B,YAAY,EAAE,IAAI,GAAG,SAAS,EAC9B,4BAA4B,CAAC,EAAE,MAAM,GACpC,IAAI;IA0CP,6BAA6B,CAC3B,IAAI,EAAE,IAAI,EACV,IAAI,EAAE,cAAc,EACpB,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,OAAO,EAClB,SAAS,EAAE,KAAK,GAAG,SAAS,EAC5B,UAAU,EAAE,KAAK,GAAG,SAAS,EAC7B,SAAS,EAAE,IAAI,EACf,WAAW,EAAE,IAAI,GAChB,IAAI;IAmCP,iCAAiC,CAC/B,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,KAAK,GAAG,SAAS,GAC5B,KAAK,GAAG,SAAS;IA0CpB,OAAO,CAAC,uBAAuB;IA8C/B,OAAO,CAAC,sBAAsB;IA6B9B,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,0BAA0B;IAiBlC,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,aAAa;YAaP,uBAAuB;YAYvB,eAAe;IAgC7B,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,OAAO;CAOhB"}
package/dist/client.js CHANGED
@@ -3,6 +3,7 @@ import { defaultLogger, mergeConfig } from './config.js';
3
3
  import { callContentCaptureResolver, resolveClientContentCaptureMode, resolveContentCaptureMode, shouldIncludeToolContent, stampContentCaptureMetadata, stripContent, } from './content_capture.js';
4
4
  import { agentNameFromContext, agentVersionFromContext, conversationIdFromContext, conversationTitleFromContext, userIdFromContext, } from './context.js';
5
5
  import { createDefaultGenerationExporter } from './exporters/default.js';
6
+ import { evaluateHook as evaluateHookImpl } from './hooks.js';
6
7
  import { asError, cloneArtifact, cloneEmbeddingResult, cloneEmbeddingStart, cloneGeneration, cloneGenerationResult, cloneGenerationStart, cloneMessage, cloneModelRef, cloneToolDefinition, cloneToolExecution, cloneToolExecutionResult, cloneToolExecutionStart, defaultOperationNameForMode, defaultSleep, encodedSizeBytes, maybeUnref, newLocalID, validateEmbeddingResult, validateEmbeddingStart, validateGeneration, validateToolExecution, } from './utils.js';
7
8
  const spanAttrGenerationID = 'sigil.generation.id';
8
9
  const spanAttrSDKName = 'sigil.sdk.name';
@@ -67,6 +68,12 @@ const metricTokenTypeCacheRead = 'cache_read';
67
68
  const metricTokenTypeCacheWrite = 'cache_write';
68
69
  const metricTokenTypeCacheCreation = 'cache_creation';
69
70
  const metricTokenTypeReasoning = 'reasoning';
71
+ const durationBucketsSeconds = [
72
+ 0.01, 0.02, 0.04, 0.08, 0.16, 0.32, 0.64, 1.28, 2.56, 5.12, 10.24, 20.48, 40.96, 81.92,
73
+ ];
74
+ const tokenUsageBuckets = [
75
+ 1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864,
76
+ ];
70
77
  const instrumentationName = 'github.com/grafana/sigil/sdks/js';
71
78
  const sdkName = 'sdk-js';
72
79
  const defaultEmbeddingOperationName = 'embeddings';
@@ -107,9 +114,18 @@ export class SigilClient {
107
114
  this.config.generationExporter ?? createDefaultGenerationExporter(this.config.generationExport);
108
115
  this.tracer = this.config.tracer ?? trace.getTracer(instrumentationName);
109
116
  this.meter = this.config.meter ?? metrics.getMeter(instrumentationName);
110
- this.operationDurationHistogram = this.meter.createHistogram(metricOperationDuration, { unit: 's' });
111
- this.tokenUsageHistogram = this.meter.createHistogram(metricTokenUsage, { unit: 'token' });
112
- this.ttftHistogram = this.meter.createHistogram(metricTimeToFirstToken, { unit: 's' });
117
+ this.operationDurationHistogram = this.meter.createHistogram(metricOperationDuration, {
118
+ unit: 's',
119
+ advice: { explicitBucketBoundaries: durationBucketsSeconds },
120
+ });
121
+ this.tokenUsageHistogram = this.meter.createHistogram(metricTokenUsage, {
122
+ unit: 'token',
123
+ advice: { explicitBucketBoundaries: tokenUsageBuckets },
124
+ });
125
+ this.ttftHistogram = this.meter.createHistogram(metricTimeToFirstToken, {
126
+ unit: 's',
127
+ advice: { explicitBucketBoundaries: durationBucketsSeconds },
128
+ });
113
129
  this.toolCallsHistogram = this.meter.createHistogram(metricToolCallsPerOperation, { unit: 'count' });
114
130
  if (this.config.generationExport.flushIntervalMs > 0) {
115
131
  this.flushTimer = setInterval(() => {
@@ -130,9 +146,21 @@ export class SigilClient {
130
146
  if (!notEmpty(seed.agentName)) {
131
147
  seed.agentName = agentNameFromContext();
132
148
  }
149
+ if (!notEmpty(seed.agentName)) {
150
+ const fromConfig = this.internalAgentName();
151
+ if (fromConfig !== undefined && fromConfig.length > 0) {
152
+ seed.agentName = fromConfig;
153
+ }
154
+ }
133
155
  if (!notEmpty(seed.agentVersion)) {
134
156
  seed.agentVersion = agentVersionFromContext();
135
157
  }
158
+ if (!notEmpty(seed.agentVersion)) {
159
+ const fromConfig = this.internalAgentVersion();
160
+ if (fromConfig !== undefined && fromConfig.length > 0) {
161
+ seed.agentVersion = fromConfig;
162
+ }
163
+ }
136
164
  const recorder = new EmbeddingRecorderImpl(this, seed);
137
165
  if (callback === undefined) {
138
166
  return recorder;
@@ -198,6 +226,40 @@ export class SigilClient {
198
226
  }
199
227
  return parseSubmitConversationRatingResponse(payload);
200
228
  }
229
+ /**
230
+ * Returns the resolved hook configuration. Framework adapters use this to
231
+ * decide whether to invoke `evaluateHook` and which phases are configured.
232
+ */
233
+ get hooksConfig() {
234
+ return this.config.hooks;
235
+ }
236
+ /**
237
+ * Evaluates synchronous hook rules for the given request.
238
+ *
239
+ * Use this to enforce preflight or postflight guardrails (PII, content
240
+ * policy, etc.) on the LLM call's critical path. The server returns
241
+ * `{ action: 'deny' }` to block; framework adapters typically translate that
242
+ * into a `HookDeniedError`.
243
+ *
244
+ * When `hooks.enabled` is false, this short-circuits to `allow`. When
245
+ * `hooks.failOpen` is true (default), network/timeout failures also resolve
246
+ * to `allow` so the LLM call can proceed.
247
+ *
248
+ * Framework adapters can pass `hooksConfigOverride` to override specific
249
+ * fields of the client's hooks config (e.g., force `enabled: true` when the
250
+ * adapter has its own `enableHooks` option).
251
+ */
252
+ async evaluateHook(request, hooksConfigOverride) {
253
+ this.assertOpen();
254
+ const effectiveHooks = hooksConfigOverride !== undefined ? { ...this.config.hooks, ...hooksConfigOverride } : this.config.hooks;
255
+ return evaluateHookImpl({
256
+ apiEndpoint: this.config.api.endpoint,
257
+ insecure: this.config.generationExport.insecure,
258
+ extraHeaders: this.config.generationExport.headers,
259
+ hooks: effectiveHooks,
260
+ request,
261
+ });
262
+ }
201
263
  /** Forces immediate drain of queued generation exports. */
202
264
  async flush() {
203
265
  this.assertOpen();
@@ -239,6 +301,18 @@ export class SigilClient {
239
301
  internalNow() {
240
302
  return this.nowFn();
241
303
  }
304
+ internalAgentName() {
305
+ return this.config.agentName;
306
+ }
307
+ internalAgentVersion() {
308
+ return this.config.agentVersion;
309
+ }
310
+ internalUserId() {
311
+ return this.config.userId;
312
+ }
313
+ internalTags() {
314
+ return this.config.tags;
315
+ }
242
316
  internalRecordGeneration(generation) {
243
317
  this.generations.push(cloneGeneration(generation));
244
318
  }
@@ -278,6 +352,20 @@ export class SigilClient {
278
352
  const resolverMode = callContentCaptureResolver(this.config.contentCaptureResolver, undefined);
279
353
  return shouldIncludeToolContent(seed.contentCapture ?? 'default', this.config.contentCapture, resolverMode, seed.includeContent ?? false);
280
354
  }
355
+ internalHasGenerationSanitizer() {
356
+ return this.config.generationSanitizer !== undefined;
357
+ }
358
+ internalSanitizeGeneration(generation) {
359
+ const sanitizer = this.config.generationSanitizer;
360
+ if (sanitizer === undefined) {
361
+ return generation;
362
+ }
363
+ const sanitized = sanitizer(cloneGeneration(generation));
364
+ if (sanitized === undefined) {
365
+ throw new Error('generation sanitizer must return a generation');
366
+ }
367
+ return cloneGeneration(sanitized);
368
+ }
281
369
  internalStartGenerationSpan(seed, mode, startedAt) {
282
370
  const operationName = seed.operationName ?? defaultOperationNameForMode(mode);
283
371
  const span = this.tracer.startSpan(generationSpanName(operationName, seed.model.name), {
@@ -635,12 +723,34 @@ class GenerationRecorderImpl {
635
723
  if (!notEmpty(this.seed.userId)) {
636
724
  this.seed.userId = userIdFromContext();
637
725
  }
726
+ if (!notEmpty(this.seed.userId)) {
727
+ const fromConfig = this.client.internalUserId();
728
+ if (fromConfig !== undefined && fromConfig.length > 0) {
729
+ this.seed.userId = fromConfig;
730
+ }
731
+ }
638
732
  if (!notEmpty(this.seed.agentName)) {
639
733
  this.seed.agentName = agentNameFromContext();
640
734
  }
735
+ if (!notEmpty(this.seed.agentName)) {
736
+ const fromConfig = this.client.internalAgentName();
737
+ if (fromConfig !== undefined && fromConfig.length > 0) {
738
+ this.seed.agentName = fromConfig;
739
+ }
740
+ }
641
741
  if (!notEmpty(this.seed.agentVersion)) {
642
742
  this.seed.agentVersion = agentVersionFromContext();
643
743
  }
744
+ if (!notEmpty(this.seed.agentVersion)) {
745
+ const fromConfig = this.client.internalAgentVersion();
746
+ if (fromConfig !== undefined && fromConfig.length > 0) {
747
+ this.seed.agentVersion = fromConfig;
748
+ }
749
+ }
750
+ const tags = this.client.internalTags();
751
+ if (tags !== undefined && Object.keys(tags).length > 0) {
752
+ this.seed.tags = { ...tags, ...(this.seed.tags ?? {}) };
753
+ }
644
754
  if (!notEmpty(this.seed.operationName)) {
645
755
  this.seed.operationName = defaultOperationNameForMode(this.seed.mode ?? defaultMode);
646
756
  }
@@ -675,7 +785,7 @@ class GenerationRecorderImpl {
675
785
  return;
676
786
  }
677
787
  this.ended = true;
678
- const generation = {
788
+ let generation = {
679
789
  id: this.seed.id ?? newLocalID('gen'),
680
790
  conversationId: firstNonEmptyString(this.result?.conversationId, this.seed.conversationId),
681
791
  conversationTitle: firstNonEmptyString(this.result?.conversationTitle, this.seed.conversationTitle),
@@ -734,14 +844,28 @@ class GenerationRecorderImpl {
734
844
  generation.metadata = {};
735
845
  }
736
846
  generation.metadata[spanAttrSDKName] = sdkName;
737
- const validationError = validateGeneration(generation);
738
847
  const callErrorCategory = errorCategoryFromError(this.callError, false);
848
+ let effectiveContentCaptureMode = this.contentCaptureMode;
849
+ let validationTarget = cloneGeneration(generation);
739
850
  stampContentCaptureMetadata(generation, this.contentCaptureMode);
740
851
  if (this.contentCaptureMode === 'metadata_only') {
741
852
  stripContent(generation, callErrorCategory);
742
853
  }
854
+ else if (this.client.internalHasGenerationSanitizer()) {
855
+ try {
856
+ generation = this.client.internalSanitizeGeneration(generation);
857
+ validationTarget = cloneGeneration(generation);
858
+ }
859
+ catch (error) {
860
+ effectiveContentCaptureMode = 'metadata_only';
861
+ stripContent(generation, callErrorCategory);
862
+ stampContentCaptureMetadata(generation, effectiveContentCaptureMode);
863
+ this.client.internalLogWarn('sigil generation sanitization failed; falling back to metadata_only', error);
864
+ }
865
+ }
866
+ const validationError = validateGeneration(validationTarget);
743
867
  this.client.internalSyncGenerationSpan(this.span, generation);
744
- if (this.contentCaptureMode === 'metadata_only') {
868
+ if (effectiveContentCaptureMode === 'metadata_only') {
745
869
  this.client.internalClearSpanConversationTitle(this.span);
746
870
  }
747
871
  this.client.internalApplyTraceContextFromSpan(this.span, generation);
@@ -761,7 +885,7 @@ class GenerationRecorderImpl {
761
885
  this.client.internalLogWarn('sigil generation enqueue failed', enqueueError);
762
886
  }
763
887
  }
764
- const finalCallError = this.contentCaptureMode === 'metadata_only' ? generation.callError : this.callError;
888
+ const finalCallError = effectiveContentCaptureMode === 'metadata_only' ? generation.callError : this.callError;
765
889
  this.client.internalFinalizeGenerationSpan(this.span, generation, finalCallError, validationError, enqueueError, this.firstTokenAt, callErrorCategory.length > 0 ? callErrorCategory : undefined);
766
890
  }
767
891
  getError() {
@@ -837,9 +961,21 @@ class ToolExecutionRecorderImpl {
837
961
  if (!notEmpty(this.seed.agentName)) {
838
962
  this.seed.agentName = agentNameFromContext();
839
963
  }
964
+ if (!notEmpty(this.seed.agentName)) {
965
+ const fromConfig = this.client.internalAgentName();
966
+ if (notEmpty(fromConfig)) {
967
+ this.seed.agentName = fromConfig;
968
+ }
969
+ }
840
970
  if (!notEmpty(this.seed.agentVersion)) {
841
971
  this.seed.agentVersion = agentVersionFromContext();
842
972
  }
973
+ if (!notEmpty(this.seed.agentVersion)) {
974
+ const fromConfig = this.client.internalAgentVersion();
975
+ if (notEmpty(fromConfig)) {
976
+ this.seed.agentVersion = fromConfig;
977
+ }
978
+ }
843
979
  this.resolvedIncludeContent = this.client.internalResolveToolIncludeContent(this.seed);
844
980
  this.startedAt = this.seed.startedAt ?? this.client.internalNow();
845
981
  this.span = this.client.internalStartToolExecutionSpan(this.seed, this.startedAt);