@compilr-dev/agents 0.3.14 → 0.3.15

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.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Shell command tokenizer for guardrail compound command parsing
3
+ *
4
+ * Splits compound shell commands (cmd1 | cmd2 && cmd3) into individual
5
+ * subcommands so each can be validated independently against guardrails.
6
+ */
7
+ /**
8
+ * A parsed subcommand from a compound shell command
9
+ */
10
+ export interface ShellToken {
11
+ /** Trimmed subcommand text */
12
+ command: string;
13
+ /** Operator that preceded this token (undefined for the first) */
14
+ operator?: '|' | '&&' | '||' | ';';
15
+ /** 0-based position in the command sequence */
16
+ index: number;
17
+ }
18
+ /**
19
+ * Parse a compound shell command into individual subcommands.
20
+ *
21
+ * Handles pipes, logical operators (&&, ||), semicolons, and respects
22
+ * single-quoted, double-quoted, and backtick-quoted strings.
23
+ *
24
+ * @param input - The full command string
25
+ * @returns Array of parsed subcommand tokens
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * parseShellCommand('ls && rm -rf /tmp')
30
+ * // => [
31
+ * // { command: 'ls', index: 0 },
32
+ * // { command: 'rm -rf /tmp', operator: '&&', index: 1 }
33
+ * // ]
34
+ * ```
35
+ */
36
+ export declare function parseShellCommand(input: string): ShellToken[];
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Shell command tokenizer for guardrail compound command parsing
3
+ *
4
+ * Splits compound shell commands (cmd1 | cmd2 && cmd3) into individual
5
+ * subcommands so each can be validated independently against guardrails.
6
+ */
7
+ /**
8
+ * Parse a compound shell command into individual subcommands.
9
+ *
10
+ * Handles pipes, logical operators (&&, ||), semicolons, and respects
11
+ * single-quoted, double-quoted, and backtick-quoted strings.
12
+ *
13
+ * @param input - The full command string
14
+ * @returns Array of parsed subcommand tokens
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * parseShellCommand('ls && rm -rf /tmp')
19
+ * // => [
20
+ * // { command: 'ls', index: 0 },
21
+ * // { command: 'rm -rf /tmp', operator: '&&', index: 1 }
22
+ * // ]
23
+ * ```
24
+ */
25
+ export function parseShellCommand(input) {
26
+ const tokens = [];
27
+ let current = '';
28
+ let quote = null;
29
+ let i = 0;
30
+ let operator;
31
+ while (i < input.length) {
32
+ const ch = input[i];
33
+ // Handle escape sequences inside double quotes
34
+ if (ch === '\\' && quote === '"' && i + 1 < input.length) {
35
+ current += ch + input[i + 1];
36
+ i += 2;
37
+ continue;
38
+ }
39
+ // Toggle quote state
40
+ if ((ch === "'" || ch === '"' || ch === '`') && (quote === null || quote === ch)) {
41
+ quote = quote === ch ? null : ch;
42
+ current += ch;
43
+ i++;
44
+ continue;
45
+ }
46
+ // Only split when outside quotes
47
+ if (quote === null) {
48
+ // && operator
49
+ if (ch === '&' && input[i + 1] === '&') {
50
+ pushToken(tokens, current, operator);
51
+ operator = '&&';
52
+ current = '';
53
+ i += 2;
54
+ continue;
55
+ }
56
+ // || operator (must check before single |)
57
+ if (ch === '|' && input[i + 1] === '|') {
58
+ pushToken(tokens, current, operator);
59
+ operator = '||';
60
+ current = '';
61
+ i += 2;
62
+ continue;
63
+ }
64
+ // | pipe (single)
65
+ if (ch === '|') {
66
+ pushToken(tokens, current, operator);
67
+ operator = '|';
68
+ current = '';
69
+ i++;
70
+ continue;
71
+ }
72
+ // ; semicolon
73
+ if (ch === ';') {
74
+ pushToken(tokens, current, operator);
75
+ operator = ';';
76
+ current = '';
77
+ i++;
78
+ continue;
79
+ }
80
+ }
81
+ current += ch;
82
+ i++;
83
+ }
84
+ // Push final segment
85
+ pushToken(tokens, current, operator);
86
+ return tokens;
87
+ }
88
+ /**
89
+ * Push a trimmed, non-empty token onto the array
90
+ */
91
+ function pushToken(tokens, command, operator) {
92
+ const trimmed = command.trim();
93
+ if (trimmed.length === 0)
94
+ return;
95
+ const token = {
96
+ command: trimmed,
97
+ index: tokens.length,
98
+ };
99
+ if (operator !== undefined) {
100
+ token.operator = operator;
101
+ }
102
+ tokens.push(token);
103
+ }
@@ -94,6 +94,14 @@ export interface GuardrailResult {
94
94
  * The input that triggered the guardrail
95
95
  */
96
96
  input?: unknown;
97
+ /**
98
+ * The specific subcommand that triggered (compound commands only)
99
+ */
100
+ subcommand?: string;
101
+ /**
102
+ * 0-based index of the subcommand in the compound command
103
+ */
104
+ subcommandIndex?: number;
97
105
  }
