@agimon-ai/log-sink-mcp 0.24.4 → 0.25.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 +109 -16
- package/dist/cli.cjs +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/extensions/pi.cjs +1 -1
- package/dist/extensions/pi.mjs +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/piTelemetry-BYKSOd4F.cjs +2 -0
- package/dist/piTelemetry-BYKSOd4F.cjs.map +1 -0
- package/dist/piTelemetry-Dlhhs5sx.mjs +2 -0
- package/dist/piTelemetry-Dlhhs5sx.mjs.map +1 -0
- package/dist/{stdio-Gb0mj6eo.cjs → stdio-QFR4qcFs.cjs} +2 -2
- package/dist/stdio-QFR4qcFs.cjs.map +1 -0
- package/dist/{stdio-DaqaONHT.mjs → stdio-kDI4sBvK.mjs} +2 -2
- package/dist/stdio-kDI4sBvK.mjs.map +1 -0
- package/package.json +4 -4
- package/dist/piTelemetry-BPQgGDpj.mjs +0 -2
- package/dist/piTelemetry-BPQgGDpj.mjs.map +0 -1
- package/dist/piTelemetry-P-o6KRQk.cjs +0 -2
- package/dist/piTelemetry-P-o6KRQk.cjs.map +0 -1
- package/dist/stdio-DaqaONHT.mjs.map +0 -1
- package/dist/stdio-Gb0mj6eo.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -7,8 +7,9 @@ AI-powered log analysis MCP server with HTTP ingestion and SQLite storage.
|
|
|
7
7
|
`log-sink-mcp` provides a complete log analysis solution for AI assistants like Claude. It combines:
|
|
8
8
|
|
|
9
9
|
- **HTTP Server**: REST endpoint for log ingestion from any application
|
|
10
|
-
- **MCP Server**:
|
|
10
|
+
- **MCP Server**: 5 application-focused tools for searching/filtering, tracing, and inspecting logs
|
|
11
11
|
- **CLI Analysis Commands**: Local shell commands for deeper error and agent-issue analysis
|
|
12
|
+
- **Pi Extension**: Ships a Pi extension that exports the agent's own session telemetry into the sink
|
|
12
13
|
- **SQLite Database**: Fast local storage with FTS5 full-text search plus cached local embeddings
|
|
13
14
|
- **Real-time Analysis**: Query logs, trace distributed requests, analyze error patterns
|
|
14
15
|
|
|
@@ -17,7 +18,8 @@ Perfect for debugging, monitoring, and understanding application behavior throug
|
|
|
17
18
|
## Features
|
|
18
19
|
|
|
19
20
|
- ✅ **HTTP Log Ingestion**: POST logs from any application via REST API
|
|
20
|
-
- ✅ **
|
|
21
|
+
- ✅ **5 MCP Tools**: Search/filter, trace, agent-issue analysis, and inspect stats/services
|
|
22
|
+
- ✅ **Agent Telemetry**: Pi extension records prompts, turns, tool calls, token usage, and cost
|
|
21
23
|
- ✅ **Hybrid Search**: FTS5-powered search plus optional local semantic retrieval across messages and errors
|
|
22
24
|
- ✅ **Distributed Tracing**: Timeline view for trace IDs and span relationships
|
|
23
25
|
- ✅ **Error Analysis**: Pattern detection and error categorization
|
|
@@ -43,7 +45,7 @@ Perfect for debugging, monitoring, and understanding application behavior throug
|
|
|
43
45
|
▼
|
|
44
46
|
┌──────────────┐ MCP Protocol ┌─────────────────┐
|
|
45
47
|
│ Claude / │ <─────────────────────> │ MCP Server │
|
|
46
|
-
│ AI Agent │
|
|
48
|
+
│ AI Agent │ 5 App Log Tools │ (stdio) │
|
|
47
49
|
└──────────────┘ └────────┬────────┘
|
|
48
50
|
│
|
|
49
51
|
▼
|
|
@@ -319,10 +321,13 @@ Search and filter log entries. Without `query`/`searchQuery`, it behaves as a st
|
|
|
319
321
|
- `traceId?`: string - Filter by trace ID
|
|
320
322
|
- `spanId?`: string - Filter by span ID
|
|
321
323
|
- `errorType?`: string - Filter by error type
|
|
322
|
-
- `sessionId?`: string - Filter by agent session ID
|
|
323
|
-
- `
|
|
324
|
-
- `
|
|
325
|
-
- `
|
|
324
|
+
- `sessionId?` / `parentSessionId?`: string - Filter by agent session ID or its parent
|
|
325
|
+
- `agentName?`: string - Filter by agent name
|
|
326
|
+
- `workflowRunId?` / `workflowRunKey?` / `workflowId?`: string - Filter by workflow run ID, stable run key, or workflow file ID
|
|
327
|
+
- `workflowName?` / `workflowWorkspace?`: string - Filter by workflow name or workspace
|
|
328
|
+
- `jobName?` / `jobId?`: string - Filter by workflow job name or job execution ID
|
|
329
|
+
- `stepName?` / `phase?`: string - Filter by workflow step name or phase
|
|
330
|
+
- `signalType?`: string - `log` or `span`
|
|
326
331
|
- `startTime?`: string - ISO 8601 start time filter
|
|
327
332
|
- `endTime?`: string - ISO 8601 end time filter
|
|
328
333
|
- `limit?`: number - Maximum results (default: 100)
|
|
@@ -336,7 +341,30 @@ Find error logs from user-service that mention "database timeout"
|
|
|
336
341
|
|
|
337
342
|
---
|
|
338
343
|
|
|
339
|
-
### 2. `
|
|
344
|
+
### 2. `analyze_agent_issues`
|
|
345
|
+
|
|
346
|
+
Detect and aggregate agent-run problems for reviewing agent performance and improving the harness: failed tool calls, API errors, refusals, retries exhausted, rejected tools, and error logs. Unlike error-severity analysis, this understands agent event semantics, so it also catches warning-level returned tool errors and info-level tool results carrying `success=false`.
|
|
347
|
+
|
|
348
|
+
**Parameters:**
|
|
349
|
+
- `sessionId?` / `parentSessionId?`: string - Scope to one agent launch
|
|
350
|
+
- `agentName?`: string - Scope to one agent identity
|
|
351
|
+
- `service?`: string or string[] - Scope by agent/service name (for example `claude-code`, `codex`)
|
|
352
|
+
- `workflowRunId?` / `workflowRunKey?` / `workflowName?` / `workflowWorkspace?`: string - Scope by workflow
|
|
353
|
+
- `jobName?` / `jobId?` / `stepName?` / `phase?`: string - Scope within a run
|
|
354
|
+
- `startTime?` / `endTime?`: string - ISO 8601 analysis window
|
|
355
|
+
- `limit?`: number - Maximum issue samples returned (default: 100)
|
|
356
|
+
- `scanLimit?`: number - Maximum matching records scanned (default: 50000); the response reports when this truncates the window
|
|
357
|
+
|
|
358
|
+
**Example:**
|
|
359
|
+
```
|
|
360
|
+
Why did the last agent session keep failing its edits?
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**Returns:** Counts by category, tool, and error type, plus sample issues carrying the tool, input, and error context needed to diagnose them.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
### 3. `get_trace_timeline`
|
|
340
368
|
|
|
341
369
|
Get chronological timeline for a distributed trace.
|
|
342
370
|
|
|
@@ -352,22 +380,25 @@ Show me the trace timeline for trace ID a1b2c3d4e5f6789012345678901234ab
|
|
|
352
380
|
|
|
353
381
|
---
|
|
354
382
|
|
|
355
|
-
###
|
|
383
|
+
### 4. `get_log_stats`
|
|
356
384
|
|
|
357
|
-
Get statistics grouped
|
|
385
|
+
Get aggregate statistics, grouped along whichever dimension you ask for.
|
|
358
386
|
|
|
359
|
-
**Parameters:**
|
|
387
|
+
**Parameters:**
|
|
388
|
+
- `groupBy?`: string - `both` (default), `level`, `service`, `agent`, `workflow`, `job`, or `signal`
|
|
389
|
+
- `signalType?`: string - `log` (default), `span`, or `all`
|
|
390
|
+
- `startTime?` / `endTime?`: string - ISO 8601 window
|
|
360
391
|
|
|
361
392
|
**Example:**
|
|
362
393
|
```
|
|
363
394
|
Show me log statistics
|
|
364
395
|
```
|
|
365
396
|
|
|
366
|
-
**Returns:**
|
|
397
|
+
**Returns:** Counts per group, plus a total.
|
|
367
398
|
|
|
368
399
|
---
|
|
369
400
|
|
|
370
|
-
###
|
|
401
|
+
### 5. `get_services`
|
|
371
402
|
|
|
372
403
|
List all unique services that have logged.
|
|
373
404
|
|
|
@@ -380,6 +411,44 @@ What services are logging to the database?
|
|
|
380
411
|
|
|
381
412
|
**Returns:** Array of service names with log counts.
|
|
382
413
|
|
|
414
|
+
## Pi extension
|
|
415
|
+
|
|
416
|
+
The package ships a [Pi](https://github.com/earendil-works/pi) extension, declared in the `pi.extensions` field of `package.json` and loaded when the package is installed as a Pi package. It registers no commands and no tools. It subscribes to Pi's session events and exports them as OTLP logs under the service name `pi`, so the agent's own behaviour lands in the same database as your application's.
|
|
417
|
+
|
|
418
|
+
| Pi event | Record | Level |
|
|
419
|
+
|---|---|---|
|
|
420
|
+
| `session_start` | `pi.session.started` | info |
|
|
421
|
+
| `before_agent_start` | `pi.user_prompt` | info |
|
|
422
|
+
| `agent_start` | `pi.agent.started` | info |
|
|
423
|
+
| `turn_start` | `pi.turn.started` | debug |
|
|
424
|
+
| `turn_end` | `pi.turn.finished` | info |
|
|
425
|
+
| `tool_execution_start` | `pi.tool_call` | debug |
|
|
426
|
+
| `tool_execution_end` | `pi.tool_result` | info |
|
|
427
|
+
| `after_provider_response` | `pi.api_response`, or `pi.api_error` on status 400 and above | debug / error |
|
|
428
|
+
| `model_select` | `pi.model.selected` | info |
|
|
429
|
+
| `agent_end` | `pi.agent.finished` | info |
|
|
430
|
+
| `agent_settled` | `pi.agent.settled` | debug |
|
|
431
|
+
| `session_shutdown` | `pi.session.finished` | info |
|
|
432
|
+
|
|
433
|
+
Every record carries the session ID, cwd, mode, thinking level, and selected model. `pi.turn.finished` adds the standard `gen_ai.usage.*` attributes: input, output, cache read, cache write, reasoning, total tokens, and cost. `pi.tool_result` adds the tool name and its duration in milliseconds.
|
|
434
|
+
|
|
435
|
+
Two things are deliberate about what it does not do:
|
|
436
|
+
|
|
437
|
+
- **Prompts and tool payloads are omitted by default.** Set `AGENT_OTEL_REDACT=0` to include `user.prompt`, `tool.input`, and `tool.result`.
|
|
438
|
+
- **Telemetry is dropped rather than written to disk when no sink is reachable.** Endpoint discovery health-checks a running server; if there is none, the file backend is shut down instead of quietly filling a log directory. Set `LOG_SINK_PI_FILE_FALLBACK=1` to keep it.
|
|
439
|
+
|
|
440
|
+
Failures in the extension itself warn to the console and never propagate: telemetry that breaks the agent it is measuring is worse than no telemetry.
|
|
441
|
+
|
|
442
|
+
Once a session has run, query it like any other service:
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
# What went wrong in this agent session
|
|
446
|
+
log-sink-mcp logs agent-issues --session-id <session-id>
|
|
447
|
+
|
|
448
|
+
# Token spend and per-tool consumption for the session
|
|
449
|
+
log-sink-mcp logs metrics --service pi --group-by agent
|
|
450
|
+
```
|
|
451
|
+
|
|
383
452
|
## Integration Examples
|
|
384
453
|
|
|
385
454
|
### Pino Logger
|
|
@@ -548,7 +617,7 @@ bun run src/cli.ts mcp-serve --type sse --port 3002
|
|
|
548
617
|
|
|
549
618
|
#### `logs` command
|
|
550
619
|
|
|
551
|
-
Run log analysis capabilities directly from the shell.
|
|
620
|
+
Run log analysis capabilities directly from the shell. Most subcommands mirror an MCP tool; `analyze-errors` and `clear` are CLI-only.
|
|
552
621
|
|
|
553
622
|
```bash
|
|
554
623
|
bun run src/cli.ts logs <subcommand> [options]
|
|
@@ -560,6 +629,7 @@ Subcommands:
|
|
|
560
629
|
- `search --mode hybrid`: Default hybrid search across FTS5 and semantic embeddings
|
|
561
630
|
- `trace`: Equivalent to `get_trace_timeline`
|
|
562
631
|
- `analyze-errors`: CLI-only grouped error analysis
|
|
632
|
+
- `agent-issues`: Equivalent to `analyze_agent_issues`
|
|
563
633
|
- `stats`: Equivalent to `get_log_stats`
|
|
564
634
|
- `metrics`: Workflow, token, failure, and per-tool consumption metrics
|
|
565
635
|
- `services`: Equivalent to `get_services`
|
|
@@ -603,7 +673,22 @@ bun run src/cli.ts logs services
|
|
|
603
673
|
|
|
604
674
|
### Environment Variables
|
|
605
675
|
|
|
606
|
-
None required
|
|
676
|
+
None are required: the server and its CLI are configured entirely through flags. These apply to the telemetry side, meaning the Pi extension and any process using `@agimon-ai/log-sink-mcp/telemetry/node`.
|
|
677
|
+
|
|
678
|
+
| Variable | Effect |
|
|
679
|
+
|---|---|
|
|
680
|
+
| `LOG_SINK_ENDPOINT` | Send to this log-sink instead of discovering a running one |
|
|
681
|
+
| `OTEL_EXPORTER_OTLP_ENDPOINT` | Standard OTLP endpoint; the signal-specific `_LOGS_` and `_TRACES_` forms take precedence, and any of them override discovery |
|
|
682
|
+
| `OTEL_EXPORTER_OTLP_HEADERS` | Extra export headers; `_LOGS_` and `_TRACES_` variants apply per signal |
|
|
683
|
+
| `OTEL_EXPORTER_OTLP_TIMEOUT` | Export timeout in milliseconds; `_LOGS_` and `_TRACES_` variants apply per signal |
|
|
684
|
+
| `OTEL_SDK_DISABLED` | Disable telemetry entirely |
|
|
685
|
+
| `AGENT_TELEMETRY_DISABLED=1` | Disable the Pi extension only |
|
|
686
|
+
| `AGENT_OTEL_TRACES` | `1`, `true`, or `yes` to export spans as well as logs |
|
|
687
|
+
| `AGENT_OTEL_REDACT=0` | Include prompts, tool inputs, and tool results |
|
|
688
|
+
| `LOG_SINK_PI_FILE_FALLBACK=1` | Let the Pi extension write to files when no sink is reachable |
|
|
689
|
+
| `LOG_SINK_FILE_MAX_BYTES`, `LOG_SINK_FILE_MAX_FILES` | Rotation limits for the file backend |
|
|
690
|
+
|
|
691
|
+
Telemetry is tagged for later filtering from the environment it runs in. `AGENT_SESSION_ID` and `PARENT_AGENT_SESSION_ID` scope records to an agent launch, and `WORKFLOW_RUN_ID`, `WORKFLOW_RUN_KEY`, `WORKFLOW_ID`, `WORKFLOW_NAME`, `WORKFLOW_WORKSPACE`, `WORKFLOW_JOB_ID`, `WORKFLOW_JOB_NAME`, `WORKFLOW_STEP_NAME`, `WORKFLOW_PHASE`, and `WORKFLOW_RUNNER` scope them to a workflow run. Each becomes an ingest header and then a queryable field, which is what makes `--workflow-run-id` and `--session-id` work across the CLI and MCP tools.
|
|
607
692
|
|
|
608
693
|
## Development
|
|
609
694
|
|
|
@@ -628,8 +713,15 @@ log-sink-mcp/
|
|
|
628
713
|
│ ├── cli.ts # CLI entry point
|
|
629
714
|
│ ├── commands/ # CLI commands
|
|
630
715
|
│ │ ├── http-serve.ts # HTTP server command
|
|
631
|
-
│ │
|
|
716
|
+
│ │ ├── mcp-serve.ts # MCP server command
|
|
717
|
+
│ │ ├── logs.ts # Log analysis subcommands
|
|
718
|
+
│ │ └── claude-hook.ts # Claude Code hook context command
|
|
632
719
|
│ ├── container/ # InversifyJS DI container
|
|
720
|
+
│ ├── extensions/ # Pi extension
|
|
721
|
+
│ │ ├── pi.ts # Extension entry point
|
|
722
|
+
│ │ └── piTelemetry.ts # Pi session event to OTLP mapping
|
|
723
|
+
│ ├── telemetry/
|
|
724
|
+
│ │ └── node.ts # Node OTLP setup, endpoint discovery, file fallback
|
|
633
725
|
│ ├── models/ # Drizzle schemas
|
|
634
726
|
│ │ └── schema.ts # Log table schema
|
|
635
727
|
│ ├── server/
|
|
@@ -645,6 +737,7 @@ log-sink-mcp/
|
|
|
645
737
|
│ │ ├── QueryLogsTool.ts # Compatibility wrapper for filter-only queries
|
|
646
738
|
│ │ ├── GetTraceTimelineTool.ts
|
|
647
739
|
│ │ ├── AnalyzeErrorsTool.ts
|
|
740
|
+
│ │ ├── AnalyzeAgentIssuesTool.ts
|
|
648
741
|
│ │ ├── GetLogStatsTool.ts
|
|
649
742
|
│ │ ├── GetServicesTool.ts
|
|
650
743
|
│ │ └── ClearLogsTool.ts
|
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const e=require(`./node-9pwprHry.cjs`),t=require(`./stdio-
|
|
2
|
+
const e=require(`./node-9pwprHry.cjs`),t=require(`./stdio-QFR4qcFs.cjs`);let n=require(`zod`),r=require(`node:fs`),i=require(`node:fs/promises`);i=e.u(i,1);let a=require(`node:path`),o=require(`node:url`),s=require(`@agimon-ai/foundation-port-registry`),c=require(`@agimon-ai/foundation-process-registry`),l=require(`commander`),u=require(`@hono/node-server`),d=require(`node:crypto`),f=require(`hono`),p=require(`hono/cors`),m=require(`hono-rate-limiter`),h=require(`hono/html`),g=require(`hono/jsx/jsx-runtime`);function _(e){return e.option(`--config <path>`,`Path to .logsink.yaml`).option(`--global`,`Use the global log-sink instance`).option(`--instance <scope>`,`Force log-sink instance: local or global`)}function v(e){let t=e.instance?.trim();if(e.global&&t&&t.toLowerCase()!==`global`)throw Error(`Use either --global or --instance local, not both`);return e.global?`global`:t}function y(t,n=e.s){t.config&&(process.env.LOG_SINK_CONFIG=t.config);let r=v(t);return r&&(process.env.LOG_SINK_INSTANCE=r),e.c({configPath:t.config,packageName:n})}const b=[`error`,`fatal`];function x(e){let t=e.trim();if(!t)return null;try{return JSON.parse(t)}catch{return null}}function S(e){if(e==null)return`unknown`;if(typeof e==`string`)return e;try{return JSON.stringify(e,null,2)}catch{return String(e)}}function C(e){return e.hook_event_name===`PostToolUseFailure`?`Current tool failure:\n- Tool: ${e.tool_name??`unknown tool`}\n- Error: ${S(e.error)}`:null}function w(e,t,n){let r=new Map;for(let t of e){let e=`${t.service}::${t.level}::${t.error_type??``}::${t.error_message??t.message}`,i=new Date(Number(t.created_at)).toISOString();r.has(e)||r.set(e,{key:e,count:0,service:t.service,level:t.level,errorType:t.error_type,errorMessage:t.error_message,message:t.message,firstSeen:i,lastSeen:i,traceIds:new Set,examples:[]});let a=r.get(e);a.count+=1,a.lastSeen=i,t.trace_id&&a.traceIds.add(t.trace_id),a.examples.length<n&&a.examples.push({id:t.id,timestamp:i,traceId:t.trace_id})}return Array.from(r.values()).sort((e,t)=>t.count-e.count||t.lastSeen.localeCompare(e.lastSeen)).slice(0,t)}function ee(e,t,n,r){let i=w(e,n,r),a=[];a.push(`Recent log errors (${e.length} new):`),t.tool_name&&a.push(`- Triggered after tool: ${t.tool_name}`);for(let[e,t]of i.entries()){a.push(`${e+1}. ${t.service} [${t.level}] x${t.count} ${t.errorType?`(${t.errorType}) `:``}${t.errorMessage??t.message}`),a.push(` First seen: ${t.firstSeen}`),a.push(` Last seen: ${t.lastSeen}`),t.traceIds.size>0&&a.push(` Trace IDs: ${Array.from(t.traceIds).slice(0,3).join(`, `)}`);for(let e of t.examples)a.push(` Example: ${e.timestamp} id=${e.id}${e.traceId?` trace=${e.traceId}`:``}`)}if(t.hook_event_name===`PostToolUseFailure`){let e=C(t);e&&a.push(``,e)}return a.join(`
|
|
3
3
|
`)}function te(t){if(t.session_id?.trim())return(0,s.normalizeRepositoryPath)(e.l(t.cwd?.trim()||process.cwd()))}async function ne(t,n){let i=v(n);if(n.dbPath){let e=i===`global`?`global`:`local`;return(0,r.existsSync)(n.dbPath)?{dbPath:n.dbPath,scope:e}:void 0}let a={...process.env};n.config&&(a.LOG_SINK_CONFIG=n.config),i&&(a.LOG_SINK_INSTANCE=i);let o=e.c({configPath:n.config,cwd:t.cwd??process.cwd(),env:a,packageName:e.s}),c=new s.PortRegistryService(a.PORT_REGISTRY_PATH??s.DEFAULT_REGISTRY_PATH);try{let e=await c.getPort({repositoryPath:o.repositoryPath,serviceName:`log-sink-mcp-http`,serviceType:`tool`,environment:a.NODE_ENV||`development`}),t=e.record?.metadata?.dbPath;if(e.success&&typeof t==`string`&&t&&(0,r.existsSync)(t))return{dbPath:t,scope:o.scope}}catch{}return(0,r.existsSync)(o.dbPath)?{dbPath:o.dbPath,scope:o.scope}:void 0}async function re(e,t,n,r,i,a){let o=e.getSqliteClient(),s=Date.now()-n*60*1e3,c=``;if(t){let e=o.prepare(`
|
|
4
4
|
SELECT id, created_at
|
|
5
5
|
FROM logs
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,p as c,r as l,s as u,t as d,u as f}from"./stdio-
|
|
2
|
+
import{a as e,c as t,d as n,f as r,i,l as a,n as o,o as s,p as c,r as l,s as u,t as d,u as f}from"./stdio-kDI4sBvK.mjs";import{d as p,f as m,p as h,u as g}from"./node-Bv2snZci.mjs";import{z as _}from"zod";import{existsSync as v,readFileSync as y}from"node:fs";import*as b from"node:fs/promises";import{dirname as x,join as ee}from"node:path";import{fileURLToPath as te}from"node:url";import{DEFAULT_PORT_RANGE as S,DEFAULT_REGISTRY_PATH as ne,PortRegistryService as re,normalizeRepositoryPath as ie}from"@agimon-ai/foundation-port-registry";import{createProcessLease as C,resolveSiblingRegistryPath as w}from"@agimon-ai/foundation-process-registry";import{Command as T,InvalidArgumentError as E}from"commander";import{serve as ae}from"@hono/node-server";import{createHash as oe}from"node:crypto";import{Hono as D}from"hono";import{cors as se}from"hono/cors";import{rateLimiter as ce}from"hono-rate-limiter";import{raw as le}from"hono/html";import{jsx as O,jsxs as k}from"hono/jsx/jsx-runtime";function A(e){return e.option(`--config <path>`,`Path to .logsink.yaml`).option(`--global`,`Use the global log-sink instance`).option(`--instance <scope>`,`Force log-sink instance: local or global`)}function j(e){let t=e.instance?.trim();if(e.global&&t&&t.toLowerCase()!==`global`)throw Error(`Use either --global or --instance local, not both`);return e.global?`global`:t}function M(e,t=p){e.config&&(process.env.LOG_SINK_CONFIG=e.config);let n=j(e);return n&&(process.env.LOG_SINK_INSTANCE=n),m({configPath:e.config,packageName:t})}const ue=[`error`,`fatal`];function de(e){let t=e.trim();if(!t)return null;try{return JSON.parse(t)}catch{return null}}function fe(e){if(e==null)return`unknown`;if(typeof e==`string`)return e;try{return JSON.stringify(e,null,2)}catch{return String(e)}}function pe(e){return e.hook_event_name===`PostToolUseFailure`?`Current tool failure:\n- Tool: ${e.tool_name??`unknown tool`}\n- Error: ${fe(e.error)}`:null}function me(e,t,n){let r=new Map;for(let t of e){let e=`${t.service}::${t.level}::${t.error_type??``}::${t.error_message??t.message}`,i=new Date(Number(t.created_at)).toISOString();r.has(e)||r.set(e,{key:e,count:0,service:t.service,level:t.level,errorType:t.error_type,errorMessage:t.error_message,message:t.message,firstSeen:i,lastSeen:i,traceIds:new Set,examples:[]});let a=r.get(e);a.count+=1,a.lastSeen=i,t.trace_id&&a.traceIds.add(t.trace_id),a.examples.length<n&&a.examples.push({id:t.id,timestamp:i,traceId:t.trace_id})}return Array.from(r.values()).sort((e,t)=>t.count-e.count||t.lastSeen.localeCompare(e.lastSeen)).slice(0,t)}function he(e,t,n,r){let i=me(e,n,r),a=[];a.push(`Recent log errors (${e.length} new):`),t.tool_name&&a.push(`- Triggered after tool: ${t.tool_name}`);for(let[e,t]of i.entries()){a.push(`${e+1}. ${t.service} [${t.level}] x${t.count} ${t.errorType?`(${t.errorType}) `:``}${t.errorMessage??t.message}`),a.push(` First seen: ${t.firstSeen}`),a.push(` Last seen: ${t.lastSeen}`),t.traceIds.size>0&&a.push(` Trace IDs: ${Array.from(t.traceIds).slice(0,3).join(`, `)}`);for(let e of t.examples)a.push(` Example: ${e.timestamp} id=${e.id}${e.traceId?` trace=${e.traceId}`:``}`)}if(t.hook_event_name===`PostToolUseFailure`){let e=pe(t);e&&a.push(``,e)}return a.join(`
|
|
3
3
|
`)}function ge(e){if(e.session_id?.trim())return ie(h(e.cwd?.trim()||process.cwd()))}async function _e(e,t){let n=j(t);if(t.dbPath){let e=n===`global`?`global`:`local`;return v(t.dbPath)?{dbPath:t.dbPath,scope:e}:void 0}let r={...process.env};t.config&&(r.LOG_SINK_CONFIG=t.config),n&&(r.LOG_SINK_INSTANCE=n);let i=m({configPath:t.config,cwd:e.cwd??process.cwd(),env:r,packageName:p}),a=new re(r.PORT_REGISTRY_PATH??ne);try{let e=await a.getPort({repositoryPath:i.repositoryPath,serviceName:`log-sink-mcp-http`,serviceType:`tool`,environment:r.NODE_ENV||`development`}),t=e.record?.metadata?.dbPath;if(e.success&&typeof t==`string`&&t&&v(t))return{dbPath:t,scope:i.scope}}catch{}return v(i.dbPath)?{dbPath:i.dbPath,scope:i.scope}:void 0}async function ve(e,t,n,r,i,a){let o=e.getSqliteClient(),s=Date.now()-n*60*1e3,c=``;if(t){let e=o.prepare(`
|
|
4
4
|
SELECT id, created_at
|
|
5
5
|
FROM logs
|
package/dist/extensions/pi.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e=require(`../piTelemetry-
|
|
1
|
+
var e=require(`../piTelemetry-BYKSOd4F.cjs`).t();module.exports=e;
|
|
2
2
|
//# sourceMappingURL=pi.cjs.map
|
package/dist/extensions/pi.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"../piTelemetry-
|
|
1
|
+
import{t as e}from"../piTelemetry-Dlhhs5sx.mjs";var t=e();export{t as default};
|
|
2
2
|
//# sourceMappingURL=pi.mjs.map
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./node-9pwprHry.cjs`),t=require(`./stdio-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./node-9pwprHry.cjs`),t=require(`./stdio-QFR4qcFs.cjs`),n=require(`./piTelemetry-BYKSOd4F.cjs`);let r=require(`@opentelemetry/api`),i=require(`@opentelemetry/api-logs`);exports.SpanKind=r.SpanKind,exports.SpanStatusCode=r.SpanStatusCode,exports.StdioTransportHandler=t.t,exports.createNodeTelemetry=e.t,exports.createPiTelemetryExtension=n.t,exports.createServer=t.n,exports.getLogSinkOtelEndpoint=e.n,exports.log=i.logs,exports.resolveLogSinkOtelEndpoint=e.a,exports.resolveLogSinkPort=e.o,exports.resolveNodeTelemetryEndpoints=e.r,exports.trace=r.trace;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,t}from"./stdio-
|
|
1
|
+
import{n as e,t}from"./stdio-kDI4sBvK.mjs";import{a as n,c as r,i,l as a,n as o,o as s,r as c,t as l,u}from"./node-Bv2snZci.mjs";import{t as d}from"./piTelemetry-Dlhhs5sx.mjs";export{l as SpanKind,o as SpanStatusCode,t as StdioTransportHandler,c as createNodeTelemetry,d as createPiTelemetryExtension,e as createServer,i as getLogSinkOtelEndpoint,n as log,a as resolveLogSinkOtelEndpoint,u as resolveLogSinkPort,s as resolveNodeTelemetryEndpoints,r as trace};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require(`./node-9pwprHry.cjs`);function t(e){return e===`1`||e?.toLowerCase()===`true`||e?.toLowerCase()===`yes`}function n(e){try{return JSON.stringify(e)}catch{return`[unserializable]`}}function r(e,t){console.warn(`[log-sink-mcp pi telemetry] ${e} failed: ${t instanceof Error?t.message:String(t)}`)}function i(e){let t={"pi.cwd":e.cwd,"pi.mode":e.mode,"pi.session.id":e.sessionManager.getSessionId()};e.thinkingLevel&&(t[`pi.thinking_level`]=e.thinkingLevel);let n=e.sessionManager.getSessionFile();return n&&(t[`pi.session.file`]=n),e.model&&(t[`gen_ai.provider.name`]=e.model.provider,t[`gen_ai.request.model`]=e.model.id),t}function a(e){if(e.role!==`assistant`)return;let t={"gen_ai.response.model":e.responseModel??e.model,"gen_ai.response.finish_reasons":e.stopReason,"gen_ai.usage.input_tokens":e.usage.input,"gen_ai.usage.output_tokens":e.usage.output,"gen_ai.usage.cache_read_tokens":e.usage.cacheRead,"gen_ai.usage.cache_write_tokens":e.usage.cacheWrite,"gen_ai.usage.total_tokens":e.usage.totalTokens,"gen_ai.usage.cost":e.usage.cost.total};return e.usage.reasoning!==void 0&&(t[`gen_ai.usage.reasoning_tokens`]=e.usage.reasoning),t}function o(e){let t={"x-agent":`pi`},n=e.AGENT_SESSION_ID??e.PI_SESSION_ID;return n&&(t[`x-agent-session-id`]=n),e.AGENT_PARENT_SESSION_ID&&(t[`x-agent-parent-session-id`]=e.AGENT_PARENT_SESSION_ID),t}function s(s={}){return function(c){let l=s.env??process.env,u=s.telemetryFactory??e.t,d=l.AGENT_OTEL_REDACT===`0`,f=new Map,p,m,h=e=>l.AGENT_TELEMETRY_DISABLED===`1`?Promise.resolve(void 0):(m??=u({serviceName:`pi`,packageName:`@agimon-ai/log-sink-mcp`,cwd:e.cwd,env:l,discoverEndpoint:!0,enableLogs:!0,enableTraces:t(l.AGENT_OTEL_TRACES),headers:o(l)}).then(async e=>{let n=s.allowFileFallback||t(l.LOG_SINK_PI_FILE_FALLBACK);if(e.backend!==`file`||n)return e;await e.shutdown().catch(e=>r(`File fallback shutdown`,e))}).catch(e=>{r(`Initialization`,e)}),m);c.on(`session_start`,async(e,t)=>{(await h(t))?.logger.info(`pi.session.started`,{attributes:{...i(t),"pi.session.reason":e.reason}})}),c.on(`before_agent_start`,async(e,t)=>{(await h(t))?.logger.info(`pi.user_prompt`,{attributes:{...i(t),"pi.user_message.length":e.prompt.length,"pi.user_message.image_count":e.images?.length??0,...d?{"user.prompt":e.prompt}:{}}})}),c.on(`agent_start`,async(e,t)=>{p=Date.now(),(await h(t))?.logger.info(`pi.agent.started`,{attributes:i(t)})}),c.on(`turn_start`,async(e,t)=>{(await h(t))?.logger.debug(`pi.turn.started`,{attributes:{...i(t),"pi.turn.index":e.turnIndex}})}),c.on(`turn_end`,async(e,t)=>{let n=await h(t),r=a(e.message);if(n?.logger.info(`pi.turn.finished`,{attributes:{...i(t),"pi.turn.index":e.turnIndex,"pi.tool_result.count":e.toolResults.length,...r}}),r)for(let a of e.toolResults)n?.logger.debug(`pi.tool_token_sample`,{attributes:{...i(t),...r,"tool.name":a.toolName,token_attribution:`toolCallTurn`}})}),c.on(`tool_execution_start`,async(e,t)=>{f.set(e.toolCallId,Date.now()),(await h(t))?.logger.debug(`pi.tool_call`,{attributes:{...i(t),"tool.name":e.toolName,"tool.call.id":e.toolCallId,...d?{"tool.input":n(e.args)}:{}}})}),c.on(`tool_execution_end`,async(e,t)=>{let r=f.get(e.toolCallId);f.delete(e.toolCallId),(await h(t))?.logger.info(`pi.tool_result`,{attributes:{...i(t),success:!e.isError,tool_name:e.toolName,"tool.name":e.toolName,"tool.call.id":e.toolCallId,"tool.result.error":e.isError,...r===void 0?{}:{"tool.duration_ms":Date.now()-r},...d?{"tool.result":n(e.result)}:{}}})}),c.on(`after_provider_response`,async(e,t)=>{let n=await h(t),r={attributes:{...i(t),status_code:e.status,"http.response.status_code":e.status}};e.status>=400?n?.logger.error(`pi.api_error`,r):n?.logger.debug(`pi.api_response`,r)}),c.on(`model_select`,async(e,t)=>{(await h(t))?.logger.info(`pi.model.selected`,{attributes:{...i(t),"pi.model.source":e.source,"gen_ai.provider.name":e.model.provider,"gen_ai.request.model":e.model.id}})}),c.on(`agent_end`,async(e,t)=>{let n=await h(t);n?.logger.info(`pi.agent.finished`,{attributes:{...i(t),"pi.message.count":e.messages.length,...p===void 0?{}:{"pi.agent.duration_ms":Date.now()-p}}}),p=void 0,await n?.flush().catch(e=>r(`Flush`,e))}),c.on(`agent_settled`,async(e,t)=>{(await h(t))?.logger.debug(`pi.agent.settled`,{attributes:i(t)})}),c.on(`session_shutdown`,async(e,t)=>{let n=await h(t);n?.logger.info(`pi.session.finished`,{attributes:{...i(t),"pi.session.reason":e.reason}}),await n?.shutdown().catch(e=>r(`Shutdown`,e))})}}Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return s}});
|
|
2
|
+
//# sourceMappingURL=piTelemetry-BYKSOd4F.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piTelemetry-BYKSOd4F.cjs","names":["createNodeTelemetry"],"sources":["../src/extensions/piTelemetry.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext, TurnEndEvent } from '@earendil-works/pi-coding-agent';\nimport { createNodeTelemetry, type NodeTelemetryHandle, type NodeTelemetryOptions } from '../telemetry/node.js';\n\nconst SERVICE_NAME = 'pi';\nconst PACKAGE_NAME = '@agimon-ai/log-sink-mcp';\n\ntype TelemetryFactory = (options: NodeTelemetryOptions) => Promise<NodeTelemetryHandle>;\n\nexport interface PiTelemetryExtensionOptions {\n allowFileFallback?: boolean;\n env?: NodeJS.ProcessEnv;\n telemetryFactory?: TelemetryFactory;\n}\n\nfunction isEnabled(value: string | undefined): boolean {\n return value === '1' || value?.toLowerCase() === 'true' || value?.toLowerCase() === 'yes';\n}\n\nfunction serialize(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n}\n\nfunction reportFailure(operation: string, error: unknown): void {\n console.warn(\n `[log-sink-mcp pi telemetry] ${operation} failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n}\n\nfunction contextAttributes(ctx: ExtensionContext): Record<string, string | number | boolean> {\n const attributes: Record<string, string | number | boolean> = {\n 'pi.cwd': ctx.cwd,\n 'pi.mode': ctx.mode,\n 'pi.session.id': ctx.sessionManager.getSessionId(),\n };\n\n if (ctx.thinkingLevel) attributes['pi.thinking_level'] = ctx.thinkingLevel;\n const sessionFile = ctx.sessionManager.getSessionFile();\n if (sessionFile) attributes['pi.session.file'] = sessionFile;\n if (ctx.model) {\n attributes['gen_ai.provider.name'] = ctx.model.provider;\n attributes['gen_ai.request.model'] = ctx.model.id;\n }\n\n return attributes;\n}\n\n/**\n * Token accounting for one turn.\n *\n * Returns nothing when the turn carries no usage, which is what lets a caller\n * skip emitting a record the metrics aggregator would only discard.\n */\nfunction usageAttributes(message: TurnEndEvent['message']): Record<string, string | number> | undefined {\n if (message.role !== 'assistant') return undefined;\n\n const attributes: Record<string, string | number> = {\n 'gen_ai.response.model': message.responseModel ?? message.model,\n 'gen_ai.response.finish_reasons': message.stopReason,\n 'gen_ai.usage.input_tokens': message.usage.input,\n 'gen_ai.usage.output_tokens': message.usage.output,\n 'gen_ai.usage.cache_read_tokens': message.usage.cacheRead,\n 'gen_ai.usage.cache_write_tokens': message.usage.cacheWrite,\n 'gen_ai.usage.total_tokens': message.usage.totalTokens,\n 'gen_ai.usage.cost': message.usage.cost.total,\n };\n if (message.usage.reasoning !== undefined) attributes['gen_ai.usage.reasoning_tokens'] = message.usage.reasoning;\n\n return attributes;\n}\n\nfunction telemetryHeaders(env: NodeJS.ProcessEnv): Record<string, string> {\n const headers: Record<string, string> = { 'x-agent': SERVICE_NAME };\n const sessionId = env.AGENT_SESSION_ID ?? env.PI_SESSION_ID;\n if (sessionId) headers['x-agent-session-id'] = sessionId;\n if (env.AGENT_PARENT_SESSION_ID) headers['x-agent-parent-session-id'] = env.AGENT_PARENT_SESSION_ID;\n return headers;\n}\n\nexport function createPiTelemetryExtension(options: PiTelemetryExtensionOptions = {}) {\n return function registerPiTelemetry(pi: ExtensionAPI): void {\n const env = options.env ?? process.env;\n const telemetryFactory = options.telemetryFactory ?? createNodeTelemetry;\n const includeSensitiveDetails = env.AGENT_OTEL_REDACT === '0';\n const toolStartedAt = new Map<string, number>();\n let agentStartedAt: number | undefined;\n let telemetryPromise: Promise<NodeTelemetryHandle | undefined> | undefined;\n\n const getTelemetry = (ctx: ExtensionContext): Promise<NodeTelemetryHandle | undefined> => {\n if (env.AGENT_TELEMETRY_DISABLED === '1') return Promise.resolve(undefined);\n\n telemetryPromise ??= telemetryFactory({\n serviceName: SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: ctx.cwd,\n env,\n discoverEndpoint: true,\n enableLogs: true,\n enableTraces: isEnabled(env.AGENT_OTEL_TRACES),\n headers: telemetryHeaders(env),\n })\n .then(async (telemetry) => {\n const allowFileFallback = options.allowFileFallback || isEnabled(env.LOG_SINK_PI_FILE_FALLBACK);\n if (telemetry.backend !== 'file' || allowFileFallback) return telemetry;\n\n await telemetry.shutdown().catch((error: unknown) => reportFailure('File fallback shutdown', error));\n return undefined;\n })\n .catch((error: unknown) => {\n reportFailure('Initialization', error);\n return undefined;\n });\n return telemetryPromise;\n };\n\n pi.on('session_start', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.session.started', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n },\n });\n });\n\n pi.on('before_agent_start', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.user_prompt', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.user_message.length': event.prompt.length,\n 'pi.user_message.image_count': event.images?.length ?? 0,\n ...(includeSensitiveDetails ? { 'user.prompt': event.prompt } : {}),\n },\n });\n });\n\n pi.on('agent_start', async (_event, ctx) => {\n agentStartedAt = Date.now();\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.agent.started', {\n attributes: contextAttributes(ctx),\n });\n });\n\n pi.on('turn_start', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.debug('pi.turn.started', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n },\n });\n });\n\n pi.on('turn_end', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n const usage = usageAttributes(event.message);\n telemetry?.logger.info('pi.turn.finished', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n 'pi.tool_result.count': event.toolResults.length,\n ...usage,\n },\n });\n\n // One sample per tool call, carrying the usage of the turn that issued\n // it, so the percentiles answer \"how large is the context when this tool\n // runs\". A turn calling several tools emits a sample each: the metric is\n // a distribution per tool, not a sum, so the repetition is the point.\n if (!usage) return;\n for (const result of event.toolResults) {\n telemetry?.logger.debug('pi.tool_token_sample', {\n attributes: {\n ...contextAttributes(ctx),\n ...usage,\n 'tool.name': result.toolName,\n token_attribution: 'toolCallTurn',\n },\n });\n }\n });\n\n pi.on('tool_execution_start', async (event, ctx) => {\n toolStartedAt.set(event.toolCallId, Date.now());\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.debug('pi.tool_call', {\n attributes: {\n ...contextAttributes(ctx),\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n ...(includeSensitiveDetails ? { 'tool.input': serialize(event.args) } : {}),\n },\n });\n });\n\n pi.on('tool_execution_end', async (event, ctx) => {\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.tool_result', {\n attributes: {\n ...contextAttributes(ctx),\n success: !event.isError,\n tool_name: event.toolName,\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n 'tool.result.error': event.isError,\n ...(startedAt === undefined ? {} : { 'tool.duration_ms': Date.now() - startedAt }),\n ...(includeSensitiveDetails ? { 'tool.result': serialize(event.result) } : {}),\n },\n });\n });\n\n pi.on('after_provider_response', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n const options = {\n attributes: {\n ...contextAttributes(ctx),\n status_code: event.status,\n 'http.response.status_code': event.status,\n },\n };\n if (event.status >= 400) telemetry?.logger.error('pi.api_error', options);\n else telemetry?.logger.debug('pi.api_response', options);\n });\n\n pi.on('model_select', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.model.selected', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.model.source': event.source,\n 'gen_ai.provider.name': event.model.provider,\n 'gen_ai.request.model': event.model.id,\n },\n });\n });\n\n pi.on('agent_end', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.agent.finished', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.message.count': event.messages.length,\n ...(agentStartedAt === undefined ? {} : { 'pi.agent.duration_ms': Date.now() - agentStartedAt }),\n },\n });\n agentStartedAt = undefined;\n await telemetry?.flush().catch((error: unknown) => reportFailure('Flush', error));\n });\n\n pi.on('agent_settled', async (_event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.debug('pi.agent.settled', {\n attributes: contextAttributes(ctx),\n });\n });\n\n pi.on('session_shutdown', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.session.finished', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n },\n });\n await telemetry?.shutdown().catch((error: unknown) => reportFailure('Shutdown', error));\n });\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as e}from"./node-Bv2snZci.mjs";function t(e){return e===`1`||e?.toLowerCase()===`true`||e?.toLowerCase()===`yes`}function n(e){try{return JSON.stringify(e)}catch{return`[unserializable]`}}function r(e,t){console.warn(`[log-sink-mcp pi telemetry] ${e} failed: ${t instanceof Error?t.message:String(t)}`)}function i(e){let t={"pi.cwd":e.cwd,"pi.mode":e.mode,"pi.session.id":e.sessionManager.getSessionId()};e.thinkingLevel&&(t[`pi.thinking_level`]=e.thinkingLevel);let n=e.sessionManager.getSessionFile();return n&&(t[`pi.session.file`]=n),e.model&&(t[`gen_ai.provider.name`]=e.model.provider,t[`gen_ai.request.model`]=e.model.id),t}function a(e){if(e.role!==`assistant`)return;let t={"gen_ai.response.model":e.responseModel??e.model,"gen_ai.response.finish_reasons":e.stopReason,"gen_ai.usage.input_tokens":e.usage.input,"gen_ai.usage.output_tokens":e.usage.output,"gen_ai.usage.cache_read_tokens":e.usage.cacheRead,"gen_ai.usage.cache_write_tokens":e.usage.cacheWrite,"gen_ai.usage.total_tokens":e.usage.totalTokens,"gen_ai.usage.cost":e.usage.cost.total};return e.usage.reasoning!==void 0&&(t[`gen_ai.usage.reasoning_tokens`]=e.usage.reasoning),t}function o(e){let t={"x-agent":`pi`},n=e.AGENT_SESSION_ID??e.PI_SESSION_ID;return n&&(t[`x-agent-session-id`]=n),e.AGENT_PARENT_SESSION_ID&&(t[`x-agent-parent-session-id`]=e.AGENT_PARENT_SESSION_ID),t}function s(s={}){return function(c){let l=s.env??process.env,u=s.telemetryFactory??e,d=l.AGENT_OTEL_REDACT===`0`,f=new Map,p,m,h=e=>l.AGENT_TELEMETRY_DISABLED===`1`?Promise.resolve(void 0):(m??=u({serviceName:`pi`,packageName:`@agimon-ai/log-sink-mcp`,cwd:e.cwd,env:l,discoverEndpoint:!0,enableLogs:!0,enableTraces:t(l.AGENT_OTEL_TRACES),headers:o(l)}).then(async e=>{let n=s.allowFileFallback||t(l.LOG_SINK_PI_FILE_FALLBACK);if(e.backend!==`file`||n)return e;await e.shutdown().catch(e=>r(`File fallback shutdown`,e))}).catch(e=>{r(`Initialization`,e)}),m);c.on(`session_start`,async(e,t)=>{(await h(t))?.logger.info(`pi.session.started`,{attributes:{...i(t),"pi.session.reason":e.reason}})}),c.on(`before_agent_start`,async(e,t)=>{(await h(t))?.logger.info(`pi.user_prompt`,{attributes:{...i(t),"pi.user_message.length":e.prompt.length,"pi.user_message.image_count":e.images?.length??0,...d?{"user.prompt":e.prompt}:{}}})}),c.on(`agent_start`,async(e,t)=>{p=Date.now(),(await h(t))?.logger.info(`pi.agent.started`,{attributes:i(t)})}),c.on(`turn_start`,async(e,t)=>{(await h(t))?.logger.debug(`pi.turn.started`,{attributes:{...i(t),"pi.turn.index":e.turnIndex}})}),c.on(`turn_end`,async(e,t)=>{let n=await h(t),r=a(e.message);if(n?.logger.info(`pi.turn.finished`,{attributes:{...i(t),"pi.turn.index":e.turnIndex,"pi.tool_result.count":e.toolResults.length,...r}}),r)for(let a of e.toolResults)n?.logger.debug(`pi.tool_token_sample`,{attributes:{...i(t),...r,"tool.name":a.toolName,token_attribution:`toolCallTurn`}})}),c.on(`tool_execution_start`,async(e,t)=>{f.set(e.toolCallId,Date.now()),(await h(t))?.logger.debug(`pi.tool_call`,{attributes:{...i(t),"tool.name":e.toolName,"tool.call.id":e.toolCallId,...d?{"tool.input":n(e.args)}:{}}})}),c.on(`tool_execution_end`,async(e,t)=>{let r=f.get(e.toolCallId);f.delete(e.toolCallId),(await h(t))?.logger.info(`pi.tool_result`,{attributes:{...i(t),success:!e.isError,tool_name:e.toolName,"tool.name":e.toolName,"tool.call.id":e.toolCallId,"tool.result.error":e.isError,...r===void 0?{}:{"tool.duration_ms":Date.now()-r},...d?{"tool.result":n(e.result)}:{}}})}),c.on(`after_provider_response`,async(e,t)=>{let n=await h(t),r={attributes:{...i(t),status_code:e.status,"http.response.status_code":e.status}};e.status>=400?n?.logger.error(`pi.api_error`,r):n?.logger.debug(`pi.api_response`,r)}),c.on(`model_select`,async(e,t)=>{(await h(t))?.logger.info(`pi.model.selected`,{attributes:{...i(t),"pi.model.source":e.source,"gen_ai.provider.name":e.model.provider,"gen_ai.request.model":e.model.id}})}),c.on(`agent_end`,async(e,t)=>{let n=await h(t);n?.logger.info(`pi.agent.finished`,{attributes:{...i(t),"pi.message.count":e.messages.length,...p===void 0?{}:{"pi.agent.duration_ms":Date.now()-p}}}),p=void 0,await n?.flush().catch(e=>r(`Flush`,e))}),c.on(`agent_settled`,async(e,t)=>{(await h(t))?.logger.debug(`pi.agent.settled`,{attributes:i(t)})}),c.on(`session_shutdown`,async(e,t)=>{let n=await h(t);n?.logger.info(`pi.session.finished`,{attributes:{...i(t),"pi.session.reason":e.reason}}),await n?.shutdown().catch(e=>r(`Shutdown`,e))})}}export{s as t};
|
|
2
|
+
//# sourceMappingURL=piTelemetry-Dlhhs5sx.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piTelemetry-Dlhhs5sx.mjs","names":[],"sources":["../src/extensions/piTelemetry.ts"],"sourcesContent":["import type { ExtensionAPI, ExtensionContext, TurnEndEvent } from '@earendil-works/pi-coding-agent';\nimport { createNodeTelemetry, type NodeTelemetryHandle, type NodeTelemetryOptions } from '../telemetry/node.js';\n\nconst SERVICE_NAME = 'pi';\nconst PACKAGE_NAME = '@agimon-ai/log-sink-mcp';\n\ntype TelemetryFactory = (options: NodeTelemetryOptions) => Promise<NodeTelemetryHandle>;\n\nexport interface PiTelemetryExtensionOptions {\n allowFileFallback?: boolean;\n env?: NodeJS.ProcessEnv;\n telemetryFactory?: TelemetryFactory;\n}\n\nfunction isEnabled(value: string | undefined): boolean {\n return value === '1' || value?.toLowerCase() === 'true' || value?.toLowerCase() === 'yes';\n}\n\nfunction serialize(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return '[unserializable]';\n }\n}\n\nfunction reportFailure(operation: string, error: unknown): void {\n console.warn(\n `[log-sink-mcp pi telemetry] ${operation} failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n}\n\nfunction contextAttributes(ctx: ExtensionContext): Record<string, string | number | boolean> {\n const attributes: Record<string, string | number | boolean> = {\n 'pi.cwd': ctx.cwd,\n 'pi.mode': ctx.mode,\n 'pi.session.id': ctx.sessionManager.getSessionId(),\n };\n\n if (ctx.thinkingLevel) attributes['pi.thinking_level'] = ctx.thinkingLevel;\n const sessionFile = ctx.sessionManager.getSessionFile();\n if (sessionFile) attributes['pi.session.file'] = sessionFile;\n if (ctx.model) {\n attributes['gen_ai.provider.name'] = ctx.model.provider;\n attributes['gen_ai.request.model'] = ctx.model.id;\n }\n\n return attributes;\n}\n\n/**\n * Token accounting for one turn.\n *\n * Returns nothing when the turn carries no usage, which is what lets a caller\n * skip emitting a record the metrics aggregator would only discard.\n */\nfunction usageAttributes(message: TurnEndEvent['message']): Record<string, string | number> | undefined {\n if (message.role !== 'assistant') return undefined;\n\n const attributes: Record<string, string | number> = {\n 'gen_ai.response.model': message.responseModel ?? message.model,\n 'gen_ai.response.finish_reasons': message.stopReason,\n 'gen_ai.usage.input_tokens': message.usage.input,\n 'gen_ai.usage.output_tokens': message.usage.output,\n 'gen_ai.usage.cache_read_tokens': message.usage.cacheRead,\n 'gen_ai.usage.cache_write_tokens': message.usage.cacheWrite,\n 'gen_ai.usage.total_tokens': message.usage.totalTokens,\n 'gen_ai.usage.cost': message.usage.cost.total,\n };\n if (message.usage.reasoning !== undefined) attributes['gen_ai.usage.reasoning_tokens'] = message.usage.reasoning;\n\n return attributes;\n}\n\nfunction telemetryHeaders(env: NodeJS.ProcessEnv): Record<string, string> {\n const headers: Record<string, string> = { 'x-agent': SERVICE_NAME };\n const sessionId = env.AGENT_SESSION_ID ?? env.PI_SESSION_ID;\n if (sessionId) headers['x-agent-session-id'] = sessionId;\n if (env.AGENT_PARENT_SESSION_ID) headers['x-agent-parent-session-id'] = env.AGENT_PARENT_SESSION_ID;\n return headers;\n}\n\nexport function createPiTelemetryExtension(options: PiTelemetryExtensionOptions = {}) {\n return function registerPiTelemetry(pi: ExtensionAPI): void {\n const env = options.env ?? process.env;\n const telemetryFactory = options.telemetryFactory ?? createNodeTelemetry;\n const includeSensitiveDetails = env.AGENT_OTEL_REDACT === '0';\n const toolStartedAt = new Map<string, number>();\n let agentStartedAt: number | undefined;\n let telemetryPromise: Promise<NodeTelemetryHandle | undefined> | undefined;\n\n const getTelemetry = (ctx: ExtensionContext): Promise<NodeTelemetryHandle | undefined> => {\n if (env.AGENT_TELEMETRY_DISABLED === '1') return Promise.resolve(undefined);\n\n telemetryPromise ??= telemetryFactory({\n serviceName: SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: ctx.cwd,\n env,\n discoverEndpoint: true,\n enableLogs: true,\n enableTraces: isEnabled(env.AGENT_OTEL_TRACES),\n headers: telemetryHeaders(env),\n })\n .then(async (telemetry) => {\n const allowFileFallback = options.allowFileFallback || isEnabled(env.LOG_SINK_PI_FILE_FALLBACK);\n if (telemetry.backend !== 'file' || allowFileFallback) return telemetry;\n\n await telemetry.shutdown().catch((error: unknown) => reportFailure('File fallback shutdown', error));\n return undefined;\n })\n .catch((error: unknown) => {\n reportFailure('Initialization', error);\n return undefined;\n });\n return telemetryPromise;\n };\n\n pi.on('session_start', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.session.started', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n },\n });\n });\n\n pi.on('before_agent_start', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.user_prompt', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.user_message.length': event.prompt.length,\n 'pi.user_message.image_count': event.images?.length ?? 0,\n ...(includeSensitiveDetails ? { 'user.prompt': event.prompt } : {}),\n },\n });\n });\n\n pi.on('agent_start', async (_event, ctx) => {\n agentStartedAt = Date.now();\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.agent.started', {\n attributes: contextAttributes(ctx),\n });\n });\n\n pi.on('turn_start', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.debug('pi.turn.started', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n },\n });\n });\n\n pi.on('turn_end', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n const usage = usageAttributes(event.message);\n telemetry?.logger.info('pi.turn.finished', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n 'pi.tool_result.count': event.toolResults.length,\n ...usage,\n },\n });\n\n // One sample per tool call, carrying the usage of the turn that issued\n // it, so the percentiles answer \"how large is the context when this tool\n // runs\". A turn calling several tools emits a sample each: the metric is\n // a distribution per tool, not a sum, so the repetition is the point.\n if (!usage) return;\n for (const result of event.toolResults) {\n telemetry?.logger.debug('pi.tool_token_sample', {\n attributes: {\n ...contextAttributes(ctx),\n ...usage,\n 'tool.name': result.toolName,\n token_attribution: 'toolCallTurn',\n },\n });\n }\n });\n\n pi.on('tool_execution_start', async (event, ctx) => {\n toolStartedAt.set(event.toolCallId, Date.now());\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.debug('pi.tool_call', {\n attributes: {\n ...contextAttributes(ctx),\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n ...(includeSensitiveDetails ? { 'tool.input': serialize(event.args) } : {}),\n },\n });\n });\n\n pi.on('tool_execution_end', async (event, ctx) => {\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.tool_result', {\n attributes: {\n ...contextAttributes(ctx),\n success: !event.isError,\n tool_name: event.toolName,\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n 'tool.result.error': event.isError,\n ...(startedAt === undefined ? {} : { 'tool.duration_ms': Date.now() - startedAt }),\n ...(includeSensitiveDetails ? { 'tool.result': serialize(event.result) } : {}),\n },\n });\n });\n\n pi.on('after_provider_response', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n const options = {\n attributes: {\n ...contextAttributes(ctx),\n status_code: event.status,\n 'http.response.status_code': event.status,\n },\n };\n if (event.status >= 400) telemetry?.logger.error('pi.api_error', options);\n else telemetry?.logger.debug('pi.api_response', options);\n });\n\n pi.on('model_select', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.model.selected', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.model.source': event.source,\n 'gen_ai.provider.name': event.model.provider,\n 'gen_ai.request.model': event.model.id,\n },\n });\n });\n\n pi.on('agent_end', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.agent.finished', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.message.count': event.messages.length,\n ...(agentStartedAt === undefined ? {} : { 'pi.agent.duration_ms': Date.now() - agentStartedAt }),\n },\n });\n agentStartedAt = undefined;\n await telemetry?.flush().catch((error: unknown) => reportFailure('Flush', error));\n });\n\n pi.on('agent_settled', async (_event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.debug('pi.agent.settled', {\n attributes: contextAttributes(ctx),\n });\n });\n\n pi.on('session_shutdown', async (event, ctx) => {\n const telemetry = await getTelemetry(ctx);\n telemetry?.logger.info('pi.session.finished', {\n attributes: {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n },\n });\n await telemetry?.shutdown().catch((error: unknown) => reportFailure('Shutdown', error));\n });\n };\n}\n"],"mappings":"wCAcA,SAAS,EAAU,EAAoC,CACrD,OAAO,IAAU,KAAO,GAAO,aAAa,GAAK,QAAU,GAAO,aAAa,GAAK,MAGtF,SAAS,EAAU,EAAwB,CACzC,GAAI,CACF,OAAO,KAAK,UAAU,EAAM,MACtB,CACN,MAAO,oBAIX,SAAS,EAAc,EAAmB,EAAsB,CAC9D,QAAQ,KACN,+BAA+B,EAAU,WAAW,aAAiB,MAAQ,EAAM,QAAU,OAAO,EAAM,GAC3G,CAGH,SAAS,EAAkB,EAAkE,CAC3F,IAAM,EAAwD,CAC5D,SAAU,EAAI,IACd,UAAW,EAAI,KACf,gBAAiB,EAAI,eAAe,cAAc,CACnD,CAEG,EAAI,gBAAe,EAAW,qBAAuB,EAAI,eAC7D,IAAM,EAAc,EAAI,eAAe,gBAAgB,CAOvD,OANI,IAAa,EAAW,mBAAqB,GAC7C,EAAI,QACN,EAAW,wBAA0B,EAAI,MAAM,SAC/C,EAAW,wBAA0B,EAAI,MAAM,IAG1C,EAST,SAAS,EAAgB,EAA+E,CACtG,GAAI,EAAQ,OAAS,YAAa,OAElC,IAAM,EAA8C,CAClD,wBAAyB,EAAQ,eAAiB,EAAQ,MAC1D,iCAAkC,EAAQ,WAC1C,4BAA6B,EAAQ,MAAM,MAC3C,6BAA8B,EAAQ,MAAM,OAC5C,iCAAkC,EAAQ,MAAM,UAChD,kCAAmC,EAAQ,MAAM,WACjD,4BAA6B,EAAQ,MAAM,YAC3C,oBAAqB,EAAQ,MAAM,KAAK,MACzC,CAGD,OAFI,EAAQ,MAAM,YAAc,IAAA,KAAW,EAAW,iCAAmC,EAAQ,MAAM,WAEhG,EAGT,SAAS,EAAiB,EAAgD,CACxE,IAAM,EAAkC,CAAE,UAAW,KAAc,CAC7D,EAAY,EAAI,kBAAoB,EAAI,cAG9C,OAFI,IAAW,EAAQ,sBAAwB,GAC3C,EAAI,0BAAyB,EAAQ,6BAA+B,EAAI,yBACrE,EAGT,SAAgB,EAA2B,EAAuC,EAAE,CAAE,CACpF,OAAO,SAA6B,EAAwB,CAC1D,IAAM,EAAM,EAAQ,KAAO,QAAQ,IAC7B,EAAmB,EAAQ,kBAAoB,EAC/C,EAA0B,EAAI,oBAAsB,IACpD,EAAgB,IAAI,IACtB,EACA,EAEE,EAAgB,GAChB,EAAI,2BAA6B,IAAY,QAAQ,QAAQ,IAAA,GAAU,EAE3E,IAAqB,EAAiB,CACpC,YAAa,KACb,YAAa,0BACb,IAAK,EAAI,IACT,MACA,iBAAkB,GAClB,WAAY,GACZ,aAAc,EAAU,EAAI,kBAAkB,CAC9C,QAAS,EAAiB,EAAI,CAC/B,CAAC,CACC,KAAK,KAAO,IAAc,CACzB,IAAM,EAAoB,EAAQ,mBAAqB,EAAU,EAAI,0BAA0B,CAC/F,GAAI,EAAU,UAAY,QAAU,EAAmB,OAAO,EAE9D,MAAM,EAAU,UAAU,CAAC,MAAO,GAAmB,EAAc,yBAA0B,EAAM,CAAC,EAEpG,CACD,MAAO,GAAmB,CACzB,EAAc,iBAAkB,EAAM,EAEtC,CACG,GAGT,EAAG,GAAG,gBAAiB,MAAO,EAAO,IAAQ,EAE3C,MADwB,EAAa,EAAI,GAC9B,OAAO,KAAK,qBAAsB,CAC3C,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,oBAAqB,EAAM,OAC5B,CACF,CAAC,EACF,CAEF,EAAG,GAAG,qBAAsB,MAAO,EAAO,IAAQ,EAEhD,MADwB,EAAa,EAAI,GAC9B,OAAO,KAAK,iBAAkB,CACvC,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,yBAA0B,EAAM,OAAO,OACvC,8BAA+B,EAAM,QAAQ,QAAU,EACvD,GAAI,EAA0B,CAAE,cAAe,EAAM,OAAQ,CAAG,EAAE,CACnE,CACF,CAAC,EACF,CAEF,EAAG,GAAG,cAAe,MAAO,EAAQ,IAAQ,CAC1C,EAAiB,KAAK,KAAK,EAE3B,MADwB,EAAa,EAAI,GAC9B,OAAO,KAAK,mBAAoB,CACzC,WAAY,EAAkB,EAAI,CACnC,CAAC,EACF,CAEF,EAAG,GAAG,aAAc,MAAO,EAAO,IAAQ,EAExC,MADwB,EAAa,EAAI,GAC9B,OAAO,MAAM,kBAAmB,CACzC,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,gBAAiB,EAAM,UACxB,CACF,CAAC,EACF,CAEF,EAAG,GAAG,WAAY,MAAO,EAAO,IAAQ,CACtC,IAAM,EAAY,MAAM,EAAa,EAAI,CACnC,EAAQ,EAAgB,EAAM,QAAQ,CAC5C,MAAW,OAAO,KAAK,mBAAoB,CACzC,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,gBAAiB,EAAM,UACvB,uBAAwB,EAAM,YAAY,OAC1C,GAAG,EACJ,CACF,CAAC,CAMG,EACL,IAAK,IAAM,KAAU,EAAM,YACzB,GAAW,OAAO,MAAM,uBAAwB,CAC9C,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,GAAG,EACH,YAAa,EAAO,SACpB,kBAAmB,eACpB,CACF,CAAC,EAEJ,CAEF,EAAG,GAAG,uBAAwB,MAAO,EAAO,IAAQ,CAClD,EAAc,IAAI,EAAM,WAAY,KAAK,KAAK,CAAC,EAE/C,MADwB,EAAa,EAAI,GAC9B,OAAO,MAAM,eAAgB,CACtC,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,YAAa,EAAM,SACnB,eAAgB,EAAM,WACtB,GAAI,EAA0B,CAAE,aAAc,EAAU,EAAM,KAAK,CAAE,CAAG,EAAE,CAC3E,CACF,CAAC,EACF,CAEF,EAAG,GAAG,qBAAsB,MAAO,EAAO,IAAQ,CAChD,IAAM,EAAY,EAAc,IAAI,EAAM,WAAW,CACrD,EAAc,OAAO,EAAM,WAAW,EAEtC,MADwB,EAAa,EAAI,GAC9B,OAAO,KAAK,iBAAkB,CACvC,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,QAAS,CAAC,EAAM,QAChB,UAAW,EAAM,SACjB,YAAa,EAAM,SACnB,eAAgB,EAAM,WACtB,oBAAqB,EAAM,QAC3B,GAAI,IAAc,IAAA,GAAY,EAAE,CAAG,CAAE,mBAAoB,KAAK,KAAK,CAAG,EAAW,CACjF,GAAI,EAA0B,CAAE,cAAe,EAAU,EAAM,OAAO,CAAE,CAAG,EAAE,CAC9E,CACF,CAAC,EACF,CAEF,EAAG,GAAG,0BAA2B,MAAO,EAAO,IAAQ,CACrD,IAAM,EAAY,MAAM,EAAa,EAAI,CACnC,EAAU,CACd,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,YAAa,EAAM,OACnB,4BAA6B,EAAM,OACpC,CACF,CACG,EAAM,QAAU,IAAK,GAAW,OAAO,MAAM,eAAgB,EAAQ,CACpE,GAAW,OAAO,MAAM,kBAAmB,EAAQ,EACxD,CAEF,EAAG,GAAG,eAAgB,MAAO,EAAO,IAAQ,EAE1C,MADwB,EAAa,EAAI,GAC9B,OAAO,KAAK,oBAAqB,CAC1C,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,kBAAmB,EAAM,OACzB,uBAAwB,EAAM,MAAM,SACpC,uBAAwB,EAAM,MAAM,GACrC,CACF,CAAC,EACF,CAEF,EAAG,GAAG,YAAa,MAAO,EAAO,IAAQ,CACvC,IAAM,EAAY,MAAM,EAAa,EAAI,CACzC,GAAW,OAAO,KAAK,oBAAqB,CAC1C,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,mBAAoB,EAAM,SAAS,OACnC,GAAI,IAAmB,IAAA,GAAY,EAAE,CAAG,CAAE,uBAAwB,KAAK,KAAK,CAAG,EAAgB,CAChG,CACF,CAAC,CACF,EAAiB,IAAA,GACjB,MAAM,GAAW,OAAO,CAAC,MAAO,GAAmB,EAAc,QAAS,EAAM,CAAC,EACjF,CAEF,EAAG,GAAG,gBAAiB,MAAO,EAAQ,IAAQ,EAE5C,MADwB,EAAa,EAAI,GAC9B,OAAO,MAAM,mBAAoB,CAC1C,WAAY,EAAkB,EAAI,CACnC,CAAC,EACF,CAEF,EAAG,GAAG,mBAAoB,MAAO,EAAO,IAAQ,CAC9C,IAAM,EAAY,MAAM,EAAa,EAAI,CACzC,GAAW,OAAO,KAAK,sBAAuB,CAC5C,WAAY,CACV,GAAG,EAAkB,EAAI,CACzB,oBAAqB,EAAM,OAC5B,CACF,CAAC,CACF,MAAM,GAAW,UAAU,CAAC,MAAO,GAAmB,EAAc,WAAY,EAAM,CAAC,EACvF"}
|
|
@@ -70,7 +70,7 @@ const e=require(`./node-9pwprHry.cjs`);let t=require(`@agimon-ai/foundation-vali
|
|
|
70
70
|
ORDER BY timestamp ${o}
|
|
71
71
|
LIMIT ?
|
|
72
72
|
OFFSET ?
|
|
73
|
-
`).all(...c,r,i)).map(e=>this.mapRow(e)),total:Number(l[0]?.count??0)}}catch(e){throw Error(`Failed to query logs: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}};M=w([(0,a.injectable)(),C(0,(0,a.inject)(y.LogStorageService)),S(`design:paramtypes`,[Object])],M);const ne=[`gen_ai.usage.input_tokens`,`gen_ai.usage.prompt_tokens`,`llm.usage.input_tokens`,`input_tokens`,`prompt_tokens`,`usage.input_tokens`,`usage.prompt_tokens`,`response.usage.input_tokens`,`response.usage.prompt_tokens`,`usageMetadata.promptTokenCount`,`promptTokenCount`],re=[`gen_ai.usage.output_tokens`,`gen_ai.usage.completion_tokens`,`llm.usage.output_tokens`,`output_tokens`,`completion_tokens`,`usage.output_tokens`,`usage.completion_tokens`,`response.usage.output_tokens`,`response.usage.completion_tokens`,`usageMetadata.candidatesTokenCount`,`candidatesTokenCount`],ie=[`gen_ai.usage.total_tokens`,`llm.usage.total_tokens`,`total_tokens`,`usage.total_tokens`,`response.usage.total_tokens`,`usageMetadata.totalTokenCount`,`totalTokenCount`],ae=[`gen_ai.usage.cache_read.input_tokens`,`gen_ai.usage.cache_creation.input_tokens`,`input_tokens_details.cached_tokens`,`usage.input_tokens_details.cached_tokens`,`response.usage.input_tokens_details.cached_tokens`,`usage.metadata.cached_tokens`,`metadata.cached_tokens`,`cached_tokens`],oe=[`gen_ai.usage.reasoning.output_tokens`,`output_tokens_details.reasoning_tokens`,`usage.output_tokens_details.reasoning_tokens`,`response.usage.output_tokens_details.reasoning_tokens`,`usage.metadata.reasoning_tokens`,`metadata.reasoning_tokens`,`reasoning_tokens`],se=[`model_proxy.model`,`gen_ai.response.model`,`gen_ai.request.model`,`llm.response.model`,`llm.request.model`,`response.model`,`model`,`modelVersion`,`requestedModel`],ce=[`model_proxy.provider`,`gen_ai.system`,`llm.provider`,`provider`,`provider_name`,`model_provider`];function N(e){if(typeof e!=`string`)return e;let t=e.trim();if(!t||t[0]!==`{`&&t[0]!==`[`)return e;try{return JSON.parse(t)}catch{return e}}function le(e,t){let n=N(e);if(!n||typeof n!=`object`||Array.isArray(n))return;let r=n[t];if(r!==void 0)return N(r);let i=n;for(let e of t.split(`.`))if(i=N(i),!i||typeof i!=`object`||Array.isArray(i)||(i=i[e],i===void 0))return;return N(i)}function ue(e,t){for(let n of t){let t=le(e,n);if(t!=null&&t!==``)return t}}function de(e){if(typeof e==`number`&&Number.isFinite(e))return Math.max(0,e);if(typeof e==`bigint`)return Number(e);if(typeof e==`string`){let t=Number(e);if(Number.isFinite(t))return Math.max(0,t)}return null}function P(e,t){return de(ue(e,t))}function fe(e,t){let n=ue(e,t);return typeof n==`string`&&n.trim().length>0?n.trim():typeof n==`number`||typeof n==`boolean`?String(n):null}function pe(e){let t=P(e,ne),n=P(e,re),r=P(e,ie),i=P(e,ae),a=P(e,oe),o=t??0,s=n??0;return{inputTokens:o,outputTokens:s,totalTokens:r??o+s,cachedInputTokens:i??0,reasoningOutputTokens:a??0,hasUsage:t!==null||n!==null||r!==null||i!==null||a!==null}}function me(e){return{model:fe(e,se),provider:fe(e,ce)}}function he(e){let t=e.toLowerCase();return t.includes(`gen_ai.usage`)||t.includes(`llm.usage`)||/(^|[._-])(input|output|prompt|completion|total|cached|reasoning|candidates?)_?tokens?($|[._-])/.test(t)||/(^|[._-])(prompt|candidates|total)tokencount($|[._-])/.test(t)}function ge(e){return de(e)!==null}function _e(e){return e}function ve(e){if(!e)return{};try{let t=JSON.parse(e);return t&&typeof t==`object`&&!Array.isArray(t)?t:{}}catch{return{}}}function ye(e){let t=pe(e);return{inputTokens:t.inputTokens,outputTokens:t.outputTokens,totalTokens:t.totalTokens,cachedInputTokens:t.cachedInputTokens,reasoningOutputTokens:t.reasoningOutputTokens,usageEventCount:+!!t.hasUsage,hasUsage:t.hasUsage}}function F(){return{inputTokens:0,outputTokens:0,totalTokens:0,cachedInputTokens:0,reasoningOutputTokens:0,usageEventCount:0}}function be(e,t){e.inputTokens+=t.inputTokens,e.outputTokens+=t.outputTokens,e.totalTokens+=t.totalTokens,e.cachedInputTokens+=t.cachedInputTokens,e.reasoningOutputTokens+=t.reasoningOutputTokens,e.usageEventCount+=t.usageEventCount}function I(e,t){t&&(e[t]=(e[t]??0)+1)}function L(e){return e?(Array.isArray(e)?e:[e]).flatMap(e=>e.split(`,`)).map(e=>e.trim()).filter(e=>e.length>0):[]}function xe(e,t){return t.length===0||e!==null&&t.includes(e)}function Se(e){return Math.round(e*100)/100}function R(e,t){if(e.length===0)return 0;let n=[...e].sort((e,t)=>e-t);return n[Math.min(n.length-1,Math.max(0,Math.ceil(n.length*t)-1))]??0}function Ce(e){let t=e.map(e=>e.inputTokens),n=e.map(e=>e.outputTokens),r=e.map(e=>e.totalTokens),i=e=>e.reduce((e,t)=>e+t,0),a=e=>e.length===0?0:Se(i(e)/e.length);return{samples:e.length,avgInputTokens:a(t),p50InputTokens:R(t,.5),p90InputTokens:R(t,.9),avgOutputTokens:a(n),p50OutputTokens:R(n,.5),p90OutputTokens:R(n,.9),avgTotalTokens:a(r),p50TotalTokens:R(r,.5),p90TotalTokens:R(r,.9)}}function we(e,t){return t===0?100:Se(e/t*100)}function Te(e){return e.endsWith(`.tool_token_sample`)}function z(e,...t){for(let n of t){let t=e[n];if(typeof t==`string`&&t.trim().length>0)return t.trim();if(typeof t==`number`||typeof t==`boolean`)return String(t)}return null}function Ee(e){let t=e.metadata,n=e.raw.message??``,r=z(t,`error_type`)??e.raw.error_type,i=t.success,a=t[`tool.result.error`],o=i===!1||i===`false`||a===!0||a===`true`,s=z(t,`decision`),c=z(t,`browse_tool.error.category`);return c===`caller_validation`||c===`caller_execution`?`caller_error`:c===`spec_failure`?`expected_failure`:c===`page_state`?`page_state`:c===`infrastructure_failure`?`infrastructure_failure`:n.includes(`api_error`)?`api_error`:n.includes(`api_retries_exhausted`)?`api_retries_exhausted`:n.includes(`api_refusal`)?`api_refusal`:(n.includes(`tool_result`)||n.includes(`tool error`))&&o?`tool_failure`:n.includes(`tool_decision`)&&s===`reject`?`tool_rejected`:e.raw.level===`error`||e.raw.level===`fatal`||r?`log_error`:null}function De(e,t){return e.message!==`Selected LLM provider`&&e.message!==`LLM provider attempt failed`?null:z(t,`tool`)}function Oe(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,` `)}function ke(e,t){let n=t===`job`?`workflow.job.finished`:`workflow.run.finished`;if(e.raw.message!==n)return!1;let r=t===`job`?`workflow.job.outcome`:`workflow.outcome`;return z(e.metadata,r)===`failed`}let B=class{constructor(e){this.storage=e}async getMetrics(e={},t={}){await this.storage.ensureInitialized();let n=this.storage.getSqliteClient(),r=L(e.model),i=L(e.provider),{whereSql:a,params:o}=this.buildWhereClause(e),s=_e(n.prepare(`
|
|
73
|
+
`).all(...c,r,i)).map(e=>this.mapRow(e)),total:Number(l[0]?.count??0)}}catch(e){throw Error(`Failed to query logs: ${e instanceof Error?e.message:String(e)}`,{cause:e})}}};M=w([(0,a.injectable)(),C(0,(0,a.inject)(y.LogStorageService)),S(`design:paramtypes`,[Object])],M);const ne=[`gen_ai.usage.input_tokens`,`gen_ai.usage.prompt_tokens`,`llm.usage.input_tokens`,`input_tokens`,`prompt_tokens`,`usage.input_tokens`,`usage.prompt_tokens`,`response.usage.input_tokens`,`response.usage.prompt_tokens`,`usageMetadata.promptTokenCount`,`promptTokenCount`],re=[`gen_ai.usage.output_tokens`,`gen_ai.usage.completion_tokens`,`llm.usage.output_tokens`,`output_tokens`,`completion_tokens`,`usage.output_tokens`,`usage.completion_tokens`,`response.usage.output_tokens`,`response.usage.completion_tokens`,`usageMetadata.candidatesTokenCount`,`candidatesTokenCount`],ie=[`gen_ai.usage.total_tokens`,`llm.usage.total_tokens`,`total_tokens`,`usage.total_tokens`,`response.usage.total_tokens`,`usageMetadata.totalTokenCount`,`totalTokenCount`],ae=[`gen_ai.usage.cache_read.input_tokens`,`gen_ai.usage.cache_read_tokens`,`gen_ai.usage.cache_creation.input_tokens`,`gen_ai.usage.cache_write_tokens`,`input_tokens_details.cached_tokens`,`usage.input_tokens_details.cached_tokens`,`response.usage.input_tokens_details.cached_tokens`,`usage.metadata.cached_tokens`,`metadata.cached_tokens`,`cached_tokens`],oe=[`gen_ai.usage.reasoning.output_tokens`,`gen_ai.usage.reasoning_tokens`,`output_tokens_details.reasoning_tokens`,`usage.output_tokens_details.reasoning_tokens`,`response.usage.output_tokens_details.reasoning_tokens`,`usage.metadata.reasoning_tokens`,`metadata.reasoning_tokens`,`reasoning_tokens`],se=[`model_proxy.model`,`gen_ai.response.model`,`gen_ai.request.model`,`llm.response.model`,`llm.request.model`,`response.model`,`model`,`modelVersion`,`requestedModel`],ce=[`model_proxy.provider`,`gen_ai.system`,`llm.provider`,`provider`,`provider_name`,`model_provider`];function N(e){if(typeof e!=`string`)return e;let t=e.trim();if(!t||t[0]!==`{`&&t[0]!==`[`)return e;try{return JSON.parse(t)}catch{return e}}function le(e,t){let n=N(e);if(!n||typeof n!=`object`||Array.isArray(n))return;let r=n[t];if(r!==void 0)return N(r);let i=n;for(let e of t.split(`.`))if(i=N(i),!i||typeof i!=`object`||Array.isArray(i)||(i=i[e],i===void 0))return;return N(i)}function ue(e,t){for(let n of t){let t=le(e,n);if(t!=null&&t!==``)return t}}function de(e){if(typeof e==`number`&&Number.isFinite(e))return Math.max(0,e);if(typeof e==`bigint`)return Number(e);if(typeof e==`string`){let t=Number(e);if(Number.isFinite(t))return Math.max(0,t)}return null}function P(e,t){return de(ue(e,t))}function fe(e,t){let n=ue(e,t);return typeof n==`string`&&n.trim().length>0?n.trim():typeof n==`number`||typeof n==`boolean`?String(n):null}function pe(e){let t=P(e,ne),n=P(e,re),r=P(e,ie),i=P(e,ae),a=P(e,oe),o=t??0,s=n??0;return{inputTokens:o,outputTokens:s,totalTokens:r??o+s,cachedInputTokens:i??0,reasoningOutputTokens:a??0,hasUsage:t!==null||n!==null||r!==null||i!==null||a!==null}}function me(e){return{model:fe(e,se),provider:fe(e,ce)}}function he(e){let t=e.toLowerCase();return t.includes(`gen_ai.usage`)||t.includes(`llm.usage`)||/(^|[._-])(input|output|prompt|completion|total|cached|reasoning|candidates?)_?tokens?($|[._-])/.test(t)||/(^|[._-])(prompt|candidates|total)tokencount($|[._-])/.test(t)}function ge(e){return de(e)!==null}function _e(e){return e}function ve(e){if(!e)return{};try{let t=JSON.parse(e);return t&&typeof t==`object`&&!Array.isArray(t)?t:{}}catch{return{}}}function ye(e){let t=pe(e);return{inputTokens:t.inputTokens,outputTokens:t.outputTokens,totalTokens:t.totalTokens,cachedInputTokens:t.cachedInputTokens,reasoningOutputTokens:t.reasoningOutputTokens,usageEventCount:+!!t.hasUsage,hasUsage:t.hasUsage}}function F(){return{inputTokens:0,outputTokens:0,totalTokens:0,cachedInputTokens:0,reasoningOutputTokens:0,usageEventCount:0}}function be(e,t){e.inputTokens+=t.inputTokens,e.outputTokens+=t.outputTokens,e.totalTokens+=t.totalTokens,e.cachedInputTokens+=t.cachedInputTokens,e.reasoningOutputTokens+=t.reasoningOutputTokens,e.usageEventCount+=t.usageEventCount}function I(e,t){t&&(e[t]=(e[t]??0)+1)}function L(e){return e?(Array.isArray(e)?e:[e]).flatMap(e=>e.split(`,`)).map(e=>e.trim()).filter(e=>e.length>0):[]}function xe(e,t){return t.length===0||e!==null&&t.includes(e)}function Se(e){return Math.round(e*100)/100}function R(e,t){if(e.length===0)return 0;let n=[...e].sort((e,t)=>e-t);return n[Math.min(n.length-1,Math.max(0,Math.ceil(n.length*t)-1))]??0}function Ce(e){let t=e.map(e=>e.inputTokens),n=e.map(e=>e.outputTokens),r=e.map(e=>e.totalTokens),i=e=>e.reduce((e,t)=>e+t,0),a=e=>e.length===0?0:Se(i(e)/e.length);return{samples:e.length,avgInputTokens:a(t),p50InputTokens:R(t,.5),p90InputTokens:R(t,.9),avgOutputTokens:a(n),p50OutputTokens:R(n,.5),p90OutputTokens:R(n,.9),avgTotalTokens:a(r),p50TotalTokens:R(r,.5),p90TotalTokens:R(r,.9)}}function we(e,t){return t===0?100:Se(e/t*100)}function Te(e){return e.endsWith(`.tool_token_sample`)}function z(e,...t){for(let n of t){let t=e[n];if(typeof t==`string`&&t.trim().length>0)return t.trim();if(typeof t==`number`||typeof t==`boolean`)return String(t)}return null}function Ee(e){let t=e.metadata,n=e.raw.message??``,r=z(t,`error_type`)??e.raw.error_type,i=t.success,a=t[`tool.result.error`],o=i===!1||i===`false`||a===!0||a===`true`,s=z(t,`decision`),c=z(t,`browse_tool.error.category`);return c===`caller_validation`||c===`caller_execution`?`caller_error`:c===`spec_failure`?`expected_failure`:c===`page_state`?`page_state`:c===`infrastructure_failure`?`infrastructure_failure`:n.includes(`api_error`)?`api_error`:n.includes(`api_retries_exhausted`)?`api_retries_exhausted`:n.includes(`api_refusal`)?`api_refusal`:(n.includes(`tool_result`)||n.includes(`tool error`))&&o?`tool_failure`:n.includes(`tool_decision`)&&s===`reject`?`tool_rejected`:e.raw.level===`error`||e.raw.level===`fatal`||r?`log_error`:null}function De(e,t){return e.message!==`Selected LLM provider`&&e.message!==`LLM provider attempt failed`?null:z(t,`tool`)}function Oe(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,` `)}function ke(e,t){let n=t===`job`?`workflow.job.finished`:`workflow.run.finished`;if(e.raw.message!==n)return!1;let r=t===`job`?`workflow.job.outcome`:`workflow.outcome`;return z(e.metadata,r)===`failed`}let B=class{constructor(e){this.storage=e}async getMetrics(e={},t={}){await this.storage.ensureInitialized();let n=this.storage.getSqliteClient(),r=L(e.model),i=L(e.provider),{whereSql:a,params:o}=this.buildWhereClause(e),s=_e(n.prepare(`
|
|
74
74
|
SELECT
|
|
75
75
|
id, timestamp, level, message, service, metadata, error_type, error_message,
|
|
76
76
|
session_id, workflow_run_id, workflow_name, job_name, job_id, agent_name,
|
|
@@ -318,4 +318,4 @@ const e=require(`./node-9pwprHry.cjs`);let t=require(`@agimon-ai/foundation-vali
|
|
|
318
318
|
LIMIT ?
|
|
319
319
|
OFFSET ?
|
|
320
320
|
`,d=r.prepare(l).get(...s);return{results:Re(r.prepare(u).all(a,...s,n,c)).map(e=>this.mapRow(e)),total:Number(d.total??0)}}catch(r){let i=await this.searchService.searchLogsPaginated(e,t,n);return console.warn(`Semantic search unavailable, falling back to FTS: ${r instanceof Error?r.message:String(r)}`),{results:i.results.map(e=>this.mapFallbackRow(e)),total:i.total}}}};K=w([(0,a.injectable)(),C(0,(0,a.inject)(y.LogStorageService)),C(1,(0,a.inject)(y.LogSearchService)),C(2,(0,a.inject)(y.EmbeddingService)),S(`design:paramtypes`,[Object,Object,Object])],K);const ze=new a.ContainerModule(e=>{e.bind(y.LogStorageService).to(W).inSingletonScope(),e.bind(y.EmbeddingService).to(E).inSingletonScope(),e.bind(y.LogQueryService).to(M).inSingletonScope(),e.bind(y.LogMetricsService).to(B).inSingletonScope(),e.bind(y.LogSearchService).to(U).inSingletonScope(),e.bind(y.SemanticSearchService).to(K).inSingletonScope(),e.bind(y.LogRetentionService).to(V).inSingletonScope(),e.bind(y.HttpServerHealthCheck).to(D).inSingletonScope(),e.bind(y.HttpServerManager).to(k).inSingletonScope()});function Be(){let e=new a.Container;e.load(ze);let t=e.get(y.LogStorageService);if(x()){let n=e.get(y.EmbeddingService);t.setEmbeddingWriter(e=>n.indexLogs(e))}return e}const q=i.z.object({sessionId:i.z.string().optional().describe(`Agent session id to scope the analysis to one agent launch`),workflowRunId:i.z.string().optional().describe(`Workflow run id to scope to all jobs of one workflow run`),workflowName:i.z.string().optional().describe(`Workflow name to scope to all runs of a workflow type`),jobName:i.z.string().optional().describe(`Workflow job name to scope to a single job`),jobId:i.z.string().optional().describe(`Workflow job execution id to scope by`),agentName:i.z.string().optional().describe(`Agent identity to scope by`),workflowRunKey:i.z.string().optional().describe(`Workflow run key to scope by`),workflowWorkspace:i.z.string().optional().describe(`Workflow workspace to scope by`),stepName:i.z.string().optional().describe(`Workflow step name to scope by`),phase:i.z.string().optional().describe(`Workflow phase to scope by`),service:i.z.union([i.z.string(),i.z.array(i.z.string())]).optional().describe(`Agent/service name(s) to scope by (e.g. claude-code, codex)`),startTime:i.z.string().optional().describe(`Start time for the analysis window (ISO 8601 format)`),endTime:i.z.string().optional().describe(`End time for the analysis window (ISO 8601 format)`),limit:i.z.number().min(1).max(1e3).optional().default(100).describe(`Maximum number of issue samples to return`),scanLimit:i.z.number().min(1).max(1e5).optional().default(5e4).describe(`Maximum matching log records to scan; the response reports when this truncates the window`)});var Ve=class e{static TOOL_NAME=`analyze_agent_issues`;queryService;constructor(e){this.queryService=e.get(y.LogQueryService)}getInputSchema(){return q}getDefinition(){return{name:e.TOOL_NAME,description:`Detect and aggregate agent-run problems (failed tool calls, API errors, refusals, retries exhausted, rejected tools, error logs) for reviewing agent performance and improving the repo harness. Filter by session id, workflow run id, workflow name, job name, or agent/service. Returns counts by category/tool/error-type plus sample issues with the tool, input, and error context needed to diagnose them.`,inputSchema:i.z.toJSONSchema(q,{reused:`inline`})}}async execute(e){try{let t=q.parse(e),n=await this.queryService.analyzeAgentIssues({sessionId:t.sessionId,workflowRunId:t.workflowRunId,workflowName:t.workflowName,jobName:t.jobName,jobId:t.jobId,agentName:t.agentName,workflowRunKey:t.workflowRunKey,workflowWorkspace:t.workflowWorkspace,stepName:t.stepName,phase:t.phase,service:t.service,startTime:t.startTime?new Date(t.startTime):void 0,endTime:t.endTime?new Date(t.endTime):void 0},{sampleLimit:t.limit,scanLimit:t.scanLimit});return{content:[{type:`text`,text:JSON.stringify({...n,issues:n.issues.map(e=>({...e,timestamp:e.timestamp.toISOString()}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error analyzing agent issues: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const J=i.z.object({startTime:i.z.string().optional().describe(`Start time for statistics (ISO 8601 format)`),endTime:i.z.string().optional().describe(`End time for statistics (ISO 8601 format)`),groupBy:i.z.enum([`level`,`service`,`both`,`agent`,`workflow`,`job`,`signal`]).optional().default(`both`).describe(`Dimension used to group statistics.`),signalType:i.z.enum([`log`,`span`,`all`]).optional().default(`log`).describe(`Count log records by default, span records, or 'all' signals.`)});var Y=class e{static TOOL_NAME=`get_log_stats`;queryService;constructor(e){this.queryService=e.get(y.LogQueryService)}getInputSchema(){return J}getDefinition(){return{name:e.TOOL_NAME,description:`Get aggregate log statistics including counts by level, service, or both. Useful for dashboards and monitoring.`,inputSchema:i.z.toJSONSchema(J,{reused:`inline`})}}async execute(e){try{let t=J.parse(e),n;(t.startTime||t.endTime)&&(n={start:t.startTime?new Date(t.startTime):new Date(0),end:t.endTime?new Date(t.endTime):new Date});let r=await this.queryService.getStatistics(n,t.groupBy,t.signalType);return{content:[{type:`text`,text:JSON.stringify({timeRange:n,groupBy:t.groupBy,signalType:t.signalType,totalEntries:r.reduce((e,t)=>e+t.count,0),statistics:r.map(e=>({service:e.service,level:e.level,count:e.count,value:t.groupBy===`level`?e.level:e.service}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error getting log stats: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const He=i.z.object({});var Ue=class e{static TOOL_NAME=`get_services`;queryService;constructor(e){this.queryService=e.get(y.LogQueryService)}getInputSchema(){return He}getDefinition(){return{name:e.TOOL_NAME,description:`List all active services that have sent logs. Returns service names sorted alphabetically.`,inputSchema:i.z.toJSONSchema(He,{reused:`inline`})}}async execute(e){try{let e=await this.queryService.getActiveServices();return{content:[{type:`text`,text:JSON.stringify({count:e.length,services:e.sort()},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error getting services: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const X=i.z.object({traceId:i.z.string().describe(`OTEL trace ID (32 hex characters)`)});var We=class e{static TOOL_NAME=`get_trace_timeline`;queryService;constructor(e){this.queryService=e.get(y.LogQueryService)}getInputSchema(){return X}getDefinition(){return{name:e.TOOL_NAME,description:`Reconstruct the chronological timeline of a distributed trace. Returns all spans in order with timing, service hops, and log messages.`,inputSchema:i.z.toJSONSchema(X,{reused:`inline`})}}async execute(e){try{let t=X.parse(e),n=await this.queryService.getTraceTimeline(t.traceId);return{content:[{type:`text`,text:JSON.stringify({traceId:t.traceId,count:n.length,timeline:n.map(e=>({timestamp:e.timestamp.toISOString(),service:e.service,spanId:e.spanId,parentSpanId:e.parentSpanId,level:e.level,message:e.message,duration:e.duration}))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error getting trace timeline: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};let Ge=function(e){return e.TRACE=`trace`,e.DEBUG=`debug`,e.INFO=`info`,e.WARN=`warn`,e.ERROR=`error`,e.FATAL=`fatal`,e}({});const Ke=i.z.enum([`trace`,`debug`,`info`,`warn`,`error`,`fatal`]),qe=i.z.enum([`fts`,`semantic`,`hybrid`]),Z=i.z.object({searchQuery:i.z.string().optional().describe(`Text to search for. Common punctuation such as hyphens is escaped safely; quoted phrases, prefix matching with *, and boolean operators AND/OR/NOT are supported.`),query:i.z.string().optional().describe(`Alias for searchQuery. Use this for full-text search while also applying structured filters.`),mode:qe.optional().default(`hybrid`).describe(`Search strategy to use`),service:i.z.union([i.z.string(),i.z.array(i.z.string())]).optional().describe(`Service name(s) to filter by`),level:i.z.union([Ke,i.z.array(Ke)]).optional().describe(`Log level(s) to filter by`),traceId:i.z.string().optional().describe(`Trace ID to filter by (OTEL format: 32 hex characters)`),spanId:i.z.string().optional().describe(`Span ID to filter by`),errorType:i.z.string().optional().describe(`Error type to filter by`),sessionId:i.z.string().optional().describe(`Agent session id to filter by`),workflowRunId:i.z.string().optional().describe(`Workflow run id to filter by`),workflowName:i.z.string().optional().describe(`Workflow name to filter by`),jobName:i.z.string().optional().describe(`Workflow job name to filter by`),jobId:i.z.string().optional().describe(`Workflow job execution id to filter by`),agentName:i.z.string().optional().describe(`Agent name to filter by`),parentSessionId:i.z.string().optional().describe(`Parent agent session id to filter by`),workflowRunKey:i.z.string().optional().describe(`Stable workflow run key to filter by`),workflowId:i.z.string().optional().describe(`Workflow file id to filter by`),workflowWorkspace:i.z.string().optional().describe(`Workflow workspace to filter by`),stepName:i.z.string().optional().describe(`Workflow step name to filter by`),phase:i.z.string().optional().describe(`Workflow phase to filter by`),signalType:i.z.enum([`log`,`span`]).optional().describe(`Telemetry signal type to filter by`),startTime:i.z.string().optional().describe(`Start time for log range (ISO 8601 format)`),endTime:i.z.string().optional().describe(`End time for log range (ISO 8601 format)`),limit:i.z.number().min(1).max(1e3).optional().default(100).describe(`Maximum number of search results to return`)});var Je=class e{static TOOL_NAME=`search_logs`;queryService;searchService;semanticSearchService;constructor(e){this.queryService=e.get(y.LogQueryService),this.searchService=e.get(y.LogSearchService),this.semanticSearchService=e.get(y.SemanticSearchService)}getInputSchema(){return Z}getDefinition(){return{name:e.TOOL_NAME,description:`Search and query log entries. Supports structured filters such as level, service, trace/session/workflow fields, time range, and optional full-text query using FTS5, local semantic embeddings, or both.`,inputSchema:i.z.toJSONSchema(Z,{reused:`inline`})}}async execute(e){try{let t=Z.parse(e),n=this.buildFilters(t),r=this.getSearchQuery(t),i=t.mode??`hybrid`,a=r?await this.searchByText(r,n,i,t.limit):await this.queryByFilters(n,t.limit);return{content:[{type:`text`,text:JSON.stringify({mode:r?i:`filter`,count:a.length,query:r,filters:Object.keys(n).length>0?this.serializeFilters(n):void 0,results:a.map(e=>this.serializeResult(e,!!r))},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error searching logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}buildFilters(e){let t={};return e.service&&(t.service=e.service),e.level&&(t.level=e.level),e.traceId&&(t.traceId=e.traceId),e.spanId&&(t.spanId=e.spanId),e.errorType&&(t.errorType=e.errorType),e.sessionId&&(t.sessionId=e.sessionId),e.workflowRunId&&(t.workflowRunId=e.workflowRunId),e.workflowName&&(t.workflowName=e.workflowName),e.jobName&&(t.jobName=e.jobName),e.jobId&&(t.jobId=e.jobId),e.agentName&&(t.agentName=e.agentName),e.parentSessionId&&(t.parentSessionId=e.parentSessionId),e.workflowRunKey&&(t.workflowRunKey=e.workflowRunKey),e.workflowId&&(t.workflowId=e.workflowId),e.workflowWorkspace&&(t.workflowWorkspace=e.workflowWorkspace),e.stepName&&(t.stepName=e.stepName),e.phase&&(t.phase=e.phase),e.signalType&&(t.signalType=e.signalType),e.startTime&&(t.startTime=this.parseDate(e.startTime,`startTime`)),e.endTime&&(t.endTime=this.parseDate(e.endTime,`endTime`)),t}getSearchQuery(e){return(e.searchQuery??e.query)?.trim()||void 0}parseDate(e,t){let n=new Date(e);if(Number.isNaN(n.getTime()))throw Error(`Invalid ${t}: expected ISO 8601 timestamp`);return n}async queryByFilters(e,t){let n=Object.keys(e).length>0,r=!!(e.sessionId||e.workflowRunId||e.workflowName||e.jobName||e.traceId||e.spanId||e.startTime||e.endTime||e.service&&!e.level);return(await this.queryService.queryLogs({...e,level:n?e.level:[`info`,`warn`,`error`,`fatal`],order:r?`asc`:`desc`,limit:t})).logs}async searchByText(e,t,n,r){return n===`semantic`?this.semanticSearchService.searchLogs(e,t,r):n===`fts`?this.searchService.searchLogs(e,t,r):this.searchHybrid(e,t,r)}serializeFilters(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t instanceof Date?t.toISOString():t]))}serializeResult(e,t){let n=typeof e.score==`number`?e.score:typeof e.rank==`number`?this.getFallbackScore(e.rank):void 0;return{...t?{score:n,rank:e.rank,distance:e.distance,sources:e.sources}:{},id:e.id,timestamp:e.timestamp.toISOString(),level:e.level,message:e.message,service:e.service,traceId:e.traceId,spanId:e.spanId,parentSpanId:e.parentSpanId,hostname:e.hostname,pid:e.pid,metadata:e.metadata,errorType:e.errorType,errorMessage:e.errorMessage,errorStack:e.errorStack,sessionId:e.sessionId,workflowRunId:e.workflowRunId,workflowName:e.workflowName,jobName:e.jobName,jobId:e.jobId,agentName:e.agentName,parentSessionId:e.parentSessionId,workflowRunKey:e.workflowRunKey,workflowId:e.workflowId,workflowWorkspace:e.workflowWorkspace,stepName:e.stepName,phase:e.phase,signalType:e.signalType,durationMs:e.durationMs}}getFallbackScore(e){return 1/(1+Math.max(0,Math.abs(e)))}async searchHybrid(e,t,n){let r=Math.max(n*2,50),i=this.searchService.searchLogs(e,t,r),a=await this.semanticSearchService.isAvailable(),o=await i;if(!a)return o;let s=await this.semanticSearchService.searchLogs(e,t,r),c=new Map;return o.forEach((e,t)=>{c.set(e.id,{result:e,score:.65*(1/(1+t)),rank:e.rank,sources:new Set([`fts`])})}),s.forEach((e,t)=>{let n=c.get(e.id),r=.35*(1/(1+t));n?(n.score+=r,n.sources.add(`semantic`),n.distance=e.distance,n.result={...n.result,...e}):c.set(e.id,{result:e,score:r,distance:e.distance,sources:new Set([`semantic`])})}),Array.from(c.values()).sort((e,t)=>t.score-e.score).slice(0,n).map(e=>({...e.result,score:e.score,sources:Array.from(e.sources),rank:e.rank,distance:e.distance}))}};const Q=`@agimon-ai/log-sink-mcp`;let Ye;function Xe(){return Ye??=e.t({serviceName:Q,workspaceRoot:process.cwd(),discoverEndpoint:!1}),Ye}async function $(e,t,n){let r=await Xe();return r.runInSpan(e,{attributes:t},async()=>{try{return await n()}catch(n){throw r.logger.error(`${e} failed`,{attributes:t,exception:n}),n}})}function Ze(e){let a=new n.Server({name:`log-sink-mcp`,version:`0.1.0`},{capabilities:{tools:{}}}),o=e??Be(),s=[new Je(o),new Ve(o),new We(o),new Y(o),new Ue(o)],c=new Map(s.map(e=>[e.getDefinition().name,e]));return a.setRequestHandler(r.ListToolsRequestSchema,async()=>$(`mcp.server.list_tools`,{"mcp.service":Q},async()=>({tools:s.map(e=>e.getDefinition())}))),a.setRequestHandler(r.CallToolRequestSchema,async e=>{let{name:n,arguments:r}=e.params;return $(`mcp.server.call_tool`,{"mcp.service":Q,"mcp.tool.name":n},async()=>{let e=c.get(n);if(!e)return{content:[{type:`text`,text:`Unknown tool: ${n}`}],isError:!0};let a=(0,t.coerceArgs)(r??{},e.getInputSchema());try{return await e.execute(a)}catch(r){return r instanceof i.z.ZodError?{content:[{type:`text`,text:(0,t.formatZodError)(r,{schemaName:n,schema:e.getInputSchema()})}],isError:!0}:{content:[{type:`text`,text:`Error executing tool ${n}: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}})}),a}var Qe=class{server;transport=null;constructor(e){this.server=e}async start(){this.transport=new v.StdioServerTransport,await this.server.connect(this.transport)}async stop(){this.transport&&=(await this.transport.close(),null)}};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return We}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return Ve}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return he}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return ge}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return Ge}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return Be}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return Ze}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return Ue}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return Je}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return Y}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return Qe}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return W}});
|
|
321
|
-
//# sourceMappingURL=stdio-
|
|
321
|
+
//# sourceMappingURL=stdio-QFR4qcFs.cjs.map
|