98
106
  /**
99
107
  * Context passed to guardrail handlers
package/dist/index.d.ts CHANGED
@@ -60,9 +60,11 @@ export type { TokenUsage, UsageRecord, UsageStats, TokenBudgetConfig, BudgetStat
60
60
  export { HooksManager } from './hooks/index.js';
61
61
  export type { HooksManagerOptions } from './hooks/index.js';
62
62
  export type { HooksConfig, HookRegistrationOptions, HookContext, IterationHookContext, LLMHookContext, AfterLLMHookContext, ToolHookContext, AfterToolHookContext, ErrorHookContext, BeforeIterationHook, AfterIterationHook, BeforeLLMHook, AfterLLMHook, BeforeToolHook, AfterToolHook, OnErrorHook, BeforeLLMHookResult, BeforeToolHookResult, AfterToolHookResult, ErrorHookResult, RegisteredHook, HookEventType, HookEvent, HookEventHandler, } from './hooks/index.js';
63
- export { TracingManager, createTracingHooks, createLoggingHooks, mergeHooks, createStructuredLogger, createNoopLogger, createBufferedLogger, createTracingLogger, formatDuration, formatBytes, redactSensitive, createOTelExporter, createConsoleExporter, createBatchExporter, createMultiExporter, OTelNotInstalledError, isOTelNotInstalledError, SemanticAttributes, } from './tracing/index.js';
64
- export type { Span, SpanStatus, SpanKind, SpanAttributes, SpanContext, SpanEvent, AttributeValue, Trace, TracingManagerOptions, StartSpanOptions, EndSpanOptions, TracingHooksConfig, TracingEvent, TracingEventHandler, OTelExporter, OTelSDK, OTelTracer, OTelSpan, LogLevel, LogEntry, StructuredLogger, StructuredLoggerOptions, TracingHookContext, TracingManagerInterface, } from './tracing/index.js';
63
+ export { TracingManager, createTracingHooks, createLoggingHooks, mergeHooks, createStructuredLogger, createNoopLogger, createBufferedLogger, createTracingLogger, formatDuration, formatBytes, redactSensitive, createOTelExporter, createConsoleExporter, createBatchExporter, createMultiExporter, OTelNotInstalledError, isOTelNotInstalledError, createOTelHooks, GenAIAttributes, AgentAttributes, PROVIDER_TO_SYSTEM, SemanticAttributes, } from './tracing/index.js';
64
+ export type { Span, SpanStatus, SpanKind, SpanAttributes, SpanContext, SpanEvent, AttributeValue, Trace, TracingManagerOptions, StartSpanOptions, EndSpanOptions, TracingHooksConfig, TracingEvent, TracingEventHandler, OTelExporter, OTelSDK, OTelTracer, OTelSpan, OTelHooksConfig, LogLevel, LogEntry, StructuredLogger, StructuredLoggerOptions, TracingHookContext, TracingManagerInterface, } from './tracing/index.js';
65
65
  export { TokenBucketRateLimiter, createRateLimiter, createNoopRateLimiter, RateLimitExceededError, isRateLimitExceededError, withRetry, createRetryWithRateLimit, RetryPresets, RetryStats, RateLimitedProvider, wrapWithRateLimit, ProviderRateLimits, } from './rate-limit/index.js';
66
66
  export type { RateLimiterConfig, RateLimiterStats, RetryConfig, RateLimitRetryConfig, AcquireResult, RateLimiter, } from './rate-limit/index.js';
67
67
  export { RehearsalManager, createRehearsalManager, GitRehearsalAnalyzer, createGitAnalyzer, FileRehearsalAnalyzer, createFileAnalyzer, } from './rehearsal/index.js';
68
68
  export type { ImpactSeverity, RehearsalRecommendation, OperationCategory, AffectedFile, RehearsalImpact, RehearsalResult, RehearsalContext, RehearsalAnalyzer, RehearsalManagerOptions, RehearsalEventType, RehearsalEvent, RehearsalEventHandler, } from './rehearsal/index.js';
69
+ export { estimateEffort, DEFAULT_WEIGHTS, EFFORT_ORDER } from './episodes/index.js';
70
+ export type { Effort, WorkEpisode, EffortSignals, EffortWeights, EffortSummary, ProjectWorkSummary, EpisodeStore, } from './episodes/index.js';
package/dist/index.js CHANGED
@@ -83,8 +83,10 @@ TracingManager,
83
83
  createTracingHooks, createLoggingHooks, mergeHooks,
84
84
  // Structured logging
85
85
  createStructuredLogger, createNoopLogger, createBufferedLogger, createTracingLogger, formatDuration, formatBytes, redactSensitive,
86
- // OpenTelemetry integration
86
+ // OpenTelemetry integration (post-hoc exporter)
87
87
  createOTelExporter, createConsoleExporter, createBatchExporter, createMultiExporter, OTelNotInstalledError, isOTelNotInstalledError,
88
+ // OpenTelemetry native hooks
89
+ createOTelHooks, GenAIAttributes, AgentAttributes, PROVIDER_TO_SYSTEM,
88
90
  // Semantic conventions
89
91
  SemanticAttributes, } from './tracing/index.js';
90
92
  // Rate Limiting & Retry
@@ -97,3 +99,5 @@ withRetry, createRetryWithRateLimit, RetryPresets, RetryStats,
97
99
  RateLimitedProvider, wrapWithRateLimit, ProviderRateLimits, } from './rate-limit/index.js';
98
100
  // Rehearsal - Impact analysis for destructive operations
99
101
  export { RehearsalManager, createRehearsalManager, GitRehearsalAnalyzer, createGitAnalyzer, FileRehearsalAnalyzer, createFileAnalyzer, } from './rehearsal/index.js';
102
+ // Episodes - Work history tracking with effort estimation
103
+ export { estimateEffort, DEFAULT_WEIGHTS, EFFORT_ORDER } from './episodes/index.js';
@@ -199,9 +199,16 @@ export class GeminiNativeProvider {
199
199
  case 'tool_result': {
200
200
  // Convert to functionResponse
201
201
  // block.content is a JSON string from the agent - parse it for the API
202
+ // Gemini requires response to be a Struct (object), never a string
202
203
  let responseData;
203
204
  try {
204
- responseData = JSON.parse(block.content);
205
+ const parsed = JSON.parse(block.content);
206
+ // Ensure we always have an object — parse may return a string
207
+ // (double-encoded JSON) or a primitive
208
+ responseData =
209
+ typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
210
+ ? parsed
211
+ : { result: parsed };
205
212
  }
206
213
  catch {
207
214
  // If not valid JSON, wrap the string in a result object
@@ -33,6 +33,7 @@ const DEFAULT_EXCLUDE_DIRS = [
33
33
  */
34
34
  export const globTool = defineTool({
35
35
  name: 'glob',
36
+ readonly: true,
36
37
  description: 'Find files matching glob patterns. ' +
37
38
  'Supports common patterns like **/*.ts for recursive matching. ' +
38
39
  'Returns a list of matching file paths.',
@@ -222,6 +223,7 @@ function patternToRegex(pattern) {
222
223
  export function createGlobTool(options) {
223
224
  return defineTool({
224
225
  name: 'glob',
226
+ readonly: true,
225
227
  description: 'Find files matching glob patterns. ' +
226
228
  'Supports common patterns like **/*.ts for recursive matching. ' +
227
229
  'Returns a list of matching file paths.',
@@ -34,6 +34,7 @@ const DEFAULT_EXCLUDE_DIRS = [
34
34
  */
35
35
  export const grepTool = defineTool({
36
36
  name: 'grep',
37
+ readonly: true,
37
38
  description: 'Search for patterns in files using regular expressions. ' +
38
39
  'Can search a single file or recursively through directories. ' +
39
40
  'Returns matching lines with optional context.',
@@ -319,6 +320,7 @@ function formatMatches(matches, options) {
319
320
  export function createGrepTool(options) {
320
321
  return defineTool({
321
322
  name: 'grep',
323
+ readonly: true,
322
324
  description: 'Search for patterns in files using regular expressions. ' +
323
325
  'Can search a single file or recursively through directories. ' +
324
326
  'Returns matching lines with optional context.',
@@ -18,6 +18,7 @@ const DEFAULT_MAX_CONTENT_SIZE = 100 * 1024;
18
18
  */
19
19
  export const readFileTool = defineTool({
20
20
  name: 'read_file',
21
+ readonly: true,
21
22
  description: 'Read the contents of a file. Returns the file content as text. ' +
22
23
  'Use maxLines and startLine to read specific portions of large files.',
23
24
  inputSchema: {
@@ -137,6 +138,7 @@ function formatSize(bytes) {
137
138
  export function createReadFileTool(options) {
138
139
  return defineTool({
139
140
  name: 'read_file',
141
+ readonly: true,
140
142
  description: 'Read the contents of a file. Returns the file content as text. ' +
141
143
  'Use maxLines and startLine to read specific portions of large files.',
142
144
  inputSchema: {
@@ -228,6 +228,7 @@ export const todoWriteTool = defineTool({
228
228
  */
229
229
  export const todoReadTool = defineTool({
230
230
  name: 'todo_read',
231
+ readonly: true,
231
232
  description: 'Get the SESSION todo list (shown in CLI footer). These are ephemeral session tasks - ' +
232
233
  'for persistent project backlog, use workitem_query. Optionally filter by status or owner.',
233
234
  inputSchema: {
@@ -340,6 +341,7 @@ export function createTodoTools(store) {
340
341
  });
341
342
  const todoRead = defineTool({
342
343
  name: 'todo_read',
344
+ readonly: true,
343
345
  description: 'Get the SESSION todo list (shown in CLI footer). These are ephemeral session tasks - ' +
344
346
  'for persistent project backlog, use workitem_query.',
345
347
  inputSchema: {
@@ -17,6 +17,7 @@ const DEFAULT_MAX_SIZE = 100 * 1024;
17
17
  */
18
18
  export const webFetchTool = defineTool({
19
19
  name: 'web_fetch',
20
+ readonly: true,
20
21
  description: 'Fetch content from a URL. Returns the page content as text. ' +
21
22
  'Use for retrieving documentation, API responses, or web pages. ' +
22
23
  'HTTP URLs are automatically upgraded to HTTPS.',
@@ -168,6 +169,7 @@ export function createWebFetchTool(options) {
168
169
  const { defaultTimeout = DEFAULT_TIMEOUT, defaultMaxSize = DEFAULT_MAX_SIZE, blockedDomains = [], allowedDomains, customHeaders = {}, } = options ?? {};
169
170
  return defineTool({
170
171
  name: 'web_fetch',
172
+ readonly: true,
171
173
  description: 'Fetch content from a URL. Returns the page content as text. ' +
172
174
  'Use for retrieving documentation, API responses, or web pages. ' +
173
175
  'HTTP URLs are automatically upgraded to HTTPS.',
@@ -35,6 +35,13 @@ export interface DefineToolOptions<T extends object> {
35
35
  * Default: false (normal visibility)
36
36
  */
37
37
  silent?: boolean;
38
+ /**
39
+ * If true, this tool performs no side effects (only reads data).
40
+ * Read-only tools are automatically batched for parallel execution
41
+ * even in a mixed batch with write tools.
42
+ * Default: false
43
+ */
44
+ readonly?: boolean;
38
45
  }
39
46
  /**
40
47
  * Define a tool with type-safe input handling
@@ -34,6 +34,7 @@ export function defineTool(options) {
34
34
  execute: options.execute,
35
35
  parallel: options.parallel,
36
36
  silent: options.silent,
37
+ readonly: options.readonly,
37
38
  };
38
39
  }
39
40
  /**
@@ -78,6 +78,13 @@ export interface Tool<T = object> {
78
78
  * Default: false (normal visibility)
79
79
  */
80
80
  silent?: boolean;
81
+ /**
82
+ * If true, this tool performs no side effects (only reads data).
83
+ * Read-only tools are automatically batched for parallel execution
84
+ * even in a mixed batch with write tools.
85
+ * Default: false
86
+ */
87
+ readonly?: boolean;
81
88
  }
82
89
  /**
83
90
  * Fallback handler for tools not found in the primary registry.
@@ -47,5 +47,8 @@ export { TracingManager } from './manager.js';
47
47
  export { createTracingHooks, createLoggingHooks, mergeHooks } from './hooks.js';
48
48
  export { createStructuredLogger, createNoopLogger, createBufferedLogger, createTracingLogger, formatDuration, formatBytes, redactSensitive, } from './logging.js';
49
49
  export { createOTelExporter, createConsoleExporter, createBatchExporter, createMultiExporter, OTelNotInstalledError, isOTelNotInstalledError, } from './otel.js';
50
+ export { createOTelHooks } from './otel-hooks.js';
51
+ export type { OTelHooksConfig } from './otel-hooks.js';
52
+ export { GenAIAttributes, AgentAttributes, PROVIDER_TO_SYSTEM } from './otel-attributes.js';
50
53
  export type { Span, SpanStatus, SpanKind, SpanAttributes, SpanContext, SpanEvent, AttributeValue, Trace, TracingManagerOptions, StartSpanOptions, EndSpanOptions, TracingHooksConfig, TracingEvent, TracingEventHandler, OTelExporter, OTelSDK, OTelTracer, OTelSpan, LogLevel, LogEntry, StructuredLogger, StructuredLoggerOptions, TracingHookContext, TracingManagerInterface, } from './types.js';
51
54
  export { SemanticAttributes } from './types.js';
@@ -49,7 +49,10 @@ export { TracingManager } from './manager.js';
49
49
  export { createTracingHooks, createLoggingHooks, mergeHooks } from './hooks.js';
50
50
  // Structured logging
51
51
  export { createStructuredLogger, createNoopLogger, createBufferedLogger, createTracingLogger, formatDuration, formatBytes, redactSensitive, } from './logging.js';
52
- // OpenTelemetry integration
52
+ // OpenTelemetry integration (post-hoc exporter)
53
53
  export { createOTelExporter, createConsoleExporter, createBatchExporter, createMultiExporter, OTelNotInstalledError, isOTelNotInstalledError, } from './otel.js';
54
+ // OpenTelemetry native hooks (real-time instrumentation)
55
+ export { createOTelHooks } from './otel-hooks.js';
56
+ export { GenAIAttributes, AgentAttributes, PROVIDER_TO_SYSTEM } from './otel-attributes.js';
54
57
  // Semantic conventions
55
58
  export { SemanticAttributes } from './types.js';
@@ -0,0 +1,59 @@
1
+ /**
2
+ * OpenTelemetry Semantic Convention Attributes
3
+ *
4
+ * Constants following the official OpenTelemetry gen_ai semantic conventions
5
+ * for LLM observability, plus agent-specific attributes.
6
+ *
7
+ * @see https://opentelemetry.io/docs/specs/semconv/gen-ai/
8
+ */
9
+ /**
10
+ * gen_ai.* semantic convention attributes for LLM operations
11
+ */
12
+ export declare const GenAIAttributes: {
13
+ /** The AI system (e.g. 'anthropic', 'openai', 'google') */
14
+ readonly SYSTEM: "gen_ai.system";
15
+ /** The operation name (e.g. 'chat') */
16
+ readonly OPERATION_NAME: "gen_ai.operation.name";
17
+ /** The model requested */
18
+ readonly REQUEST_MODEL: "gen_ai.request.model";
19
+ /** Max tokens requested */
20
+ readonly REQUEST_MAX_TOKENS: "gen_ai.request.max_tokens";
21
+ /** Temperature requested */
22
+ readonly REQUEST_TEMPERATURE: "gen_ai.request.temperature";
23
+ /** The model actually used in the response */
24
+ readonly RESPONSE_MODEL: "gen_ai.response.model";
25
+ /** Finish reasons (e.g. ['stop'], ['tool_calls']) */
26
+ readonly RESPONSE_FINISH_REASON: "gen_ai.response.finish_reasons";
27
+ /** Input token count */
28
+ readonly USAGE_INPUT_TOKENS: "gen_ai.usage.input_tokens";
29
+ /** Output token count */
30
+ readonly USAGE_OUTPUT_TOKENS: "gen_ai.usage.output_tokens";
31
+ };
32
+ /**
33
+ * Agent-specific attributes for iteration and tool tracking
34
+ */
35
+ export declare const AgentAttributes: {
36
+ /** Session ID for the agent run */
37
+ readonly SESSION_ID: "agent.session_id";
38
+ /** Current iteration number (1-indexed) */
39
+ readonly ITERATION_NUMBER: "agent.iteration.number";
40
+ /** Maximum allowed iterations */
41
+ readonly ITERATION_MAX: "agent.iteration.max";
42
+ /** Tool name being executed */
43
+ readonly TOOL_NAME: "agent.tool.name";
44
+ /** Whether the tool executed successfully */
45
+ readonly TOOL_SUCCESS: "agent.tool.success";
46
+ /** Tool execution duration in milliseconds */
47
+ readonly TOOL_DURATION_MS: "agent.tool.duration_ms";
48
+ /** Number of tool calls in the iteration */
49
+ readonly TOOL_CALL_COUNT: "agent.tool_call_count";
50
+ /** Number of messages in the conversation */
51
+ readonly MESSAGE_COUNT: "agent.message_count";
52
+ /** Whether the iteration completed with text (no tool calls) */
53
+ readonly COMPLETED_WITH_TEXT: "agent.completed_with_text";
54
+ };
55
+ /**
56
+ * Maps provider names (as used in the agents library) to gen_ai.system values
57
+ * following OpenTelemetry semantic conventions.
58
+ */
59
+ export declare const PROVIDER_TO_SYSTEM: Record<string, string>;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * OpenTelemetry Semantic Convention Attributes
3
+ *
4
+ * Constants following the official OpenTelemetry gen_ai semantic conventions
5
+ * for LLM observability, plus agent-specific attributes.
6
+ *
7
+ * @see https://opentelemetry.io/docs/specs/semconv/gen-ai/
8
+ */
9
+ /**
10
+ * gen_ai.* semantic convention attributes for LLM operations
11
+ */
12
+ export const GenAIAttributes = {
13
+ /** The AI system (e.g. 'anthropic', 'openai', 'google') */
14
+ SYSTEM: 'gen_ai.system',
15
+ /** The operation name (e.g. 'chat') */
16
+ OPERATION_NAME: 'gen_ai.operation.name',
17
+ /** The model requested */
18
+ REQUEST_MODEL: 'gen_ai.request.model',
19
+ /** Max tokens requested */
20
+ REQUEST_MAX_TOKENS: 'gen_ai.request.max_tokens',
21
+ /** Temperature requested */
22
+ REQUEST_TEMPERATURE: 'gen_ai.request.temperature',
23
+ /** The model actually used in the response */
24
+ RESPONSE_MODEL: 'gen_ai.response.model',
25
+ /** Finish reasons (e.g. ['stop'], ['tool_calls']) */
26
+ RESPONSE_FINISH_REASON: 'gen_ai.response.finish_reasons',
27
+ /** Input token count */
28
+ USAGE_INPUT_TOKENS: 'gen_ai.usage.input_tokens',
29
+ /** Output token count */
30
+ USAGE_OUTPUT_TOKENS: 'gen_ai.usage.output_tokens',
31
+ };
32
+ /**
33
+ * Agent-specific attributes for iteration and tool tracking
34
+ */
35
+ export const AgentAttributes = {
36
+ /** Session ID for the agent run */
37
+ SESSION_ID: 'agent.session_id',
38
+ /** Current iteration number (1-indexed) */
39
+ ITERATION_NUMBER: 'agent.iteration.number',
40
+ /** Maximum allowed iterations */
41
+ ITERATION_MAX: 'agent.iteration.max',
42
+ /** Tool name being executed */
43
+ TOOL_NAME: 'agent.tool.name',
44
+ /** Whether the tool executed successfully */
45
+ TOOL_SUCCESS: 'agent.tool.success',
46
+ /** Tool execution duration in milliseconds */
47
+ TOOL_DURATION_MS: 'agent.tool.duration_ms',
48
+ /** Number of tool calls in the iteration */
49
+ TOOL_CALL_COUNT: 'agent.tool_call_count',
50
+ /** Number of messages in the conversation */
51
+ MESSAGE_COUNT: 'agent.message_count',
52
+ /** Whether the iteration completed with text (no tool calls) */
53
+ COMPLETED_WITH_TEXT: 'agent.completed_with_text',
54
+ };
55
+ /**
56
+ * Maps provider names (as used in the agents library) to gen_ai.system values
57
+ * following OpenTelemetry semantic conventions.
58
+ */
59
+ export const PROVIDER_TO_SYSTEM = {
60
+ claude: 'anthropic',
61
+ openai: 'openai',
62
+ gemini: 'google',
63
+ 'gemini-native': 'google',
64
+ ollama: 'ollama',
65
+ groq: 'groq',
66
+ together: 'together_ai',
67
+ fireworks: 'fireworks_ai',
68
+ openrouter: 'openrouter',
69
+ perplexity: 'perplexity',
70
+ mock: 'mock',
71
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Native OpenTelemetry Hooks
3
+ *
4
+ * Creates real OTel spans during agent execution with proper context propagation
5
+ * and gen_ai semantic conventions. Unlike the post-hoc `createOTelExporter()`,
6
+ * these hooks create spans in real time with correct parent-child relationships.
7
+ *
8
+ * Requires `@opentelemetry/api` (regular dependency, no-ops without SDK registered).
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { createOTelHooks } from '@compilr-dev/agents';
13
+ *
14
+ * const hooks = createOTelHooks({
15
+ * providerName: 'claude',
16
+ * tracerName: 'my-agent',
17
+ * });
18
+ *
19
+ * const agent = new Agent({ provider, hooks });
20
+ * ```
21
+ */
22
+ import type { HooksConfig } from '../hooks/types.js';
23
+ /**
24
+ * Configuration for OTel hooks
25
+ */
26
+ export interface OTelHooksConfig {
27
+ /** Tracer name (default: '@compilr-dev/agents') */
28
+ tracerName?: string;
29
+ /** Tracer version */
30
+ tracerVersion?: string;
31
+ /** Provider name for gen_ai.system mapping (e.g. 'claude', 'openai') */
32
+ providerName?: string;
33
+ /** Trace iteration spans (default: true) */
34
+ traceIterations?: boolean;
35
+ /** Trace LLM call spans (default: true) */
36
+ traceLLM?: boolean;
37
+ /** Trace tool execution spans (default: true) */
38
+ traceTools?: boolean;
39
+ /** Include input/output content in span attributes (default: false) */
40
+ includeIO?: boolean;
41
+ /** Truncate attribute values at this length (default: 1000) */
42
+ truncateAt?: number;
43
+ }
44
+ /**
45
+ * Create native OpenTelemetry hooks for agent instrumentation.
46
+ *
47
+ * These hooks create real OTel spans during execution, as opposed to the
48
+ * post-hoc `createOTelExporter()` which recreates spans after the fact.
49
+ *
50
+ * Span hierarchy:
51
+ * ```
52
+ * agent.iteration [INTERNAL]
53
+ * ├── gen_ai.chat [CLIENT] (gen_ai.system, tokens, model)
54
+ * ├── agent.tool.read_file [INTERNAL] (tool.name, success, duration)
55
+ * └── agent.tool.bash [INTERNAL]
56
+ * ```
57
+ *
58
+ * @param config - Optional configuration
59
+ * @returns HooksConfig ready to pass to Agent constructor
60
+ */
61
+ export declare function createOTelHooks(config?: OTelHooksConfig): HooksConfig;