@agimon-ai/doompi-log 0.0.1-alpha.7 → 0.0.1-alpha.71
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 +68 -21
- package/dist/api-contracts.cjs +1 -0
- package/dist/api-contracts.d.cts +2 -0
- package/dist/api-contracts.d.mts +2 -0
- package/dist/api-contracts.mjs +1 -0
- package/dist/constants/telemetry.cjs +2 -0
- package/dist/constants/telemetry.cjs.map +1 -0
- package/dist/constants/telemetry.mjs +2 -0
- package/dist/constants/telemetry.mjs.map +1 -0
- package/dist/controllers/hubApi.cjs +2 -0
- package/dist/controllers/hubApi.cjs.map +1 -0
- package/dist/controllers/hubApi.mjs +2 -0
- package/dist/controllers/hubApi.mjs.map +1 -0
- package/dist/controllers/piTelemetry.cjs +2 -0
- package/dist/controllers/piTelemetry.cjs.map +1 -0
- package/dist/controllers/piTelemetry.mjs +2 -0
- package/dist/controllers/piTelemetry.mjs.map +1 -0
- package/dist/controllers/serverTelemetry.cjs +2 -0
- package/dist/controllers/serverTelemetry.cjs.map +1 -0
- package/dist/controllers/serverTelemetry.mjs +2 -0
- package/dist/controllers/serverTelemetry.mjs.map +1 -0
- package/dist/extensions/pi.cjs +1 -1
- package/dist/extensions/pi.cjs.map +1 -1
- package/dist/extensions/pi.d.cts +4 -2
- package/dist/extensions/pi.d.cts.map +1 -1
- package/dist/extensions/pi.d.mts +3 -2
- package/dist/extensions/pi.d.mts.map +1 -1
- package/dist/extensions/pi.mjs +1 -1
- package/dist/extensions/pi.mjs.map +1 -1
- package/dist/extensions/server.cjs +2 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.cts +5 -0
- package/dist/extensions/server.d.cts.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +2 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.mts +7 -4
- package/dist/index.mjs +1 -1
- package/dist/metrics-overlay.cjs +1 -0
- package/dist/metrics-overlay.d.cts +3 -0
- package/dist/metrics-overlay.d.mts +3 -0
- package/dist/metrics-overlay.mjs +1 -0
- package/dist/metrics.cjs +1 -2
- package/dist/metrics.d.cts +3 -94
- package/dist/metrics.d.mts +3 -94
- package/dist/metrics.mjs +1 -2
- package/dist/metricsSource.cjs +1 -2
- package/dist/metricsSource.d.cts +3 -25
- package/dist/metricsSource.d.mts +3 -25
- package/dist/metricsSource.mjs +1 -2
- package/dist/schemas/apiContracts.cjs +2 -0
- package/dist/schemas/apiContracts.cjs.map +1 -0
- package/dist/schemas/apiContracts.d.cts +53 -0
- package/dist/schemas/apiContracts.d.cts.map +1 -0
- package/dist/schemas/apiContracts.d.mts +53 -0
- package/dist/schemas/apiContracts.d.mts.map +1 -0
- package/dist/schemas/apiContracts.mjs +2 -0
- package/dist/schemas/apiContracts.mjs.map +1 -0
- package/dist/services/findings/index.cjs +2 -0
- package/dist/services/findings/index.cjs.map +1 -0
- package/dist/services/findings/index.d.cts +22 -0
- package/dist/services/findings/index.d.cts.map +1 -0
- package/dist/services/findings/index.d.mts +22 -0
- package/dist/services/findings/index.d.mts.map +1 -0
- package/dist/services/findings/index.mjs +2 -0
- package/dist/services/findings/index.mjs.map +1 -0
- package/dist/services/issuesSource/index.cjs +2 -0
- package/dist/services/issuesSource/index.cjs.map +1 -0
- package/dist/services/issuesSource/index.mjs +2 -0
- package/dist/services/issuesSource/index.mjs.map +1 -0
- package/dist/services/metrics/index.cjs +2 -0
- package/dist/services/metrics/index.cjs.map +1 -0
- package/dist/services/metrics/index.d.cts +164 -0
- package/dist/services/metrics/index.d.cts.map +1 -0
- package/dist/services/metrics/index.d.mts +164 -0
- package/dist/services/metrics/index.d.mts.map +1 -0
- package/dist/services/metrics/index.mjs +2 -0
- package/dist/services/metrics/index.mjs.map +1 -0
- package/dist/services/metricsSource/index.cjs +2 -0
- package/dist/services/metricsSource/index.cjs.map +1 -0
- package/dist/services/metricsSource/index.d.cts +16 -0
- package/dist/services/metricsSource/index.d.cts.map +1 -0
- package/dist/services/metricsSource/index.d.mts +16 -0
- package/dist/services/metricsSource/index.d.mts.map +1 -0
- package/dist/services/metricsSource/index.mjs +2 -0
- package/dist/services/metricsSource/index.mjs.map +1 -0
- package/dist/services/telemetryEnabled/index.cjs +2 -0
- package/dist/services/telemetryEnabled/index.cjs.map +1 -0
- package/dist/services/telemetryEnabled/index.mjs +2 -0
- package/dist/services/telemetryEnabled/index.mjs.map +1 -0
- package/dist/tui/doomOverlay.cjs.map +1 -1
- package/dist/tui/doomOverlay.d.cts +3 -4
- package/dist/tui/doomOverlay.d.cts.map +1 -1
- package/dist/tui/doomOverlay.d.mts +3 -4
- package/dist/tui/doomOverlay.d.mts.map +1 -1
- package/dist/tui/doomOverlay.mjs.map +1 -1
- package/dist/tui/logMetricsOverlay.cjs +2 -0
- package/dist/tui/logMetricsOverlay.cjs.map +1 -0
- package/dist/tui/{metricsOverlay.d.cts → logMetricsOverlay.d.cts} +29 -18
- package/dist/tui/logMetricsOverlay.d.cts.map +1 -0
- package/dist/tui/{metricsOverlay.d.mts → logMetricsOverlay.d.mts} +29 -18
- package/dist/tui/logMetricsOverlay.d.mts.map +1 -0
- package/dist/tui/logMetricsOverlay.mjs +2 -0
- package/dist/tui/logMetricsOverlay.mjs.map +1 -0
- package/dist/tui/logRuntime.cjs +2 -0
- package/dist/tui/logRuntime.cjs.map +1 -0
- package/dist/tui/logRuntime.d.cts +10 -0
- package/dist/tui/logRuntime.d.cts.map +1 -0
- package/dist/tui/logRuntime.d.mts +10 -0
- package/dist/tui/logRuntime.d.mts.map +1 -0
- package/dist/tui/logRuntime.mjs +2 -0
- package/dist/tui/logRuntime.mjs.map +1 -0
- package/dist/types/metricsSource.d.cts +48 -0
- package/dist/types/metricsSource.d.cts.map +1 -0
- package/dist/types/metricsSource.d.mts +48 -0
- package/dist/types/metricsSource.d.mts.map +1 -0
- package/dist/types/piTelemetry.d.cts +26 -0
- package/dist/types/piTelemetry.d.cts.map +1 -0
- package/dist/types/piTelemetry.d.mts +26 -0
- package/dist/types/piTelemetry.d.mts.map +1 -0
- package/dist/types/sinkStatus.d.cts +12 -0
- package/dist/types/sinkStatus.d.cts.map +1 -0
- package/dist/types/sinkStatus.d.mts +12 -0
- package/dist/types/sinkStatus.d.mts.map +1 -0
- package/dist/types/webMetrics.cjs +2 -0
- package/dist/types/webMetrics.cjs.map +1 -0
- package/dist/types/webMetrics.mjs +2 -0
- package/dist/types/webMetrics.mjs.map +1 -0
- package/package.json +82 -26
- package/src/extensions/web.ts +26 -0
- package/src/types/issueGrouping.ts +86 -0
- package/src/types/webMetrics.ts +245 -0
- package/src/web/api/metricsApi.ts +115 -0
- package/src/web/components/IssuesDetail.tsx +148 -0
- package/src/web/components/IssuesSection.tsx +84 -0
- package/src/web/components/MetricsNotice.tsx +60 -0
- package/src/web/components/MetricsPanel.tsx +127 -0
- package/src/web/components/MetricsReportView.tsx +133 -0
- package/src/web/components/charts/GroupBars.tsx +83 -0
- package/src/web/components/charts/TimelineChart.tsx +76 -0
- package/src/web/lib/chartScale.ts +47 -0
- package/src/web/tsconfig.json +23 -0
- package/dist/extension.cjs +0 -2
- package/dist/extension.cjs.map +0 -1
- package/dist/extension.d.cts +0 -33
- package/dist/extension.d.cts.map +0 -1
- package/dist/extension.d.mts +0 -33
- package/dist/extension.d.mts.map +0 -1
- package/dist/extension.mjs +0 -2
- package/dist/extension.mjs.map +0 -1
- package/dist/metrics.cjs.map +0 -1
- package/dist/metrics.d.cts.map +0 -1
- package/dist/metrics.d.mts.map +0 -1
- package/dist/metrics.mjs.map +0 -1
- package/dist/metricsSource.cjs.map +0 -1
- package/dist/metricsSource.d.cts.map +0 -1
- package/dist/metricsSource.d.mts.map +0 -1
- package/dist/metricsSource.mjs.map +0 -1
- package/dist/tui/metricsOverlay.cjs +0 -2
- package/dist/tui/metricsOverlay.cjs.map +0 -1
- package/dist/tui/metricsOverlay.d.cts.map +0 -1
- package/dist/tui/metricsOverlay.d.mts.map +0 -1
- package/dist/tui/metricsOverlay.mjs +0 -2
- package/dist/tui/metricsOverlay.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,40 +1,87 @@
|
|
|
1
1
|
# @agimon-ai/doompi-log
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Session metrics, findings, sink status, and a Log Metrics overlay for Pi.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Part of the [DoomPi distribution](https://www.npmjs.com/package/@agimon-ai/doompi).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
The extension aggregates telemetry it observes in-process and can query historical data from
|
|
8
|
+
`@agimon-ai/log-sink-mcp`.
|
|
9
|
+
|
|
10
|
+
> **Alpha:** metric attribution and UI contracts may change between releases.
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- Node.js 22.19.0 or newer
|
|
15
|
+
- Pi 0.85.0 and Pi TUI 0.85.0
|
|
10
16
|
|
|
11
17
|
## Install
|
|
12
18
|
|
|
19
|
+
`doompi init` and `dpi init` include Log in `default.packages` in `.doom/modes.yaml`. Remove it
|
|
20
|
+
or move it to a named layer to change which major modes load it. Pi discovers its sole extension
|
|
21
|
+
entry through `package.json.pi.extensions`. For standalone Pi:
|
|
22
|
+
|
|
13
23
|
```bash
|
|
14
|
-
|
|
24
|
+
pi install npm:@agimon-ai/doompi-log
|
|
15
25
|
```
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
Use `SPC h l` to open the current session's Log Metrics overlay.
|
|
28
|
+
|
|
29
|
+
## What is collected
|
|
30
|
+
|
|
31
|
+
The in-process aggregator tracks operational records such as turn token totals, cache use, tool
|
|
32
|
+
calls and failures, operation durations, recent errors, and event counts. It derives findings from
|
|
33
|
+
those records without requiring an external sink.
|
|
34
|
+
|
|
35
|
+
Historical panels resolve a sink over HTTP first and then use the Log Sink CLI fallback. Without a
|
|
36
|
+
reachable sink, current-session headline metrics, findings, errors, and tool metrics still work;
|
|
37
|
+
historical top-consumer and burn-rate views are unavailable. Sink failures are reported in status
|
|
38
|
+
rather than failing the Pi session.
|
|
39
|
+
|
|
40
|
+
Turn cost attribution is contextual: when a turn invokes several tools, the emitted turn total can
|
|
41
|
+
be associated with each participating tool. The overlay uses that value for ranking, not as a
|
|
42
|
+
precise per-tool share.
|
|
43
|
+
|
|
44
|
+
Keys: `r` refreshes, `g` changes dimension, `p` changes period, `?` opens details, and Escape closes.
|
|
18
45
|
|
|
19
|
-
|
|
20
|
-
`.doom/modes.yaml`: making it optional would only create broken configurations.
|
|
46
|
+
## Telemetry controls
|
|
21
47
|
|
|
22
|
-
|
|
48
|
+
| Variable | Effect |
|
|
49
|
+
| -------------------------- | ------------------------------------------------------ |
|
|
50
|
+
| `AGENT_TELEMETRY_DISABLED` | Disables the extension's telemetry export |
|
|
51
|
+
| `OTEL_SDK_DISABLED` | Disables OpenTelemetry SDK export |
|
|
52
|
+
| `AGENT_OTEL_TRACES` | Enables/disables trace export; logs can remain enabled |
|
|
23
53
|
|
|
24
|
-
|
|
54
|
+
File fallback is opt-in through host options. The absence of a configured or reachable endpoint
|
|
55
|
+
does not prevent in-process aggregation. Exceptions and caller-supplied attributes can still be
|
|
56
|
+
sensitive; do not assume "metadata" is harmless.
|
|
25
57
|
|
|
26
|
-
|
|
58
|
+
The overlay requires a TUI. The aggregator, findings, and metrics-source APIs can be embedded in
|
|
59
|
+
headless hosts.
|
|
27
60
|
|
|
28
|
-
##
|
|
61
|
+
## Public API
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { deriveFindings, LogMetricsAggregator, openLogMetricsOverlay } from '@agimon-ai/doompi-log';
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Normal Pi activation uses `extensions/pi`. Composition hosts await `doomLogExtension.install(context, pi)`
|
|
68
|
+
from that entry. The helper owns registrations and cleanup. Session shutdown flushes final telemetry
|
|
69
|
+
before `onDispose` releases the telemetry handle. Optional UI services follow provider replacement.
|
|
70
|
+
|
|
71
|
+
`src/extensions/` declares the Pi, server, and web plugins. Controllers handle events and HTTP requests,
|
|
72
|
+
services own aggregation and sink queries, and `src/tui/` owns terminal presentation. Flat `src/exports/`
|
|
73
|
+
modules expose reusable APIs. The overlay component is available through `metrics-overlay`.
|
|
74
|
+
|
|
75
|
+
## Development
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pnpm build
|
|
79
|
+
pnpm typecheck
|
|
80
|
+
pnpm test
|
|
81
|
+
pnpm lint
|
|
82
|
+
```
|
|
29
83
|
|
|
30
|
-
|
|
31
|
-
| ------------------------------------------ |
|
|
32
|
-
| `@agimon-ai/doompi-log` |
|
|
33
|
-
| `@agimon-ai/doompi-log/extension` |
|
|
34
|
-
| `@agimon-ai/doompi-log/extensions/pi` |
|
|
35
|
-
| `@agimon-ai/doompi-log/metrics` |
|
|
36
|
-
| `@agimon-ai/doompi-log/metricsSource` |
|
|
37
|
-
| `@agimon-ai/doompi-log/tui/metricsOverlay` |
|
|
84
|
+
Maintained by [Agimon](https://agimon.ai/about).
|
|
38
85
|
|
|
39
86
|
## License
|
|
40
87
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./schemas/apiContracts.cjs");exports.apiContracts=e.apiContracts,exports.default=e.apiContracts;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{apiContracts as e}from"./schemas/apiContracts.mjs";export{e as apiContracts,e as default};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=[`error`,`aborted`];exports.DIAGNOSTICS_STDERR=`stderr`,exports.FAILURE_STOP_REASONS=e,exports.HELP_GROUP_DETAIL=`package docs and logs`,exports.LEADER_SOURCE=`@agimon-ai/doompi-log`,exports.LOG_METRICS_COMMAND=`log-metrics`,exports.LOG_METRICS_DESCRIPTION=`Show session-local log metrics`,exports.NO_ENDPOINT=`none`,exports.PACKAGE_NAME=`@agimon-ai/doompi-log`;
|
|
2
|
+
//# sourceMappingURL=telemetry.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.cjs","names":[],"sources":["../../src/constants/telemetry.ts"],"sourcesContent":["export const SERVICE_NAME = 'pi';\nexport const PACKAGE_NAME = '@agimon-ai/doompi-log';\n/** Opt back into stderr diagnostics when debugging outside a live TUI. */\nexport const DIAGNOSTICS_STDERR = 'stderr';\nexport const LEADER_SOURCE = '@agimon-ai/doompi-log';\nexport const LOG_METRICS_COMMAND = 'log-metrics';\nexport const LOG_METRICS_DESCRIPTION = 'Show session-local log metrics';\nexport const HELP_GROUP_ORDER = 70;\n/** Must match the Help group published by doompi-ui, or the registry drops this. */\nexport const HELP_GROUP_DETAIL = 'package docs and logs';\nexport const NO_ENDPOINT = 'none';\nexport const API_ERROR_STATUS = 400;\nexport const FAILURE_STOP_REASONS = ['error', 'aborted'];\n"],"mappings":"AACA,MAWa,EAAuB,CAAC,QAAS,SAAS"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=`@agimon-ai/doompi-log`,t=`stderr`,n=`@agimon-ai/doompi-log`,r=`log-metrics`,i=`Show session-local log metrics`,a=`package docs and logs`,o=`none`,s=[`error`,`aborted`];export{t as DIAGNOSTICS_STDERR,s as FAILURE_STOP_REASONS,a as HELP_GROUP_DETAIL,n as LEADER_SOURCE,r as LOG_METRICS_COMMAND,i as LOG_METRICS_DESCRIPTION,o as NO_ENDPOINT,e as PACKAGE_NAME};
|
|
2
|
+
//# sourceMappingURL=telemetry.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.mjs","names":[],"sources":["../../src/constants/telemetry.ts"],"sourcesContent":["export const SERVICE_NAME = 'pi';\nexport const PACKAGE_NAME = '@agimon-ai/doompi-log';\n/** Opt back into stderr diagnostics when debugging outside a live TUI. */\nexport const DIAGNOSTICS_STDERR = 'stderr';\nexport const LEADER_SOURCE = '@agimon-ai/doompi-log';\nexport const LOG_METRICS_COMMAND = 'log-metrics';\nexport const LOG_METRICS_DESCRIPTION = 'Show session-local log metrics';\nexport const HELP_GROUP_ORDER = 70;\n/** Must match the Help group published by doompi-ui, or the registry drops this. */\nexport const HELP_GROUP_DETAIL = 'package docs and logs';\nexport const NO_ENDPOINT = 'none';\nexport const API_ERROR_STATUS = 400;\nexport const FAILURE_STOP_REASONS = ['error', 'aborted'];\n"],"mappings":"AACA,MAAa,EAAe,wBAEf,EAAqB,SACrB,EAAgB,wBAChB,EAAsB,cACtB,EAA0B,iCAG1B,EAAoB,wBACpB,EAAc,OAEd,EAAuB,CAAC,QAAS,SAAS"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../services/metricsSource/index.cjs"),t=require("../services/issuesSource/index.cjs"),n=require("../types/webMetrics.cjs");let r=require("hono");const i={session:`session`,agent:`agent`,model:`model`,provider:`provider`};function a(e){return{label:e.label,totalTokens:e.totalTokens,inputTokens:e.inputTokens,outputTokens:e.outputTokens}}function o(e){return{key:e.key,totalTokens:e.totalTokens,inputTokens:e.inputTokens,outputTokens:e.outputTokens,issueCount:e.issueCount,failed:e.failed}}function s(e){return{name:e.toolName,calls:e.invocationCount,p90TotalTokens:e.toolCallTurn.p90TotalTokens}}const c={session:`sessionId`,agent:`agentName`,model:`model`,provider:`provider`};function l(e,t){let n=e.filters;if(n===void 0)return;if(t===`session`)return n.sessionId;if(t===`agent`)return n.agentName;let r=t===`model`?n.model:n.provider;if(r!==void 0)return Array.isArray(r)?r[0]:r}function u(e){if(e instanceof Date)return e.toISOString();let t=new Date(e);return Number.isNaN(t.getTime())?new Date(0).toISOString():t.toISOString()}function d(e,t,n){let r=l(e,n);return{generatedAt:u(e.generatedAt),dimension:e.groupBy,period:e.period,bucketUnit:String(e.bucket),transport:t.lastTransport(),...r===void 0?{}:{focus:r},totals:{totalTokens:e.totals.totalTokens,inputTokens:e.totals.inputTokens,outputTokens:e.totals.outputTokens,cachedTokens:e.totals.cachedInputTokens,reasoningTokens:e.totals.reasoningOutputTokens,groupCount:e.totals.groupCount,failedGroups:e.totals.failedGroups,issueCount:e.totals.issueCount},timeline:e.timeline.map(a),groups:e.groups.map(o),tools:e.tools.rows.map(s)}}function f(e){return e.totals.totalTokens===0&&e.groups.length===0&&e.tools.length===0}function p(a={}){let o=new r.Hono,s=a.source??e.createMetricsSource(),l=a.issues??t.createIssuesSource();return o.get(`/metrics`,async e=>{let t=e.req.query(n.METRICS_QUERY_PARAMS.dimension)??`model`,r=e.req.query(n.METRICS_QUERY_PARAMS.period)??`week`;if(!n.isMetricsDimension(t))return e.json({error:`Unknown dimension '${t}'.`},400);if(!n.isMetricsPeriod(r))return e.json({error:`Unknown period '${r}'.`},400);let a=e.req.query(n.METRICS_QUERY_PARAMS.focus),o=a===void 0||a===``?void 0:{[c[t]]:a},l;try{l=await s.query({groupBy:i[t],period:r,limit:20,toolLimit:15,...o===void 0?{}:{filter:o}})}catch(t){let n={unavailable:`no-sink`,detail:t instanceof Error?t.message:`The log sink did not answer.`};return e.json(n)}let u=d(l,s,t);return f(u)?e.json({unavailable:`no-data`,detail:`The log sink is running but has recorded no usage for this period.`}):e.json(u)}),o.get(`/issues`,async e=>{let t=e.req.query(n.METRICS_QUERY_PARAMS.focus);try{let n=await l.query({limit:50,...t===void 0||t===``?{}:{sessionId:t}});return e.json(n)}catch(t){let n={unavailable:`no-sink`,detail:t instanceof Error?t.message:`The log sink did not answer.`};return e.json(n)}}),o}const m={basePath:`log`,start(e){let t=p();return{fetch:e=>t.fetch(e),close:()=>void 0}}};exports.api=m,exports.createLogHubApi=p;
|
|
2
|
+
//# sourceMappingURL=hubApi.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubApi.cjs","names":["Hono","createMetricsSource","createIssuesSource","METRICS_QUERY_PARAMS","isMetricsDimension","isMetricsPeriod"],"sources":["../../src/controllers/hubApi.ts"],"sourcesContent":["import type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-core/package-api';\nimport type { LogMetricGroupRow, LogMetricsReport, ToolMetricRow } from '@agimon-ai/log-sink-mcp';\nimport { Hono } from 'hono';\n\nimport { createIssuesSource } from '../services/issuesSource';\nimport { createMetricsSource } from '../services/metricsSource';\nimport type { IssuesSource } from '../types/issuesSource';\nimport type { MetricsFilter, MetricsSource } from '../types/metricsSource';\nimport {\n isMetricsDimension,\n isMetricsPeriod,\n LOG_API_BASE_PATH,\n ISSUE_SAMPLE_LIMIT,\n METRICS_GROUP_LIMIT,\n METRICS_QUERY_PARAMS,\n METRICS_TOOL_LIMIT,\n type MetricsBucket,\n type MetricsDimension,\n type MetricsGroup,\n type MetricsPeriod,\n type MetricsReport,\n type MetricsTool,\n type MetricsUnavailable,\n type IssuesView,\n} from '../types/webMetrics';\n\n/**\n * The cockpit's half of this package's metrics access.\n *\n * Hub-scoped rather than session-scoped: the questions this answers, where the\n * tokens went and which agents failed, are about the machine over time, and a\n * session server can only see itself. The sink is already machine-wide, so the\n * hub is the only place the two agree.\n *\n * The query path is not reimplemented here. `createMetricsSource` already owns\n * the sink's two transports and its fallback order, and the TUI overlay reads\n * through the same source, so both surfaces answer from one implementation.\n */\n\nconst DEFAULT_DIMENSION: MetricsDimension = 'model';\nconst DEFAULT_PERIOD: MetricsPeriod = 'week';\n\n/** The sink groups sessions under a different name than the page's dimension. */\nconst SINK_GROUP_BY = {\n session: 'session',\n agent: 'agent',\n model: 'model',\n provider: 'provider',\n} as const;\n\nfunction toBucket(bucket: LogMetricsReport['timeline'][number]): MetricsBucket {\n return {\n label: bucket.label,\n totalTokens: bucket.totalTokens,\n inputTokens: bucket.inputTokens,\n outputTokens: bucket.outputTokens,\n };\n}\n\nfunction toGroup(row: LogMetricGroupRow): MetricsGroup {\n return {\n key: row.key,\n totalTokens: row.totalTokens,\n inputTokens: row.inputTokens,\n outputTokens: row.outputTokens,\n issueCount: row.issueCount,\n failed: row.failed,\n };\n}\n\n/**\n * The turn's p90 rather than a sum: the sink attributes a whole turn's tokens\n * to every tool that ran in it, so adding them across calls would multiply the\n * same tokens by however many tools shared the turn.\n */\nfunction toTool(row: ToolMetricRow): MetricsTool {\n return {\n name: row.toolName,\n calls: row.invocationCount,\n p90TotalTokens: row.toolCallTurn.p90TotalTokens,\n };\n}\n\n/** Which filter field carries a focus value, per dimension. */\nconst FOCUS_FIELD: Record<MetricsDimension, keyof MetricsFilter> = {\n session: 'sessionId',\n agent: 'agentName',\n model: 'model',\n provider: 'provider',\n};\n\n/**\n * The focus the sink actually applied, read from its own echo.\n *\n * A daemon that predates a filter ignores it and answers with everything. If\n * the page trusted the request instead of this echo, it would label the whole\n * machine's numbers as one model's.\n */\nfunction appliedFocus(report: LogMetricsReport, dimension: MetricsDimension): string | undefined {\n // The daemon is a separate process on its own release cadence, so its\n // response is parsed defensively rather than trusted to carry every field.\n const filters = report.filters as LogMetricsReport['filters'] | undefined;\n if (filters === undefined) return undefined;\n if (dimension === 'session') return filters.sessionId;\n if (dimension === 'agent') return filters.agentName;\n const applied = dimension === 'model' ? filters.model : filters.provider;\n if (applied === undefined) return undefined;\n return Array.isArray(applied) ? applied[0] : applied;\n}\n\n/**\n * The sink's own type says Date, but both transports parse JSON, so what\n * arrives is an ISO string. Trusting the declared type here throws on every\n * real response, which no test using a hand-built Date would ever catch.\n */\nfunction generatedAtIso(value: LogMetricsReport['generatedAt']): string {\n if (value instanceof Date) return value.toISOString();\n const parsed = new Date(value as unknown as string);\n return Number.isNaN(parsed.getTime()) ? new Date(0).toISOString() : parsed.toISOString();\n}\n\nfunction toReport(report: LogMetricsReport, source: MetricsSource, dimension: MetricsDimension): MetricsReport {\n const focus = appliedFocus(report, dimension);\n return {\n generatedAt: generatedAtIso(report.generatedAt),\n dimension: report.groupBy as MetricsDimension,\n period: report.period as MetricsPeriod,\n bucketUnit: String(report.bucket),\n transport: source.lastTransport(),\n ...(focus === undefined ? {} : { focus }),\n totals: {\n totalTokens: report.totals.totalTokens,\n inputTokens: report.totals.inputTokens,\n outputTokens: report.totals.outputTokens,\n cachedTokens: report.totals.cachedInputTokens,\n reasoningTokens: report.totals.reasoningOutputTokens,\n groupCount: report.totals.groupCount,\n failedGroups: report.totals.failedGroups,\n issueCount: report.totals.issueCount,\n },\n timeline: report.timeline.map(toBucket),\n groups: report.groups.map(toGroup),\n tools: report.tools.rows.map(toTool),\n };\n}\n\n/**\n * A report the sink answered but with nothing in it reads as \"no data\", not as\n * an empty chart. The distinction the reader needs is whether telemetry is not\n * being recorded or simply has not been recorded yet.\n */\nfunction isEmpty(report: MetricsReport): boolean {\n return report.totals.totalTokens === 0 && report.groups.length === 0 && report.tools.length === 0;\n}\n\nexport interface HubApiOptions {\n /** Injected by tests; production resolves the machine's sink. */\n source?: MetricsSource;\n issues?: IssuesSource;\n}\n\nexport function createLogHubApi(options: HubApiOptions = {}): Hono {\n const app = new Hono();\n // One source for the life of the hub: it caches the resolved sink endpoint,\n // and rebuilding it per request would re-probe the daemon every time.\n const source = options.source ?? createMetricsSource();\n const issues = options.issues ?? createIssuesSource();\n\n app.get('/metrics', async (context) => {\n const requestedDimension = context.req.query(METRICS_QUERY_PARAMS.dimension) ?? DEFAULT_DIMENSION;\n const requestedPeriod = context.req.query(METRICS_QUERY_PARAMS.period) ?? DEFAULT_PERIOD;\n if (!isMetricsDimension(requestedDimension)) {\n return context.json({ error: `Unknown dimension '${requestedDimension}'.` }, 400);\n }\n if (!isMetricsPeriod(requestedPeriod)) {\n return context.json({ error: `Unknown period '${requestedPeriod}'.` }, 400);\n }\n\n const focus = context.req.query(METRICS_QUERY_PARAMS.focus);\n const filter: MetricsFilter | undefined =\n focus === undefined || focus === '' ? undefined : { [FOCUS_FIELD[requestedDimension]]: focus };\n\n let report: LogMetricsReport;\n try {\n report = await source.query({\n groupBy: SINK_GROUP_BY[requestedDimension],\n period: requestedPeriod,\n limit: METRICS_GROUP_LIMIT,\n toolLimit: METRICS_TOOL_LIMIT,\n ...(filter === undefined ? {} : { filter }),\n });\n } catch (error) {\n // Both transports declined. That is the ordinary state on a machine\n // where the sink has never run, so it is reported as absence rather\n // than raised as a fault the page has to render as a crash.\n const unavailable: MetricsUnavailable = {\n unavailable: 'no-sink',\n detail: error instanceof Error ? error.message : 'The log sink did not answer.',\n };\n return context.json(unavailable);\n }\n\n const body = toReport(report, source, requestedDimension);\n if (isEmpty(body)) {\n const unavailable: MetricsUnavailable = {\n unavailable: 'no-data',\n detail: 'The log sink is running but has recorded no usage for this period.',\n };\n return context.json(unavailable);\n }\n return context.json(body);\n });\n\n /**\n * The detail behind the count the report carries. Separate from /metrics\n * because it costs a subprocess: the running daemon has no issues route, so\n * folding it into every report would make the whole page as slow as its\n * slowest transport.\n */\n app.get('/issues', async (context) => {\n const focus = context.req.query(METRICS_QUERY_PARAMS.focus);\n try {\n const report = await issues.query({\n limit: ISSUE_SAMPLE_LIMIT,\n ...(focus === undefined || focus === '' ? {} : { sessionId: focus }),\n });\n return context.json(report satisfies IssuesView);\n } catch (error) {\n const unavailable: MetricsUnavailable = {\n unavailable: 'no-sink',\n detail: error instanceof Error ? error.message : 'The log sink did not answer.',\n };\n return context.json(unavailable);\n }\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built hub entry. */\nexport const api: DoomApi = {\n basePath: LOG_API_BASE_PATH,\n start(_context: DoomApiContext): DoomApiHandler {\n const app = createLogHubApi();\n return {\n fetch: (request) => app.fetch(request),\n // The source holds no handle of its own: the HTTP transport is a fetch\n // per query and the CLI transport is a subprocess bounded by its timeout.\n close: () => undefined,\n };\n },\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createMetricsSource as e}from"../services/metricsSource/index.mjs";import{createIssuesSource as t}from"../services/issuesSource/index.mjs";import{METRICS_QUERY_PARAMS as n,isMetricsDimension as r,isMetricsPeriod as i}from"../types/webMetrics.mjs";import{Hono as a}from"hono";const o={session:`session`,agent:`agent`,model:`model`,provider:`provider`};function s(e){return{label:e.label,totalTokens:e.totalTokens,inputTokens:e.inputTokens,outputTokens:e.outputTokens}}function c(e){return{key:e.key,totalTokens:e.totalTokens,inputTokens:e.inputTokens,outputTokens:e.outputTokens,issueCount:e.issueCount,failed:e.failed}}function l(e){return{name:e.toolName,calls:e.invocationCount,p90TotalTokens:e.toolCallTurn.p90TotalTokens}}const u={session:`sessionId`,agent:`agentName`,model:`model`,provider:`provider`};function d(e,t){let n=e.filters;if(n===void 0)return;if(t===`session`)return n.sessionId;if(t===`agent`)return n.agentName;let r=t===`model`?n.model:n.provider;if(r!==void 0)return Array.isArray(r)?r[0]:r}function f(e){if(e instanceof Date)return e.toISOString();let t=new Date(e);return Number.isNaN(t.getTime())?new Date(0).toISOString():t.toISOString()}function p(e,t,n){let r=d(e,n);return{generatedAt:f(e.generatedAt),dimension:e.groupBy,period:e.period,bucketUnit:String(e.bucket),transport:t.lastTransport(),...r===void 0?{}:{focus:r},totals:{totalTokens:e.totals.totalTokens,inputTokens:e.totals.inputTokens,outputTokens:e.totals.outputTokens,cachedTokens:e.totals.cachedInputTokens,reasoningTokens:e.totals.reasoningOutputTokens,groupCount:e.totals.groupCount,failedGroups:e.totals.failedGroups,issueCount:e.totals.issueCount},timeline:e.timeline.map(s),groups:e.groups.map(c),tools:e.tools.rows.map(l)}}function m(e){return e.totals.totalTokens===0&&e.groups.length===0&&e.tools.length===0}function h(s={}){let c=new a,l=s.source??e(),d=s.issues??t();return c.get(`/metrics`,async e=>{let t=e.req.query(n.dimension)??`model`,a=e.req.query(n.period)??`week`;if(!r(t))return e.json({error:`Unknown dimension '${t}'.`},400);if(!i(a))return e.json({error:`Unknown period '${a}'.`},400);let s=e.req.query(n.focus),c=s===void 0||s===``?void 0:{[u[t]]:s},d;try{d=await l.query({groupBy:o[t],period:a,limit:20,toolLimit:15,...c===void 0?{}:{filter:c}})}catch(t){let n={unavailable:`no-sink`,detail:t instanceof Error?t.message:`The log sink did not answer.`};return e.json(n)}let f=p(d,l,t);return m(f)?e.json({unavailable:`no-data`,detail:`The log sink is running but has recorded no usage for this period.`}):e.json(f)}),c.get(`/issues`,async e=>{let t=e.req.query(n.focus);try{let n=await d.query({limit:50,...t===void 0||t===``?{}:{sessionId:t}});return e.json(n)}catch(t){let n={unavailable:`no-sink`,detail:t instanceof Error?t.message:`The log sink did not answer.`};return e.json(n)}}),c}const g={basePath:`log`,start(e){let t=h();return{fetch:e=>t.fetch(e),close:()=>void 0}}};export{g as api,h as createLogHubApi};
|
|
2
|
+
//# sourceMappingURL=hubApi.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hubApi.mjs","names":[],"sources":["../../src/controllers/hubApi.ts"],"sourcesContent":["import type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-core/package-api';\nimport type { LogMetricGroupRow, LogMetricsReport, ToolMetricRow } from '@agimon-ai/log-sink-mcp';\nimport { Hono } from 'hono';\n\nimport { createIssuesSource } from '../services/issuesSource';\nimport { createMetricsSource } from '../services/metricsSource';\nimport type { IssuesSource } from '../types/issuesSource';\nimport type { MetricsFilter, MetricsSource } from '../types/metricsSource';\nimport {\n isMetricsDimension,\n isMetricsPeriod,\n LOG_API_BASE_PATH,\n ISSUE_SAMPLE_LIMIT,\n METRICS_GROUP_LIMIT,\n METRICS_QUERY_PARAMS,\n METRICS_TOOL_LIMIT,\n type MetricsBucket,\n type MetricsDimension,\n type MetricsGroup,\n type MetricsPeriod,\n type MetricsReport,\n type MetricsTool,\n type MetricsUnavailable,\n type IssuesView,\n} from '../types/webMetrics';\n\n/**\n * The cockpit's half of this package's metrics access.\n *\n * Hub-scoped rather than session-scoped: the questions this answers, where the\n * tokens went and which agents failed, are about the machine over time, and a\n * session server can only see itself. The sink is already machine-wide, so the\n * hub is the only place the two agree.\n *\n * The query path is not reimplemented here. `createMetricsSource` already owns\n * the sink's two transports and its fallback order, and the TUI overlay reads\n * through the same source, so both surfaces answer from one implementation.\n */\n\nconst DEFAULT_DIMENSION: MetricsDimension = 'model';\nconst DEFAULT_PERIOD: MetricsPeriod = 'week';\n\n/** The sink groups sessions under a different name than the page's dimension. */\nconst SINK_GROUP_BY = {\n session: 'session',\n agent: 'agent',\n model: 'model',\n provider: 'provider',\n} as const;\n\nfunction toBucket(bucket: LogMetricsReport['timeline'][number]): MetricsBucket {\n return {\n label: bucket.label,\n totalTokens: bucket.totalTokens,\n inputTokens: bucket.inputTokens,\n outputTokens: bucket.outputTokens,\n };\n}\n\nfunction toGroup(row: LogMetricGroupRow): MetricsGroup {\n return {\n key: row.key,\n totalTokens: row.totalTokens,\n inputTokens: row.inputTokens,\n outputTokens: row.outputTokens,\n issueCount: row.issueCount,\n failed: row.failed,\n };\n}\n\n/**\n * The turn's p90 rather than a sum: the sink attributes a whole turn's tokens\n * to every tool that ran in it, so adding them across calls would multiply the\n * same tokens by however many tools shared the turn.\n */\nfunction toTool(row: ToolMetricRow): MetricsTool {\n return {\n name: row.toolName,\n calls: row.invocationCount,\n p90TotalTokens: row.toolCallTurn.p90TotalTokens,\n };\n}\n\n/** Which filter field carries a focus value, per dimension. */\nconst FOCUS_FIELD: Record<MetricsDimension, keyof MetricsFilter> = {\n session: 'sessionId',\n agent: 'agentName',\n model: 'model',\n provider: 'provider',\n};\n\n/**\n * The focus the sink actually applied, read from its own echo.\n *\n * A daemon that predates a filter ignores it and answers with everything. If\n * the page trusted the request instead of this echo, it would label the whole\n * machine's numbers as one model's.\n */\nfunction appliedFocus(report: LogMetricsReport, dimension: MetricsDimension): string | undefined {\n // The daemon is a separate process on its own release cadence, so its\n // response is parsed defensively rather than trusted to carry every field.\n const filters = report.filters as LogMetricsReport['filters'] | undefined;\n if (filters === undefined) return undefined;\n if (dimension === 'session') return filters.sessionId;\n if (dimension === 'agent') return filters.agentName;\n const applied = dimension === 'model' ? filters.model : filters.provider;\n if (applied === undefined) return undefined;\n return Array.isArray(applied) ? applied[0] : applied;\n}\n\n/**\n * The sink's own type says Date, but both transports parse JSON, so what\n * arrives is an ISO string. Trusting the declared type here throws on every\n * real response, which no test using a hand-built Date would ever catch.\n */\nfunction generatedAtIso(value: LogMetricsReport['generatedAt']): string {\n if (value instanceof Date) return value.toISOString();\n const parsed = new Date(value as unknown as string);\n return Number.isNaN(parsed.getTime()) ? new Date(0).toISOString() : parsed.toISOString();\n}\n\nfunction toReport(report: LogMetricsReport, source: MetricsSource, dimension: MetricsDimension): MetricsReport {\n const focus = appliedFocus(report, dimension);\n return {\n generatedAt: generatedAtIso(report.generatedAt),\n dimension: report.groupBy as MetricsDimension,\n period: report.period as MetricsPeriod,\n bucketUnit: String(report.bucket),\n transport: source.lastTransport(),\n ...(focus === undefined ? {} : { focus }),\n totals: {\n totalTokens: report.totals.totalTokens,\n inputTokens: report.totals.inputTokens,\n outputTokens: report.totals.outputTokens,\n cachedTokens: report.totals.cachedInputTokens,\n reasoningTokens: report.totals.reasoningOutputTokens,\n groupCount: report.totals.groupCount,\n failedGroups: report.totals.failedGroups,\n issueCount: report.totals.issueCount,\n },\n timeline: report.timeline.map(toBucket),\n groups: report.groups.map(toGroup),\n tools: report.tools.rows.map(toTool),\n };\n}\n\n/**\n * A report the sink answered but with nothing in it reads as \"no data\", not as\n * an empty chart. The distinction the reader needs is whether telemetry is not\n * being recorded or simply has not been recorded yet.\n */\nfunction isEmpty(report: MetricsReport): boolean {\n return report.totals.totalTokens === 0 && report.groups.length === 0 && report.tools.length === 0;\n}\n\nexport interface HubApiOptions {\n /** Injected by tests; production resolves the machine's sink. */\n source?: MetricsSource;\n issues?: IssuesSource;\n}\n\nexport function createLogHubApi(options: HubApiOptions = {}): Hono {\n const app = new Hono();\n // One source for the life of the hub: it caches the resolved sink endpoint,\n // and rebuilding it per request would re-probe the daemon every time.\n const source = options.source ?? createMetricsSource();\n const issues = options.issues ?? createIssuesSource();\n\n app.get('/metrics', async (context) => {\n const requestedDimension = context.req.query(METRICS_QUERY_PARAMS.dimension) ?? DEFAULT_DIMENSION;\n const requestedPeriod = context.req.query(METRICS_QUERY_PARAMS.period) ?? DEFAULT_PERIOD;\n if (!isMetricsDimension(requestedDimension)) {\n return context.json({ error: `Unknown dimension '${requestedDimension}'.` }, 400);\n }\n if (!isMetricsPeriod(requestedPeriod)) {\n return context.json({ error: `Unknown period '${requestedPeriod}'.` }, 400);\n }\n\n const focus = context.req.query(METRICS_QUERY_PARAMS.focus);\n const filter: MetricsFilter | undefined =\n focus === undefined || focus === '' ? undefined : { [FOCUS_FIELD[requestedDimension]]: focus };\n\n let report: LogMetricsReport;\n try {\n report = await source.query({\n groupBy: SINK_GROUP_BY[requestedDimension],\n period: requestedPeriod,\n limit: METRICS_GROUP_LIMIT,\n toolLimit: METRICS_TOOL_LIMIT,\n ...(filter === undefined ? {} : { filter }),\n });\n } catch (error) {\n // Both transports declined. That is the ordinary state on a machine\n // where the sink has never run, so it is reported as absence rather\n // than raised as a fault the page has to render as a crash.\n const unavailable: MetricsUnavailable = {\n unavailable: 'no-sink',\n detail: error instanceof Error ? error.message : 'The log sink did not answer.',\n };\n return context.json(unavailable);\n }\n\n const body = toReport(report, source, requestedDimension);\n if (isEmpty(body)) {\n const unavailable: MetricsUnavailable = {\n unavailable: 'no-data',\n detail: 'The log sink is running but has recorded no usage for this period.',\n };\n return context.json(unavailable);\n }\n return context.json(body);\n });\n\n /**\n * The detail behind the count the report carries. Separate from /metrics\n * because it costs a subprocess: the running daemon has no issues route, so\n * folding it into every report would make the whole page as slow as its\n * slowest transport.\n */\n app.get('/issues', async (context) => {\n const focus = context.req.query(METRICS_QUERY_PARAMS.focus);\n try {\n const report = await issues.query({\n limit: ISSUE_SAMPLE_LIMIT,\n ...(focus === undefined || focus === '' ? {} : { sessionId: focus }),\n });\n return context.json(report satisfies IssuesView);\n } catch (error) {\n const unavailable: MetricsUnavailable = {\n unavailable: 'no-sink',\n detail: error instanceof Error ? error.message : 'The log sink did not answer.',\n };\n return context.json(unavailable);\n }\n });\n\n return app;\n}\n\n/** The named export a host imports from this package's built hub entry. */\nexport const api: DoomApi = {\n basePath: LOG_API_BASE_PATH,\n start(_context: DoomApiContext): DoomApiHandler {\n const app = createLogHubApi();\n return {\n fetch: (request) => app.fetch(request),\n // The source holds no handle of its own: the HTTP transport is a fetch\n // per query and the CLI transport is a subprocess bounded by its timeout.\n close: () => undefined,\n };\n },\n};\n"],"mappings":"0RAuCA,MAIM,EAAgB,CACpB,QAAS,UACT,MAAO,QACP,MAAO,QACP,SAAU,UACZ,EAEA,SAAS,EAAS,EAA6D,CAC7E,MAAO,CACL,MAAO,EAAO,MACd,YAAa,EAAO,YACpB,YAAa,EAAO,YACpB,aAAc,EAAO,YACvB,CACF,CAEA,SAAS,EAAQ,EAAsC,CACrD,MAAO,CACL,IAAK,EAAI,IACT,YAAa,EAAI,YACjB,YAAa,EAAI,YACjB,aAAc,EAAI,aAClB,WAAY,EAAI,WAChB,OAAQ,EAAI,MACd,CACF,CAOA,SAAS,EAAO,EAAiC,CAC/C,MAAO,CACL,KAAM,EAAI,SACV,MAAO,EAAI,gBACX,eAAgB,EAAI,aAAa,cACnC,CACF,CAGA,MAAM,EAA6D,CACjE,QAAS,YACT,MAAO,YACP,MAAO,QACP,SAAU,UACZ,EASA,SAAS,EAAa,EAA0B,EAAiD,CAG/F,IAAM,EAAU,EAAO,QACvB,GAAI,IAAY,IAAA,GAAW,OAC3B,GAAI,IAAc,UAAW,OAAO,EAAQ,UAC5C,GAAI,IAAc,QAAS,OAAO,EAAQ,UAC1C,IAAM,EAAU,IAAc,QAAU,EAAQ,MAAQ,EAAQ,SAC5D,OAAY,IAAA,GAChB,OAAO,MAAM,QAAQ,CAAO,EAAI,EAAQ,GAAK,CAC/C,CAOA,SAAS,EAAe,EAAgD,CACtE,GAAI,aAAiB,KAAM,OAAO,EAAM,YAAY,EACpD,IAAM,EAAS,IAAI,KAAK,CAA0B,EAClD,OAAO,OAAO,MAAM,EAAO,QAAQ,CAAC,EAAI,IAAI,KAAK,CAAC,CAAA,CAAE,YAAY,EAAI,EAAO,YAAY,CACzF,CAEA,SAAS,EAAS,EAA0B,EAAuB,EAA4C,CAC7G,IAAM,EAAQ,EAAa,EAAQ,CAAS,EAC5C,MAAO,CACL,YAAa,EAAe,EAAO,WAAW,EAC9C,UAAW,EAAO,QAClB,OAAQ,EAAO,OACf,WAAY,OAAO,EAAO,MAAM,EAChC,UAAW,EAAO,cAAc,EAChC,GAAI,IAAU,IAAA,GAAY,CAAC,EAAI,CAAE,OAAM,EACvC,OAAQ,CACN,YAAa,EAAO,OAAO,YAC3B,YAAa,EAAO,OAAO,YAC3B,aAAc,EAAO,OAAO,aAC5B,aAAc,EAAO,OAAO,kBAC5B,gBAAiB,EAAO,OAAO,sBAC/B,WAAY,EAAO,OAAO,WAC1B,aAAc,EAAO,OAAO,aAC5B,WAAY,EAAO,OAAO,UAC5B,EACA,SAAU,EAAO,SAAS,IAAI,CAAQ,EACtC,OAAQ,EAAO,OAAO,IAAI,CAAO,EACjC,MAAO,EAAO,MAAM,KAAK,IAAI,CAAM,CACrC,CACF,CAOA,SAAS,EAAQ,EAAgC,CAC/C,OAAO,EAAO,OAAO,cAAgB,GAAK,EAAO,OAAO,SAAW,GAAK,EAAO,MAAM,SAAW,CAClG,CAQA,SAAgB,EAAgB,EAAyB,CAAC,EAAS,CACjE,IAAM,EAAM,IAAI,EAGV,EAAS,EAAQ,QAAU,EAAoB,EAC/C,EAAS,EAAQ,QAAU,EAAmB,EAsEpD,OApEA,EAAI,IAAI,WAAY,KAAO,IAAY,CACrC,IAAM,EAAqB,EAAQ,IAAI,MAAM,EAAqB,SAAS,GAAK,QAC1E,EAAkB,EAAQ,IAAI,MAAM,EAAqB,MAAM,GAAK,OAC1E,GAAI,CAAC,EAAmB,CAAkB,EACxC,OAAO,EAAQ,KAAK,CAAE,MAAO,sBAAsB,EAAmB,GAAI,EAAG,GAAG,EAElF,GAAI,CAAC,EAAgB,CAAe,EAClC,OAAO,EAAQ,KAAK,CAAE,MAAO,mBAAmB,EAAgB,GAAI,EAAG,GAAG,EAG5E,IAAM,EAAQ,EAAQ,IAAI,MAAM,EAAqB,KAAK,EACpD,EACJ,IAAU,IAAA,IAAa,IAAU,GAAK,IAAA,GAAY,EAAG,EAAY,IAAsB,CAAM,EAE3F,EACJ,GAAI,CACF,EAAS,MAAM,EAAO,MAAM,CAC1B,QAAS,EAAc,GACvB,OAAQ,EACR,MAAA,GACA,UAAA,GACA,GAAI,IAAW,IAAA,GAAY,CAAC,EAAI,CAAE,QAAO,CAC3C,CAAC,CACH,OAAS,EAAO,CAId,IAAM,EAAkC,CACtC,YAAa,UACb,OAAQ,aAAiB,MAAQ,EAAM,QAAU,8BACnD,EACA,OAAO,EAAQ,KAAK,CAAW,CACjC,CAEA,IAAM,EAAO,EAAS,EAAQ,EAAQ,CAAkB,EAQxD,OAPI,EAAQ,CAAI,EAKP,EAAQ,KAAK,CAHlB,YAAa,UACb,OAAQ,oEAEoB,CAAC,EAE1B,EAAQ,KAAK,CAAI,CAC1B,CAAC,EAQD,EAAI,IAAI,UAAW,KAAO,IAAY,CACpC,IAAM,EAAQ,EAAQ,IAAI,MAAM,EAAqB,KAAK,EAC1D,GAAI,CACF,IAAM,EAAS,MAAM,EAAO,MAAM,CAChC,MAAA,GACA,GAAI,IAAU,IAAA,IAAa,IAAU,GAAK,CAAC,EAAI,CAAE,UAAW,CAAM,CACpE,CAAC,EACD,OAAO,EAAQ,KAAK,CAA2B,CACjD,OAAS,EAAO,CACd,IAAM,EAAkC,CACtC,YAAa,UACb,OAAQ,aAAiB,MAAQ,EAAM,QAAU,8BACnD,EACA,OAAO,EAAQ,KAAK,CAAW,CACjC,CACF,CAAC,EAEM,CACT,CAGA,MAAa,EAAe,CAC1B,SAAA,MACA,MAAM,EAA0C,CAC9C,IAAM,EAAM,EAAgB,EAC5B,MAAO,CACL,MAAQ,GAAY,EAAI,MAAM,CAAO,EAGrC,UAAa,IAAA,EACf,CACF,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
const e=require("../constants/telemetry.cjs"),t=require("../services/telemetryEnabled/index.cjs");let n=require("@agimon-ai/doompi-core/readiness"),r=require("@agimon-ai/doompi-telemetry");function i(e){let t={"pi.mode":e.mode,"pi.session.id":e.sessionManager.getSessionId()};return e.thinkingLevel&&(t[`pi.thinking_level`]=e.thinkingLevel),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){if(e.role!==`assistant`)return{"error.type":`AgentError`,outcome:`error`};let t=e.stopReason===`aborted`;return{"error.type":`ProviderError`,"error.code":e.stopReason,outcome:t?`aborted`:`error`}}function s(e){return{service:e.serviceName,backend:e.backend,endpoint:e.endpoint??`none`,endpointSource:e.endpointSource,traces:e.traces,redaction:!0,fileFallback:e.fileFallback}}function c(c,l={},u=!1){let d=l.env??process.env,f=l.metrics,p=new Map,m=!0,h=0,g,_,v,y,b=Promise.resolve(),x=e=>{b=b.then(e).catch(()=>void 0)},S=async(e,t,n)=>{m&&_&&(e===`debug`?await _.recordDebug(t,n):e===`warn`?await _.recordWarning(t,void 0,n):e===`error`?await _.recordError(t,void 0,n):await _.recordEvent(t,n))},C=n=>{if(!m)throw Error(`Log telemetry runtime is disposed.`);let i=n.sessionManager.getSessionId();if(_&&v!==i){let e=_;_=void 0,v=void 0,x(()=>e.shutdown())}return _||=(v=i,(0,r.createDoomTelemetry)({serviceName:l.serviceName??`pi`,packageName:e.PACKAGE_NAME,cwd:n.cwd,env:{...d,PI_SESSION_ID:i},allowFileFallback:l.allowFileFallback,telemetryFactory:l.telemetryFactory,warn:l.onDiagnostic,enableLogs:!0,enableTraces:d.AGENT_OTEL_TRACES===void 0||t.isEnabled(d.AGENT_OTEL_TRACES),onRecord:e=>{m&&f?.record(e.event,e.attributes)},onStatus:e=>{m&&l.onSinkStatus?.(s(e))}})),m&&l.onSinkStatus?.(s(_.status())),_},w=async e=>{let t=y;if(t){if(t.sessionManager!==e.sessionManager)throw Error(`Log readiness belongs to a stale Pi session.`);if(await t.operation,!m||t!==y)throw Error(`Log readiness belongs to a stale extension generation.`)}},T=(e,t)=>{let n=y,r=async()=>{if(n){if(!await n.operation.then(()=>!0,()=>!1)||!m||n!==y||n.sessionManager!==e.sessionManager)return}else if(!m)return;await t()};if(!u)return r();x(r)};return{events:{session_start:(t,r)=>{if(!m)return;let a=++h;p.clear(),g=void 0;let o=async e=>{e?.throwIfAborted(),C(r),await S(`info`,`pi.session.started`,{...i(r),"pi.session.reason":t.reason,"pi.session.previous":t.previousSessionFile!==void 0}),e?.throwIfAborted()},s=u?(0,n.readDoomReadinessCoordinator)(c):void 0;if(!s)return o();let l=y,d=(async()=>{l?.coordinator===s&&await l.operation.catch(()=>void 0),m&&a===h&&await s.start(e.PACKAGE_NAME,`${r.sessionManager.getSessionId()}:${a}`,async e=>(await o(e),{value:void 0})).wait()})();d.catch(()=>void 0),y={sessionManager:r.sessionManager,coordinator:s,operation:d}},before_agent_start:(e,t)=>{if(m)return T(t,async()=>{C(t),await S(`info`,`pi.user_prompt`,{...i(t),"pi.user_message.length":e.prompt.length,"pi.user_message.image_count":e.images?.length??0})})},agent_start:(e,t)=>{if(m)return T(t,async()=>{g=Date.now(),C(t),await S(`info`,`pi.agent.started`,i(t))})},turn_start:(e,t)=>{if(m)return T(t,async()=>{C(t),await S(`debug`,`pi.turn.started`,{...i(t),"pi.turn.index":e.turnIndex})})},turn_end:(t,n)=>{if(m)return T(n,async()=>{C(n);let r=a(t.message);if(await S(`info`,`pi.turn.finished`,{...i(n),"pi.turn.index":t.turnIndex,"pi.tool_result.count":t.toolResults.length,...r}),t.message.role===`assistant`&&e.FAILURE_STOP_REASONS.includes(t.message.stopReason)&&await S(t.message.stopReason===`aborted`?`warn`:`error`,`pi.turn.failed`,{...i(n),"pi.turn.index":t.turnIndex,...o(t.message),...r}),r)for(let e of t.toolResults)await S(`debug`,`pi.tool_token_sample`,{...i(n),...r,"tool.name":e.toolName,token_attribution:`toolCallTurn`})})},tool_execution_start:(e,t)=>{if(m)return T(t,async()=>{p.set(e.toolCallId,Date.now()),C(t),await S(`debug`,`pi.tool_call`,{...i(t),"tool.name":e.toolName,"tool.call.id":e.toolCallId})})},tool_execution_end:(e,t)=>{if(m)return T(t,async()=>{let n=p.get(e.toolCallId);p.delete(e.toolCallId),C(t),await S(`info`,`pi.tool_result`,{...i(t),success:!e.isError,"tool.name":e.toolName,"tool.call.id":e.toolCallId,"tool.result.error":e.isError,...n===void 0?{}:{"tool.duration_ms":Date.now()-n}})})},after_provider_response:(e,t)=>{if(m)return T(t,async()=>{C(t);let n={...i(t),"http.response.status_code":e.status};e.status>=400?await S(`error`,`pi.api_error`,n):await S(`debug`,`pi.api_response`,n)})},model_select:(e,t)=>{if(m)return T(t,async()=>{C(t),await S(`info`,`pi.model.selected`,{...i(t),"pi.model.source":e.source,"gen_ai.provider.name":e.model.provider,"gen_ai.request.model":e.model.id})})},agent_end:(e,t)=>{if(!m)return;let n=h;return T(t,async()=>{C(t),await S(`info`,`pi.agent.finished`,{...i(t),"pi.message.count":e.messages.length,...g===void 0?{}:{"pi.agent.duration_ms":Date.now()-g}}),m&&n===h&&(g=void 0,await _?.flush())})},agent_settled:(e,t)=>{if(m)return T(t,async()=>{C(t),await S(`debug`,`pi.agent.settled`,i(t))})}},onDispose:async()=>{m=!1,h+=1,y=void 0,p.clear(),g=void 0;let e=_;_=void 0,v=void 0,await e?.shutdown()},waitForSession:w,async finishSession(e,t){if(!m)return;await b;let n=y;n&&await n.operation.catch(()=>void 0),m&&(n&&n!==y||_&&(C(t),await S(`info`,`pi.session.finished`,{...i(t),"pi.session.reason":e})))}}}exports.createPiTelemetryRuntime=c;
|
|
2
|
+
//# sourceMappingURL=piTelemetry.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piTelemetry.cjs","names":["createDoomTelemetry","PACKAGE_NAME","isEnabled","readDoomReadinessCoordinator","FAILURE_STOP_REASONS"],"sources":["../../src/controllers/piTelemetry.ts"],"sourcesContent":["import type { PiEventHandlers } from '@agimon-ai/doompi-core/pi-extension';\nimport { type DoomReadinessCoordinator, readDoomReadinessCoordinator } from '@agimon-ai/doompi-core/readiness';\nimport { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionContext, TurnEndEvent } from '@earendil-works/pi-coding-agent';\n\nimport {\n SERVICE_NAME,\n PACKAGE_NAME,\n NO_ENDPOINT,\n API_ERROR_STATUS,\n FAILURE_STOP_REASONS,\n} from '../constants/telemetry';\nimport type {\n PiTelemetryExtensionOptions,\n PiTelemetryRuntimeHandle,\n TelemetryAttributes,\n RecordLevel,\n} from '../types/piTelemetry';\nimport type { SinkStatus } from '../types/sinkStatus';\ninterface PiTelemetryRuntime extends PiTelemetryRuntimeHandle {\n events: PiEventHandlers;\n onDispose(this: void): Promise<void>;\n}\nimport { isEnabled } from '../services/telemetryEnabled';\nfunction contextAttributes(ctx: ExtensionContext): TelemetryAttributes {\n const attributes: TelemetryAttributes = {\n 'pi.mode': ctx.mode,\n 'pi.session.id': ctx.sessionManager.getSessionId(),\n };\n if (ctx.thinkingLevel) attributes['pi.thinking_level'] = ctx.thinkingLevel;\n if (ctx.model) {\n attributes['gen_ai.provider.name'] = ctx.model.provider;\n attributes['gen_ai.request.model'] = ctx.model.id;\n }\n return attributes;\n}\n\nfunction usageAttributes(message: TurnEndEvent['message']): TelemetryAttributes | undefined {\n if (message.role !== 'assistant') return undefined;\n const attributes: TelemetryAttributes = {\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 return attributes;\n}\n\nfunction failureAttributes(message: TurnEndEvent['message']): TelemetryAttributes {\n if (message.role !== 'assistant') return { 'error.type': 'AgentError', outcome: 'error' };\n const aborted = message.stopReason === 'aborted';\n return {\n 'error.type': 'ProviderError',\n 'error.code': message.stopReason,\n outcome: aborted ? 'aborted' : 'error',\n };\n}\n\nfunction statusFromTelemetry(status: ReturnType<DoomTelemetry['status']>): SinkStatus {\n return {\n service: status.serviceName,\n backend: status.backend,\n endpoint: status.endpoint ?? NO_ENDPOINT,\n endpointSource: status.endpointSource,\n traces: status.traces,\n redaction: true,\n fileFallback: status.fileFallback,\n };\n}\n\nexport function createPiTelemetryRuntime(\n cordis: Context,\n options: PiTelemetryExtensionOptions = {},\n splitReadiness = false,\n): PiTelemetryRuntime {\n const env = options.env ?? process.env;\n const metrics = options.metrics;\n const toolStartedAt = new Map<string, number>();\n let active = true;\n let sessionGeneration = 0;\n let agentStartedAt: number | undefined;\n let telemetry: DoomTelemetry | undefined;\n let telemetrySessionId: string | undefined;\n let sessionReadiness:\n | {\n readonly sessionManager: object;\n readonly coordinator: DoomReadinessCoordinator;\n readonly operation: Promise<void>;\n }\n | undefined;\n let backgroundQueue = Promise.resolve();\n\n const enqueue = (operation: () => Promise<void>): void => {\n const scheduled = backgroundQueue.then(operation);\n // Telemetry is advisory and the coordinator owns startup failure notification.\n backgroundQueue = scheduled.catch(() => undefined);\n };\n\n const emit = async (level: RecordLevel, name: string, attributes: TelemetryAttributes): Promise<void> => {\n if (!active || !telemetry) return;\n if (level === 'debug') await telemetry.recordDebug(name, attributes);\n else if (level === 'warn') await telemetry.recordWarning(name, undefined, attributes);\n else if (level === 'error') await telemetry.recordError(name, undefined, attributes);\n else await telemetry.recordEvent(name, attributes);\n };\n\n const getTelemetry = (ctx: ExtensionContext): DoomTelemetry => {\n if (!active) throw new Error('Log telemetry runtime is disposed.');\n // The sink groups records by the x-agent-session-id header, which the SDK derives from\n // the environment once per handle. Nothing stamps PI_SESSION_ID for a top-level session\n // (only doompi-workflow does, for spawned children), so without this overlay every record\n // lands in a single unattributed group and per-agent cost cannot be queried.\n const sessionId = ctx.sessionManager.getSessionId();\n // The handle caches those headers, so a second session in the same process would keep\n // reporting under the first session's id. Rebuild when the session changes.\n if (telemetry && telemetrySessionId !== sessionId) {\n const staleTelemetry = telemetry;\n telemetry = undefined;\n telemetrySessionId = undefined;\n enqueue(() => staleTelemetry.shutdown());\n }\n if (!telemetry) {\n telemetrySessionId = sessionId;\n telemetry = createDoomTelemetry({\n serviceName: options.serviceName ?? SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: ctx.cwd,\n env: { ...env, PI_SESSION_ID: sessionId },\n allowFileFallback: options.allowFileFallback,\n telemetryFactory: options.telemetryFactory,\n warn: options.onDiagnostic,\n enableLogs: true,\n enableTraces: env.AGENT_OTEL_TRACES === undefined || isEnabled(env.AGENT_OTEL_TRACES),\n onRecord: (record) => {\n if (active) metrics?.record(record.event, record.attributes);\n },\n onStatus: (status) => {\n if (active) options.onSinkStatus?.(statusFromTelemetry(status));\n },\n });\n }\n if (active) options.onSinkStatus?.(statusFromTelemetry(telemetry.status()));\n return telemetry;\n };\n\n const waitForSession = async (ctx: ExtensionContext): Promise<void> => {\n const current = sessionReadiness;\n if (!current) return;\n if (current.sessionManager !== ctx.sessionManager) {\n throw new Error('Log readiness belongs to a stale Pi session.');\n }\n await current.operation;\n if (!active || current !== sessionReadiness) {\n throw new Error('Log readiness belongs to a stale extension generation.');\n }\n };\n\n const enqueueForSession = (ctx: ExtensionContext, operation: () => Promise<void>): void | Promise<void> => {\n const expected = sessionReadiness;\n const run = async (): Promise<void> => {\n if (expected) {\n const ready = await expected.operation.then(\n () => true,\n () => false,\n );\n if (!ready || !active || expected !== sessionReadiness || expected.sessionManager !== ctx.sessionManager) {\n return;\n }\n } else if (!active) {\n return;\n }\n await operation();\n };\n if (!splitReadiness) return run();\n enqueue(run);\n return undefined;\n };\n\n return {\n events: {\n session_start: (event, ctx) => {\n if (!active) return undefined;\n const ownGeneration = ++sessionGeneration;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const initialize = async (signal?: AbortSignal): Promise<void> => {\n signal?.throwIfAborted();\n getTelemetry(ctx);\n await emit('info', 'pi.session.started', {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n 'pi.session.previous': event.previousSessionFile !== undefined,\n });\n signal?.throwIfAborted();\n };\n const coordinator = splitReadiness ? readDoomReadinessCoordinator(cordis) : undefined;\n if (!coordinator) return initialize();\n\n const previous = sessionReadiness;\n const operation = (async (): Promise<void> => {\n if (previous?.coordinator === coordinator) await previous.operation.catch(() => undefined);\n if (!active || ownGeneration !== sessionGeneration) return;\n const handle = coordinator.start(\n PACKAGE_NAME,\n `${ctx.sessionManager.getSessionId()}:${ownGeneration}`,\n async (signal) => {\n await initialize(signal);\n return { value: undefined };\n },\n );\n await handle.wait();\n })();\n // Config's coordinator owns the single user-facing failure notification.\n void operation.catch(() => undefined);\n sessionReadiness = { sessionManager: ctx.sessionManager, coordinator, operation };\n return undefined;\n },\n\n before_agent_start: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.user_prompt', {\n ...contextAttributes(ctx),\n 'pi.user_message.length': event.prompt.length,\n 'pi.user_message.image_count': event.images?.length ?? 0,\n });\n });\n },\n\n agent_start: (_event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n agentStartedAt = Date.now();\n getTelemetry(ctx);\n await emit('info', 'pi.agent.started', contextAttributes(ctx));\n });\n },\n\n turn_start: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('debug', 'pi.turn.started', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n });\n });\n },\n\n turn_end: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n const usage = usageAttributes(event.message);\n await emit('info', 'pi.turn.finished', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n 'pi.tool_result.count': event.toolResults.length,\n ...usage,\n });\n if (event.message.role === 'assistant' && FAILURE_STOP_REASONS.includes(event.message.stopReason)) {\n await emit(event.message.stopReason === 'aborted' ? 'warn' : 'error', 'pi.turn.failed', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n ...failureAttributes(event.message),\n ...usage,\n });\n }\n if (!usage) return;\n for (const result of event.toolResults) {\n await emit('debug', 'pi.tool_token_sample', {\n ...contextAttributes(ctx),\n ...usage,\n 'tool.name': result.toolName,\n token_attribution: 'toolCallTurn',\n });\n }\n });\n },\n\n tool_execution_start: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n toolStartedAt.set(event.toolCallId, Date.now());\n getTelemetry(ctx);\n await emit('debug', 'pi.tool_call', {\n ...contextAttributes(ctx),\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n });\n });\n },\n\n tool_execution_end: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n getTelemetry(ctx);\n await emit('info', 'pi.tool_result', {\n ...contextAttributes(ctx),\n success: !event.isError,\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 });\n });\n },\n\n after_provider_response: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n const attributes = {\n ...contextAttributes(ctx),\n 'http.response.status_code': event.status,\n };\n if (event.status >= API_ERROR_STATUS) await emit('error', 'pi.api_error', attributes);\n else await emit('debug', 'pi.api_response', attributes);\n });\n },\n\n model_select: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.model.selected', {\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 agent_end: (event, ctx) => {\n if (!active) return;\n const generation = sessionGeneration;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.agent.finished', {\n ...contextAttributes(ctx),\n 'pi.message.count': event.messages.length,\n ...(agentStartedAt === undefined ? {} : { 'pi.agent.duration_ms': Date.now() - agentStartedAt }),\n });\n if (!active || generation !== sessionGeneration) return;\n agentStartedAt = undefined;\n await telemetry?.flush();\n });\n },\n\n agent_settled: (_event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('debug', 'pi.agent.settled', contextAttributes(ctx));\n });\n },\n },\n onDispose: async () => {\n active = false;\n sessionGeneration += 1;\n sessionReadiness = undefined;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const ownedTelemetry = telemetry;\n telemetry = undefined;\n telemetrySessionId = undefined;\n await ownedTelemetry?.shutdown();\n },\n waitForSession,\n async finishSession(reason: string, ctx: ExtensionContext) {\n if (!active) return;\n await backgroundQueue;\n const current = sessionReadiness;\n if (current) await current.operation.catch(() => undefined);\n if (!active) return;\n if (current && current !== sessionReadiness) return;\n if (!telemetry) return;\n getTelemetry(ctx);\n await emit('info', 'pi.session.finished', {\n ...contextAttributes(ctx),\n 'pi.session.reason': reason,\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{FAILURE_STOP_REASONS as e,PACKAGE_NAME as t}from"../constants/telemetry.mjs";import{isEnabled as n}from"../services/telemetryEnabled/index.mjs";import{readDoomReadinessCoordinator as r}from"@agimon-ai/doompi-core/readiness";import{createDoomTelemetry as i}from"@agimon-ai/doompi-telemetry";function a(e){let t={"pi.mode":e.mode,"pi.session.id":e.sessionManager.getSessionId()};return e.thinkingLevel&&(t[`pi.thinking_level`]=e.thinkingLevel),e.model&&(t[`gen_ai.provider.name`]=e.model.provider,t[`gen_ai.request.model`]=e.model.id),t}function o(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 s(e){if(e.role!==`assistant`)return{"error.type":`AgentError`,outcome:`error`};let t=e.stopReason===`aborted`;return{"error.type":`ProviderError`,"error.code":e.stopReason,outcome:t?`aborted`:`error`}}function c(e){return{service:e.serviceName,backend:e.backend,endpoint:e.endpoint??`none`,endpointSource:e.endpointSource,traces:e.traces,redaction:!0,fileFallback:e.fileFallback}}function l(l,u={},d=!1){let f=u.env??process.env,p=u.metrics,m=new Map,h=!0,g=0,_,v,y,b,x=Promise.resolve(),S=e=>{x=x.then(e).catch(()=>void 0)},C=async(e,t,n)=>{h&&v&&(e===`debug`?await v.recordDebug(t,n):e===`warn`?await v.recordWarning(t,void 0,n):e===`error`?await v.recordError(t,void 0,n):await v.recordEvent(t,n))},w=e=>{if(!h)throw Error(`Log telemetry runtime is disposed.`);let r=e.sessionManager.getSessionId();if(v&&y!==r){let e=v;v=void 0,y=void 0,S(()=>e.shutdown())}return v||=(y=r,i({serviceName:u.serviceName??`pi`,packageName:t,cwd:e.cwd,env:{...f,PI_SESSION_ID:r},allowFileFallback:u.allowFileFallback,telemetryFactory:u.telemetryFactory,warn:u.onDiagnostic,enableLogs:!0,enableTraces:f.AGENT_OTEL_TRACES===void 0||n(f.AGENT_OTEL_TRACES),onRecord:e=>{h&&p?.record(e.event,e.attributes)},onStatus:e=>{h&&u.onSinkStatus?.(c(e))}})),h&&u.onSinkStatus?.(c(v.status())),v},T=async e=>{let t=b;if(t){if(t.sessionManager!==e.sessionManager)throw Error(`Log readiness belongs to a stale Pi session.`);if(await t.operation,!h||t!==b)throw Error(`Log readiness belongs to a stale extension generation.`)}},E=(e,t)=>{let n=b,r=async()=>{if(n){if(!await n.operation.then(()=>!0,()=>!1)||!h||n!==b||n.sessionManager!==e.sessionManager)return}else if(!h)return;await t()};if(!d)return r();S(r)};return{events:{session_start:(e,n)=>{if(!h)return;let i=++g;m.clear(),_=void 0;let o=async t=>{t?.throwIfAborted(),w(n),await C(`info`,`pi.session.started`,{...a(n),"pi.session.reason":e.reason,"pi.session.previous":e.previousSessionFile!==void 0}),t?.throwIfAborted()},s=d?r(l):void 0;if(!s)return o();let c=b,u=(async()=>{c?.coordinator===s&&await c.operation.catch(()=>void 0),h&&i===g&&await s.start(t,`${n.sessionManager.getSessionId()}:${i}`,async e=>(await o(e),{value:void 0})).wait()})();u.catch(()=>void 0),b={sessionManager:n.sessionManager,coordinator:s,operation:u}},before_agent_start:(e,t)=>{if(h)return E(t,async()=>{w(t),await C(`info`,`pi.user_prompt`,{...a(t),"pi.user_message.length":e.prompt.length,"pi.user_message.image_count":e.images?.length??0})})},agent_start:(e,t)=>{if(h)return E(t,async()=>{_=Date.now(),w(t),await C(`info`,`pi.agent.started`,a(t))})},turn_start:(e,t)=>{if(h)return E(t,async()=>{w(t),await C(`debug`,`pi.turn.started`,{...a(t),"pi.turn.index":e.turnIndex})})},turn_end:(t,n)=>{if(h)return E(n,async()=>{w(n);let r=o(t.message);if(await C(`info`,`pi.turn.finished`,{...a(n),"pi.turn.index":t.turnIndex,"pi.tool_result.count":t.toolResults.length,...r}),t.message.role===`assistant`&&e.includes(t.message.stopReason)&&await C(t.message.stopReason===`aborted`?`warn`:`error`,`pi.turn.failed`,{...a(n),"pi.turn.index":t.turnIndex,...s(t.message),...r}),r)for(let e of t.toolResults)await C(`debug`,`pi.tool_token_sample`,{...a(n),...r,"tool.name":e.toolName,token_attribution:`toolCallTurn`})})},tool_execution_start:(e,t)=>{if(h)return E(t,async()=>{m.set(e.toolCallId,Date.now()),w(t),await C(`debug`,`pi.tool_call`,{...a(t),"tool.name":e.toolName,"tool.call.id":e.toolCallId})})},tool_execution_end:(e,t)=>{if(h)return E(t,async()=>{let n=m.get(e.toolCallId);m.delete(e.toolCallId),w(t),await C(`info`,`pi.tool_result`,{...a(t),success:!e.isError,"tool.name":e.toolName,"tool.call.id":e.toolCallId,"tool.result.error":e.isError,...n===void 0?{}:{"tool.duration_ms":Date.now()-n}})})},after_provider_response:(e,t)=>{if(h)return E(t,async()=>{w(t);let n={...a(t),"http.response.status_code":e.status};e.status>=400?await C(`error`,`pi.api_error`,n):await C(`debug`,`pi.api_response`,n)})},model_select:(e,t)=>{if(h)return E(t,async()=>{w(t),await C(`info`,`pi.model.selected`,{...a(t),"pi.model.source":e.source,"gen_ai.provider.name":e.model.provider,"gen_ai.request.model":e.model.id})})},agent_end:(e,t)=>{if(!h)return;let n=g;return E(t,async()=>{w(t),await C(`info`,`pi.agent.finished`,{...a(t),"pi.message.count":e.messages.length,..._===void 0?{}:{"pi.agent.duration_ms":Date.now()-_}}),h&&n===g&&(_=void 0,await v?.flush())})},agent_settled:(e,t)=>{if(h)return E(t,async()=>{w(t),await C(`debug`,`pi.agent.settled`,a(t))})}},onDispose:async()=>{h=!1,g+=1,b=void 0,m.clear(),_=void 0;let e=v;v=void 0,y=void 0,await e?.shutdown()},waitForSession:T,async finishSession(e,t){if(!h)return;await x;let n=b;n&&await n.operation.catch(()=>void 0),h&&(n&&n!==b||v&&(w(t),await C(`info`,`pi.session.finished`,{...a(t),"pi.session.reason":e})))}}}export{l as createPiTelemetryRuntime};
|
|
2
|
+
//# sourceMappingURL=piTelemetry.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"piTelemetry.mjs","names":[],"sources":["../../src/controllers/piTelemetry.ts"],"sourcesContent":["import type { PiEventHandlers } from '@agimon-ai/doompi-core/pi-extension';\nimport { type DoomReadinessCoordinator, readDoomReadinessCoordinator } from '@agimon-ai/doompi-core/readiness';\nimport { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\nimport type { Context } from '@deepseek-ai/cordis';\nimport type { ExtensionContext, TurnEndEvent } from '@earendil-works/pi-coding-agent';\n\nimport {\n SERVICE_NAME,\n PACKAGE_NAME,\n NO_ENDPOINT,\n API_ERROR_STATUS,\n FAILURE_STOP_REASONS,\n} from '../constants/telemetry';\nimport type {\n PiTelemetryExtensionOptions,\n PiTelemetryRuntimeHandle,\n TelemetryAttributes,\n RecordLevel,\n} from '../types/piTelemetry';\nimport type { SinkStatus } from '../types/sinkStatus';\ninterface PiTelemetryRuntime extends PiTelemetryRuntimeHandle {\n events: PiEventHandlers;\n onDispose(this: void): Promise<void>;\n}\nimport { isEnabled } from '../services/telemetryEnabled';\nfunction contextAttributes(ctx: ExtensionContext): TelemetryAttributes {\n const attributes: TelemetryAttributes = {\n 'pi.mode': ctx.mode,\n 'pi.session.id': ctx.sessionManager.getSessionId(),\n };\n if (ctx.thinkingLevel) attributes['pi.thinking_level'] = ctx.thinkingLevel;\n if (ctx.model) {\n attributes['gen_ai.provider.name'] = ctx.model.provider;\n attributes['gen_ai.request.model'] = ctx.model.id;\n }\n return attributes;\n}\n\nfunction usageAttributes(message: TurnEndEvent['message']): TelemetryAttributes | undefined {\n if (message.role !== 'assistant') return undefined;\n const attributes: TelemetryAttributes = {\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 return attributes;\n}\n\nfunction failureAttributes(message: TurnEndEvent['message']): TelemetryAttributes {\n if (message.role !== 'assistant') return { 'error.type': 'AgentError', outcome: 'error' };\n const aborted = message.stopReason === 'aborted';\n return {\n 'error.type': 'ProviderError',\n 'error.code': message.stopReason,\n outcome: aborted ? 'aborted' : 'error',\n };\n}\n\nfunction statusFromTelemetry(status: ReturnType<DoomTelemetry['status']>): SinkStatus {\n return {\n service: status.serviceName,\n backend: status.backend,\n endpoint: status.endpoint ?? NO_ENDPOINT,\n endpointSource: status.endpointSource,\n traces: status.traces,\n redaction: true,\n fileFallback: status.fileFallback,\n };\n}\n\nexport function createPiTelemetryRuntime(\n cordis: Context,\n options: PiTelemetryExtensionOptions = {},\n splitReadiness = false,\n): PiTelemetryRuntime {\n const env = options.env ?? process.env;\n const metrics = options.metrics;\n const toolStartedAt = new Map<string, number>();\n let active = true;\n let sessionGeneration = 0;\n let agentStartedAt: number | undefined;\n let telemetry: DoomTelemetry | undefined;\n let telemetrySessionId: string | undefined;\n let sessionReadiness:\n | {\n readonly sessionManager: object;\n readonly coordinator: DoomReadinessCoordinator;\n readonly operation: Promise<void>;\n }\n | undefined;\n let backgroundQueue = Promise.resolve();\n\n const enqueue = (operation: () => Promise<void>): void => {\n const scheduled = backgroundQueue.then(operation);\n // Telemetry is advisory and the coordinator owns startup failure notification.\n backgroundQueue = scheduled.catch(() => undefined);\n };\n\n const emit = async (level: RecordLevel, name: string, attributes: TelemetryAttributes): Promise<void> => {\n if (!active || !telemetry) return;\n if (level === 'debug') await telemetry.recordDebug(name, attributes);\n else if (level === 'warn') await telemetry.recordWarning(name, undefined, attributes);\n else if (level === 'error') await telemetry.recordError(name, undefined, attributes);\n else await telemetry.recordEvent(name, attributes);\n };\n\n const getTelemetry = (ctx: ExtensionContext): DoomTelemetry => {\n if (!active) throw new Error('Log telemetry runtime is disposed.');\n // The sink groups records by the x-agent-session-id header, which the SDK derives from\n // the environment once per handle. Nothing stamps PI_SESSION_ID for a top-level session\n // (only doompi-workflow does, for spawned children), so without this overlay every record\n // lands in a single unattributed group and per-agent cost cannot be queried.\n const sessionId = ctx.sessionManager.getSessionId();\n // The handle caches those headers, so a second session in the same process would keep\n // reporting under the first session's id. Rebuild when the session changes.\n if (telemetry && telemetrySessionId !== sessionId) {\n const staleTelemetry = telemetry;\n telemetry = undefined;\n telemetrySessionId = undefined;\n enqueue(() => staleTelemetry.shutdown());\n }\n if (!telemetry) {\n telemetrySessionId = sessionId;\n telemetry = createDoomTelemetry({\n serviceName: options.serviceName ?? SERVICE_NAME,\n packageName: PACKAGE_NAME,\n cwd: ctx.cwd,\n env: { ...env, PI_SESSION_ID: sessionId },\n allowFileFallback: options.allowFileFallback,\n telemetryFactory: options.telemetryFactory,\n warn: options.onDiagnostic,\n enableLogs: true,\n enableTraces: env.AGENT_OTEL_TRACES === undefined || isEnabled(env.AGENT_OTEL_TRACES),\n onRecord: (record) => {\n if (active) metrics?.record(record.event, record.attributes);\n },\n onStatus: (status) => {\n if (active) options.onSinkStatus?.(statusFromTelemetry(status));\n },\n });\n }\n if (active) options.onSinkStatus?.(statusFromTelemetry(telemetry.status()));\n return telemetry;\n };\n\n const waitForSession = async (ctx: ExtensionContext): Promise<void> => {\n const current = sessionReadiness;\n if (!current) return;\n if (current.sessionManager !== ctx.sessionManager) {\n throw new Error('Log readiness belongs to a stale Pi session.');\n }\n await current.operation;\n if (!active || current !== sessionReadiness) {\n throw new Error('Log readiness belongs to a stale extension generation.');\n }\n };\n\n const enqueueForSession = (ctx: ExtensionContext, operation: () => Promise<void>): void | Promise<void> => {\n const expected = sessionReadiness;\n const run = async (): Promise<void> => {\n if (expected) {\n const ready = await expected.operation.then(\n () => true,\n () => false,\n );\n if (!ready || !active || expected !== sessionReadiness || expected.sessionManager !== ctx.sessionManager) {\n return;\n }\n } else if (!active) {\n return;\n }\n await operation();\n };\n if (!splitReadiness) return run();\n enqueue(run);\n return undefined;\n };\n\n return {\n events: {\n session_start: (event, ctx) => {\n if (!active) return undefined;\n const ownGeneration = ++sessionGeneration;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const initialize = async (signal?: AbortSignal): Promise<void> => {\n signal?.throwIfAborted();\n getTelemetry(ctx);\n await emit('info', 'pi.session.started', {\n ...contextAttributes(ctx),\n 'pi.session.reason': event.reason,\n 'pi.session.previous': event.previousSessionFile !== undefined,\n });\n signal?.throwIfAborted();\n };\n const coordinator = splitReadiness ? readDoomReadinessCoordinator(cordis) : undefined;\n if (!coordinator) return initialize();\n\n const previous = sessionReadiness;\n const operation = (async (): Promise<void> => {\n if (previous?.coordinator === coordinator) await previous.operation.catch(() => undefined);\n if (!active || ownGeneration !== sessionGeneration) return;\n const handle = coordinator.start(\n PACKAGE_NAME,\n `${ctx.sessionManager.getSessionId()}:${ownGeneration}`,\n async (signal) => {\n await initialize(signal);\n return { value: undefined };\n },\n );\n await handle.wait();\n })();\n // Config's coordinator owns the single user-facing failure notification.\n void operation.catch(() => undefined);\n sessionReadiness = { sessionManager: ctx.sessionManager, coordinator, operation };\n return undefined;\n },\n\n before_agent_start: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.user_prompt', {\n ...contextAttributes(ctx),\n 'pi.user_message.length': event.prompt.length,\n 'pi.user_message.image_count': event.images?.length ?? 0,\n });\n });\n },\n\n agent_start: (_event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n agentStartedAt = Date.now();\n getTelemetry(ctx);\n await emit('info', 'pi.agent.started', contextAttributes(ctx));\n });\n },\n\n turn_start: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('debug', 'pi.turn.started', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n });\n });\n },\n\n turn_end: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n const usage = usageAttributes(event.message);\n await emit('info', 'pi.turn.finished', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n 'pi.tool_result.count': event.toolResults.length,\n ...usage,\n });\n if (event.message.role === 'assistant' && FAILURE_STOP_REASONS.includes(event.message.stopReason)) {\n await emit(event.message.stopReason === 'aborted' ? 'warn' : 'error', 'pi.turn.failed', {\n ...contextAttributes(ctx),\n 'pi.turn.index': event.turnIndex,\n ...failureAttributes(event.message),\n ...usage,\n });\n }\n if (!usage) return;\n for (const result of event.toolResults) {\n await emit('debug', 'pi.tool_token_sample', {\n ...contextAttributes(ctx),\n ...usage,\n 'tool.name': result.toolName,\n token_attribution: 'toolCallTurn',\n });\n }\n });\n },\n\n tool_execution_start: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n toolStartedAt.set(event.toolCallId, Date.now());\n getTelemetry(ctx);\n await emit('debug', 'pi.tool_call', {\n ...contextAttributes(ctx),\n 'tool.name': event.toolName,\n 'tool.call.id': event.toolCallId,\n });\n });\n },\n\n tool_execution_end: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n const startedAt = toolStartedAt.get(event.toolCallId);\n toolStartedAt.delete(event.toolCallId);\n getTelemetry(ctx);\n await emit('info', 'pi.tool_result', {\n ...contextAttributes(ctx),\n success: !event.isError,\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 });\n });\n },\n\n after_provider_response: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n const attributes = {\n ...contextAttributes(ctx),\n 'http.response.status_code': event.status,\n };\n if (event.status >= API_ERROR_STATUS) await emit('error', 'pi.api_error', attributes);\n else await emit('debug', 'pi.api_response', attributes);\n });\n },\n\n model_select: (event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.model.selected', {\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 agent_end: (event, ctx) => {\n if (!active) return;\n const generation = sessionGeneration;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('info', 'pi.agent.finished', {\n ...contextAttributes(ctx),\n 'pi.message.count': event.messages.length,\n ...(agentStartedAt === undefined ? {} : { 'pi.agent.duration_ms': Date.now() - agentStartedAt }),\n });\n if (!active || generation !== sessionGeneration) return;\n agentStartedAt = undefined;\n await telemetry?.flush();\n });\n },\n\n agent_settled: (_event, ctx) => {\n if (!active) return;\n return enqueueForSession(ctx, async () => {\n getTelemetry(ctx);\n await emit('debug', 'pi.agent.settled', contextAttributes(ctx));\n });\n },\n },\n onDispose: async () => {\n active = false;\n sessionGeneration += 1;\n sessionReadiness = undefined;\n toolStartedAt.clear();\n agentStartedAt = undefined;\n const ownedTelemetry = telemetry;\n telemetry = undefined;\n telemetrySessionId = undefined;\n await ownedTelemetry?.shutdown();\n },\n waitForSession,\n async finishSession(reason: string, ctx: ExtensionContext) {\n if (!active) return;\n await backgroundQueue;\n const current = sessionReadiness;\n if (current) await current.operation.catch(() => undefined);\n if (!active) return;\n if (current && current !== sessionReadiness) return;\n if (!telemetry) return;\n getTelemetry(ctx);\n await emit('info', 'pi.session.finished', {\n ...contextAttributes(ctx),\n 'pi.session.reason': reason,\n });\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
let e=require("@agimon-ai/doompi-telemetry");const t=`@agimon-ai/doompi-log`;function n(){let n;return{resources:[{name:`doompi/log-status`,kind:`context`,read:e=>JSON.stringify({sessionId:e.sessionId,active:n!==void 0,status:n?.status()},null,2)}],activities:[{name:t,start(r){return n=(0,e.createDoomTelemetry)({serviceName:`pi`,packageName:t,cwd:r.cwd,env:{...r.environment,PI_SESSION_ID:r.sessionId},enableLogs:!0,enableTraces:!0}),async()=>{let e=n;n=void 0,await e?.shutdown()}}}],commands:[{name:`log-metrics`,description:`Show headless telemetry sink status.`,async execute(e,t){await t.client.notify({title:`DoomPi telemetry`,body:JSON.stringify(n?.status()??{active:!1},null,2),level:`info`})}}],hooks:[{event:`session_start`,handle(e,t){n?.recordEvent(`pi.session.started`,{"pi.session.id":t.sessionId})}},{event:`agent_settled`,handle(e,t){n?.recordEvent(`pi.agent.settled`,{"pi.session.id":t.sessionId})}},{event:`session_shutdown`,handle(e,t){n?.recordEvent(`pi.session.stopped`,{"pi.session.id":t.sessionId}),t.client.setStatus(`doompi-log`,void 0)}}]}}exports.createServerTelemetry=n;
|
|
2
|
+
//# sourceMappingURL=serverTelemetry.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverTelemetry.cjs","names":["createDoomTelemetry"],"sources":["../../src/controllers/serverTelemetry.ts"],"sourcesContent":["import {\n type DoomHeadlessActivity,\n type DoomHeadlessCommand,\n type DoomHeadlessHook,\n type DoomHeadlessResource,\n} from '@agimon-ai/doompi-core/headless';\nimport { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-log';\n\nexport function createServerTelemetry() {\n let telemetry: DoomTelemetry | undefined;\n const resource: DoomHeadlessResource = {\n name: 'doompi/log-status',\n kind: 'context',\n read: (execution) =>\n JSON.stringify(\n {\n sessionId: execution.sessionId,\n active: telemetry !== undefined,\n status: telemetry?.status(),\n },\n null,\n 2,\n ),\n };\n\n const activity: DoomHeadlessActivity = {\n name: PACKAGE_SOURCE,\n start(execution) {\n telemetry = createDoomTelemetry({\n serviceName: 'pi',\n packageName: PACKAGE_SOURCE,\n cwd: execution.cwd,\n env: { ...execution.environment, PI_SESSION_ID: execution.sessionId },\n enableLogs: true,\n enableTraces: true,\n });\n return async () => {\n const current = telemetry;\n telemetry = undefined;\n await current?.shutdown();\n };\n },\n };\n\n const command: DoomHeadlessCommand = {\n name: 'log-metrics',\n description: 'Show headless telemetry sink status.',\n async execute(_args, execution) {\n await execution.client.notify({\n title: 'DoomPi telemetry',\n body: JSON.stringify(telemetry?.status() ?? { active: false }, null, 2),\n level: 'info',\n });\n },\n };\n\n const hooks: DoomHeadlessHook[] = [\n {\n event: 'session_start',\n handle(_event, execution) {\n void telemetry?.recordEvent('pi.session.started', { 'pi.session.id': execution.sessionId });\n },\n },\n {\n event: 'agent_settled',\n handle(_event, execution) {\n void telemetry?.recordEvent('pi.agent.settled', { 'pi.session.id': execution.sessionId });\n },\n },\n {\n event: 'session_shutdown',\n handle(_event, execution) {\n void telemetry?.recordEvent('pi.session.stopped', { 'pi.session.id': execution.sessionId });\n execution.client.setStatus('doompi-log', undefined);\n },\n },\n ];\n return { resources: [resource], activities: [activity], commands: [command], hooks };\n}\n"],"mappings":"6CAQA,MAAM,EAAiB,wBAEvB,SAAgB,GAAwB,CACtC,IAAI,EAoEJ,MAAO,CAAE,UAAW,CAAC,CAlEnB,KAAM,oBACN,KAAM,UACN,KAAO,GACL,KAAK,UACH,CACE,UAAW,EAAU,UACrB,OAAQ,IAAc,IAAA,GACtB,OAAQ,GAAW,OAAO,CAC5B,EACA,KACA,CACF,CAuDwB,CAAC,EAAG,WAAY,CAAC,CAnD3C,KAAM,EACN,MAAM,EAAW,CASf,MARA,IAAA,EAAYA,EAAAA,oBAAAA,CAAoB,CAC9B,YAAa,KACb,YAAa,EACb,IAAK,EAAU,IACf,IAAK,CAAE,GAAG,EAAU,YAAa,cAAe,EAAU,SAAU,EACpE,WAAY,GACZ,aAAc,EAChB,CAAC,EACM,SAAY,CACjB,IAAM,EAAU,EAChB,EAAY,IAAA,GACZ,MAAM,GAAS,SAAS,CAC1B,CACF,CAoCkD,CAAC,EAAG,SAAU,CAAC,CAhCjE,KAAM,cACN,YAAa,uCACb,MAAM,QAAQ,EAAO,EAAW,CAC9B,MAAM,EAAU,OAAO,OAAO,CAC5B,MAAO,mBACP,KAAM,KAAK,UAAU,GAAW,OAAO,GAAK,CAAE,OAAQ,EAAM,EAAG,KAAM,CAAC,EACtE,MAAO,MACT,CAAC,CACH,CAwBuE,CAAC,EAAG,MAAA,CApB3E,CACE,MAAO,gBACP,OAAO,EAAQ,EAAW,CACxB,GAAgB,YAAY,qBAAsB,CAAE,gBAAiB,EAAU,SAAU,CAAC,CAC5F,CACF,EACA,CACE,MAAO,gBACP,OAAO,EAAQ,EAAW,CACxB,GAAgB,YAAY,mBAAoB,CAAE,gBAAiB,EAAU,SAAU,CAAC,CAC1F,CACF,EACA,CACE,MAAO,mBACP,OAAO,EAAQ,EAAW,CACxB,GAAgB,YAAY,qBAAsB,CAAE,gBAAiB,EAAU,SAAU,CAAC,EAC1F,EAAU,OAAO,UAAU,aAAc,IAAA,EAAS,CACpD,CACF,CAE+E,CAAE,CACrF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createDoomTelemetry as e}from"@agimon-ai/doompi-telemetry";const t=`@agimon-ai/doompi-log`;function n(){let n;return{resources:[{name:`doompi/log-status`,kind:`context`,read:e=>JSON.stringify({sessionId:e.sessionId,active:n!==void 0,status:n?.status()},null,2)}],activities:[{name:t,start(r){return n=e({serviceName:`pi`,packageName:t,cwd:r.cwd,env:{...r.environment,PI_SESSION_ID:r.sessionId},enableLogs:!0,enableTraces:!0}),async()=>{let e=n;n=void 0,await e?.shutdown()}}}],commands:[{name:`log-metrics`,description:`Show headless telemetry sink status.`,async execute(e,t){await t.client.notify({title:`DoomPi telemetry`,body:JSON.stringify(n?.status()??{active:!1},null,2),level:`info`})}}],hooks:[{event:`session_start`,handle(e,t){n?.recordEvent(`pi.session.started`,{"pi.session.id":t.sessionId})}},{event:`agent_settled`,handle(e,t){n?.recordEvent(`pi.agent.settled`,{"pi.session.id":t.sessionId})}},{event:`session_shutdown`,handle(e,t){n?.recordEvent(`pi.session.stopped`,{"pi.session.id":t.sessionId}),t.client.setStatus(`doompi-log`,void 0)}}]}}export{n as createServerTelemetry};
|
|
2
|
+
//# sourceMappingURL=serverTelemetry.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverTelemetry.mjs","names":[],"sources":["../../src/controllers/serverTelemetry.ts"],"sourcesContent":["import {\n type DoomHeadlessActivity,\n type DoomHeadlessCommand,\n type DoomHeadlessHook,\n type DoomHeadlessResource,\n} from '@agimon-ai/doompi-core/headless';\nimport { createDoomTelemetry, type DoomTelemetry } from '@agimon-ai/doompi-telemetry';\n\nconst PACKAGE_SOURCE = '@agimon-ai/doompi-log';\n\nexport function createServerTelemetry() {\n let telemetry: DoomTelemetry | undefined;\n const resource: DoomHeadlessResource = {\n name: 'doompi/log-status',\n kind: 'context',\n read: (execution) =>\n JSON.stringify(\n {\n sessionId: execution.sessionId,\n active: telemetry !== undefined,\n status: telemetry?.status(),\n },\n null,\n 2,\n ),\n };\n\n const activity: DoomHeadlessActivity = {\n name: PACKAGE_SOURCE,\n start(execution) {\n telemetry = createDoomTelemetry({\n serviceName: 'pi',\n packageName: PACKAGE_SOURCE,\n cwd: execution.cwd,\n env: { ...execution.environment, PI_SESSION_ID: execution.sessionId },\n enableLogs: true,\n enableTraces: true,\n });\n return async () => {\n const current = telemetry;\n telemetry = undefined;\n await current?.shutdown();\n };\n },\n };\n\n const command: DoomHeadlessCommand = {\n name: 'log-metrics',\n description: 'Show headless telemetry sink status.',\n async execute(_args, execution) {\n await execution.client.notify({\n title: 'DoomPi telemetry',\n body: JSON.stringify(telemetry?.status() ?? { active: false }, null, 2),\n level: 'info',\n });\n },\n };\n\n const hooks: DoomHeadlessHook[] = [\n {\n event: 'session_start',\n handle(_event, execution) {\n void telemetry?.recordEvent('pi.session.started', { 'pi.session.id': execution.sessionId });\n },\n },\n {\n event: 'agent_settled',\n handle(_event, execution) {\n void telemetry?.recordEvent('pi.agent.settled', { 'pi.session.id': execution.sessionId });\n },\n },\n {\n event: 'session_shutdown',\n handle(_event, execution) {\n void telemetry?.recordEvent('pi.session.stopped', { 'pi.session.id': execution.sessionId });\n execution.client.setStatus('doompi-log', undefined);\n },\n },\n ];\n return { resources: [resource], activities: [activity], commands: [command], hooks };\n}\n"],"mappings":"kEAQA,MAAM,EAAiB,wBAEvB,SAAgB,GAAwB,CACtC,IAAI,EAoEJ,MAAO,CAAE,UAAW,CAAC,CAlEnB,KAAM,oBACN,KAAM,UACN,KAAO,GACL,KAAK,UACH,CACE,UAAW,EAAU,UACrB,OAAQ,IAAc,IAAA,GACtB,OAAQ,GAAW,OAAO,CAC5B,EACA,KACA,CACF,CAuDwB,CAAC,EAAG,WAAY,CAAC,CAnD3C,KAAM,EACN,MAAM,EAAW,CASf,MARA,GAAY,EAAoB,CAC9B,YAAa,KACb,YAAa,EACb,IAAK,EAAU,IACf,IAAK,CAAE,GAAG,EAAU,YAAa,cAAe,EAAU,SAAU,EACpE,WAAY,GACZ,aAAc,EAChB,CAAC,EACM,SAAY,CACjB,IAAM,EAAU,EAChB,EAAY,IAAA,GACZ,MAAM,GAAS,SAAS,CAC1B,CACF,CAoCkD,CAAC,EAAG,SAAU,CAAC,CAhCjE,KAAM,cACN,YAAa,uCACb,MAAM,QAAQ,EAAO,EAAW,CAC9B,MAAM,EAAU,OAAO,OAAO,CAC5B,MAAO,mBACP,KAAM,KAAK,UAAU,GAAW,OAAO,GAAK,CAAE,OAAQ,EAAM,EAAG,KAAM,CAAC,EACtE,MAAO,MACT,CAAC,CACH,CAwBuE,CAAC,EAAG,MAAA,CApB3E,CACE,MAAO,gBACP,OAAO,EAAQ,EAAW,CACxB,GAAgB,YAAY,qBAAsB,CAAE,gBAAiB,EAAU,SAAU,CAAC,CAC5F,CACF,EACA,CACE,MAAO,gBACP,OAAO,EAAQ,EAAW,CACxB,GAAgB,YAAY,mBAAoB,CAAE,gBAAiB,EAAU,SAAU,CAAC,CAC1F,CACF,EACA,CACE,MAAO,mBACP,OAAO,EAAQ,EAAW,CACxB,GAAgB,YAAY,qBAAsB,CAAE,gBAAiB,EAAU,SAAU,CAAC,EAC1F,EAAU,OAAO,UAAU,aAAc,IAAA,EAAS,CACpD,CACF,CAE+E,CAAE,CACrF"}
|
package/dist/extensions/pi.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../constants/telemetry.cjs"),t=require("../tui/logRuntime.cjs"),n=require("../controllers/piTelemetry.cjs"),r=(0,require("@agimon-ai/doompi-core/pi-extension").definePiExtension)(e.PACKAGE_NAME,({context:e,options:r})=>{let i=t.createLogViewRuntime(e=>a.waitForSession(e),r),a=n.createPiTelemetryRuntime(e,i.telemetryOptions,!0),o;return{services:i.services,commands:i.commands,events:{...a.events,session_shutdown:(e,t)=>o??=a.finishSession(e.reason,t)},async onDispose(){i.onDispose(),await a.onDispose()}}});exports.default=r,exports.doomLogExtension=r;
|
|
2
2
|
//# sourceMappingURL=pi.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi.cjs","names":["
|
|
1
|
+
{"version":3,"file":"pi.cjs","names":["definePiExtension","PACKAGE_NAME","createLogViewRuntime","createPiTelemetryRuntime"],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { definePiExtension } from '@agimon-ai/doompi-core/pi-extension';\n\nimport { PACKAGE_NAME } from '../constants/telemetry';\nimport { createPiTelemetryRuntime } from '../controllers/piTelemetry';\nimport { createLogViewRuntime } from '../tui/logRuntime';\nimport type { PiTelemetryExtensionOptions } from '../types/piTelemetry';\nexport const doomLogExtension = definePiExtension<PiTelemetryExtensionOptions>(PACKAGE_NAME, ({ context, options }) => {\n const view = createLogViewRuntime((ctx) => telemetry.waitForSession(ctx), options);\n const telemetry = createPiTelemetryRuntime(context, view.telemetryOptions, true);\n let shutdown: Promise<void> | undefined;\n return {\n services: view.services,\n commands: view.commands,\n events: {\n ...telemetry.events,\n session_shutdown: (event, ctx) => (shutdown ??= telemetry.finishSession(event.reason, ctx)),\n },\n async onDispose() {\n view.onDispose();\n await telemetry.onDispose();\n },\n };\n});\nexport default doomLogExtension;\n"],"mappings":"4NAMa,GAAA,+CAAmBA,CAAAA,CAAAA,kBAAAA,CAA+CC,EAAAA,cAAe,CAAE,UAAS,aAAc,CACrH,IAAM,EAAOC,EAAAA,qBAAsB,GAAQ,EAAU,eAAe,CAAG,EAAG,CAAO,EAC3E,EAAYC,EAAAA,yBAAyB,EAAS,EAAK,iBAAkB,EAAI,EAC3E,EACJ,MAAO,CACL,SAAU,EAAK,SACf,SAAU,EAAK,SACf,OAAQ,CACN,GAAG,EAAU,OACb,kBAAmB,EAAO,IAAS,IAAa,EAAU,cAAc,EAAM,OAAQ,CAAG,CAC3F,EACA,MAAM,WAAY,CAChB,EAAK,UAAU,EACf,MAAM,EAAU,UAAU,CAC5B,CACF,CACF,CAAC"}
|
package/dist/extensions/pi.d.cts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { PiTelemetryExtensionOptions } from "../types/piTelemetry.cjs";
|
|
1
2
|
//#region src/extensions/pi.d.ts
|
|
2
|
-
declare const
|
|
3
|
-
|
|
3
|
+
export declare const doomLogExtension: import("@agimon-ai/doompi-core/runtime-pi-extension").DefinedPiExtension<PiTelemetryExtensionOptions>;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { doomLogExtension as default };
|
|
4
6
|
//# sourceMappingURL=pi.d.cts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi.d.cts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"pi.d.cts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":";;qBAMa,wEAAgB,mBAAA"}
|
package/dist/extensions/pi.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { PiTelemetryExtensionOptions } from "../types/piTelemetry.mjs";
|
|
1
2
|
//#region src/extensions/pi.d.ts
|
|
2
|
-
declare const
|
|
3
|
+
export declare const doomLogExtension: import("@agimon-ai/doompi-core/runtime-pi-extension").DefinedPiExtension<PiTelemetryExtensionOptions>;
|
|
3
4
|
//#endregion
|
|
4
|
-
export {
|
|
5
|
+
export { doomLogExtension as default };
|
|
5
6
|
//# sourceMappingURL=pi.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi.d.mts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"pi.d.mts","names":[],"sources":["../../src/extensions/pi.ts"],"mappings":";;qBAMa,wEAAgB,mBAAA"}
|
package/dist/extensions/pi.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{PACKAGE_NAME as e}from"../constants/telemetry.mjs";import{createLogViewRuntime as t}from"../tui/logRuntime.mjs";import{createPiTelemetryRuntime as n}from"../controllers/piTelemetry.mjs";import{definePiExtension as r}from"@agimon-ai/doompi-core/pi-extension";const i=r(e,({context:e,options:r})=>{let i=t(e=>a.waitForSession(e),r),a=n(e,i.telemetryOptions,!0),o;return{services:i.services,commands:i.commands,events:{...a.events,session_shutdown:(e,t)=>o??=a.finishSession(e.reason,t)},async onDispose(){i.onDispose(),await a.onDispose()}}});export{i as default,i as doomLogExtension};
|
|
2
2
|
//# sourceMappingURL=pi.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pi.mjs","names":[],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"pi.mjs","names":[],"sources":["../../src/extensions/pi.ts"],"sourcesContent":["import { definePiExtension } from '@agimon-ai/doompi-core/pi-extension';\n\nimport { PACKAGE_NAME } from '../constants/telemetry';\nimport { createPiTelemetryRuntime } from '../controllers/piTelemetry';\nimport { createLogViewRuntime } from '../tui/logRuntime';\nimport type { PiTelemetryExtensionOptions } from '../types/piTelemetry';\nexport const doomLogExtension = definePiExtension<PiTelemetryExtensionOptions>(PACKAGE_NAME, ({ context, options }) => {\n const view = createLogViewRuntime((ctx) => telemetry.waitForSession(ctx), options);\n const telemetry = createPiTelemetryRuntime(context, view.telemetryOptions, true);\n let shutdown: Promise<void> | undefined;\n return {\n services: view.services,\n commands: view.commands,\n events: {\n ...telemetry.events,\n session_shutdown: (event, ctx) => (shutdown ??= telemetry.finishSession(event.reason, ctx)),\n },\n async onDispose() {\n view.onDispose();\n await telemetry.onDispose();\n },\n };\n});\nexport default doomLogExtension;\n"],"mappings":"yQAMA,MAAa,EAAmB,EAA+C,GAAe,CAAE,UAAS,aAAc,CACrH,IAAM,EAAO,EAAsB,GAAQ,EAAU,eAAe,CAAG,EAAG,CAAO,EAC3E,EAAY,EAAyB,EAAS,EAAK,iBAAkB,EAAI,EAC3E,EACJ,MAAO,CACL,SAAU,EAAK,SACf,SAAU,EAAK,SACf,OAAQ,CACN,GAAG,EAAU,OACb,kBAAmB,EAAO,IAAS,IAAa,EAAU,cAAc,EAAM,OAAQ,CAAG,CAC3F,EACA,MAAM,WAAY,CAChB,EAAK,UAAU,EACf,MAAM,EAAU,UAAU,CAC5B,CACF,CACF,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../controllers/hubApi.cjs"),t=require("../controllers/serverTelemetry.cjs"),n=(0,require("@agimon-ai/doompi-core/server-facet").defineServerPlugin)({name:`@agimon-ai/doompi-log`,global:{api:[e.api]},workspace:{api:[e.api]},session:()=>t.createServerTelemetry()});exports.default=n,exports.logServerFacet=n;
|
|
2
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.cjs","names":["defineServerPlugin","api","createServerTelemetry"],"sources":["../../src/extensions/server.ts"],"sourcesContent":["import { defineServerPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { api } from '../controllers/hubApi';\nimport { createServerTelemetry } from '../controllers/serverTelemetry';\nexport const logServerFacet = defineServerPlugin({\n name: '@agimon-ai/doompi-log',\n global: { api: [api] },\n workspace: { api: [api] },\n session: () => createServerTelemetry(),\n});\nexport default logServerFacet;\n"],"mappings":"4LAIa,GAAA,+CAAiBA,CAAAA,CAAAA,mBAAAA,CAAmB,CAC/C,KAAM,wBACN,OAAQ,CAAE,IAAK,CAACC,EAAAA,GAAG,CAAE,EACrB,UAAW,CAAE,IAAK,CAACA,EAAAA,GAAG,CAAE,EACxB,YAAeC,EAAAA,sBAAsB,CACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.cts","names":[],"sources":["../../src/extensions/server.ts"],"mappings":";qBAIa,8DAAc"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.mts","names":[],"sources":["../../src/extensions/server.ts"],"mappings":";qBAIa,8DAAc"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{api as e}from"../controllers/hubApi.mjs";import{createServerTelemetry as t}from"../controllers/serverTelemetry.mjs";import{defineServerPlugin as n}from"@agimon-ai/doompi-core/server-facet";const r=n({name:`@agimon-ai/doompi-log`,global:{api:[e]},workspace:{api:[e]},session:()=>t()});export{r as default,r as logServerFacet};
|
|
2
|
+
//# sourceMappingURL=server.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.mjs","names":[],"sources":["../../src/extensions/server.ts"],"sourcesContent":["import { defineServerPlugin } from '@agimon-ai/doompi-core/server-facet';\n\nimport { api } from '../controllers/hubApi';\nimport { createServerTelemetry } from '../controllers/serverTelemetry';\nexport const logServerFacet = defineServerPlugin({\n name: '@agimon-ai/doompi-log',\n global: { api: [api] },\n workspace: { api: [api] },\n session: () => createServerTelemetry(),\n});\nexport default logServerFacet;\n"],"mappings":"oMAIA,MAAa,EAAiB,EAAmB,CAC/C,KAAM,wBACN,OAAQ,CAAE,IAAK,CAAC,CAAG,CAAE,EACrB,UAAW,CAAE,IAAK,CAAC,CAAG,CAAE,EACxB,YAAe,EAAsB,CACvC,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./metrics.cjs"),t=require("./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./services/metrics/index.cjs"),t=require("./services/findings/index.cjs"),n=require("./tui/logMetricsOverlay.cjs"),r=require("./tui/logRuntime.cjs");exports.API_ERROR_RECORD=e.API_ERROR_RECORD,exports.LogMetricsAggregator=e.LogMetricsAggregator,exports.LogMetricsOverlayComponent=n.LogMetricsOverlayComponent,exports.MAX_RETAINED_METRIC_NAMES=e.MAX_RETAINED_METRIC_NAMES,exports.MAX_RETAINED_STATE_PER_TOOL=e.MAX_RETAINED_STATE_PER_TOOL,exports.MAX_RETAINED_TOKEN_BUCKETS=e.MAX_RETAINED_TOKEN_BUCKETS,exports.METRIC_OVERFLOW_NAME=e.METRIC_OVERFLOW_NAME,exports.TOOL_RESULT_RECORD=e.TOOL_RESULT_RECORD,exports.TOOL_TOKEN_SAMPLE_RECORD=e.TOOL_TOKEN_SAMPLE_RECORD,exports.TURN_FAILED_RECORD=e.TURN_FAILED_RECORD,exports.TURN_FINISHED_RECORD=e.TURN_FINISHED_RECORD,exports.deriveFindings=t.deriveFindings,exports.openLogMetricsOverlay=r.openLogMetricsOverlay;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { API_ERROR_RECORD, LogMetricsAggregator, LogMetricsAggregatorOptions, LogMetricsError, LogMetricsEventCount, LogMetricsOperationDuration, LogMetricsRecorder, LogMetricsSnapshot, LogMetricsTokenTotals, LogMetricsToolLatency, MAX_RETAINED_STATE_PER_TOOL, TOOL_RESULT_RECORD, TURN_FINISHED_RECORD } from "./metrics.cjs";
|
|
2
|
-
import {
|
|
3
|
-
import { PiTelemetryExtensionOptions
|
|
4
|
-
|
|
1
|
+
import { API_ERROR_RECORD, LogMetricsAggregator, LogMetricsAggregatorOptions, LogMetricsError, LogMetricsEventCount, LogMetricsOperationDuration, LogMetricsRecorder, LogMetricsSnapshot, LogMetricsTokenTotals, LogMetricsToolCost, LogMetricsToolLatency, MAX_RETAINED_METRIC_NAMES, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, METRIC_OVERFLOW_NAME, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD } from "./services/metrics/index.cjs";
|
|
2
|
+
import { SinkStatus } from "./types/sinkStatus.cjs";
|
|
3
|
+
import { PiTelemetryExtensionOptions } from "./types/piTelemetry.cjs";
|
|
4
|
+
import { LogMetricsOverlayComponent, LogMetricsView } from "./tui/logMetricsOverlay.cjs";
|
|
5
|
+
import { openLogMetricsOverlay } from "./tui/logRuntime.cjs";
|
|
6
|
+
import { LogMetricsFinding, LogMetricsFindingSeverity, deriveFindings } from "./services/findings/index.cjs";
|
|
7
|
+
export { API_ERROR_RECORD, LogMetricsAggregator, type LogMetricsAggregatorOptions, type LogMetricsError, type LogMetricsEventCount, type LogMetricsFinding, type LogMetricsFindingSeverity, type LogMetricsOperationDuration, LogMetricsOverlayComponent, type LogMetricsRecorder, type LogMetricsSnapshot, type LogMetricsTokenTotals, type LogMetricsToolCost, type LogMetricsToolLatency, type LogMetricsView, MAX_RETAINED_METRIC_NAMES, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, METRIC_OVERFLOW_NAME, type PiTelemetryExtensionOptions, type SinkStatus, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD, deriveFindings, openLogMetricsOverlay };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { API_ERROR_RECORD, LogMetricsAggregator, LogMetricsAggregatorOptions, LogMetricsError, LogMetricsEventCount, LogMetricsOperationDuration, LogMetricsRecorder, LogMetricsSnapshot, LogMetricsTokenTotals, LogMetricsToolLatency, MAX_RETAINED_STATE_PER_TOOL, TOOL_RESULT_RECORD, TURN_FINISHED_RECORD } from "./metrics.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { PiTelemetryExtensionOptions
|
|
4
|
-
|
|
1
|
+
import { API_ERROR_RECORD, LogMetricsAggregator, LogMetricsAggregatorOptions, LogMetricsError, LogMetricsEventCount, LogMetricsOperationDuration, LogMetricsRecorder, LogMetricsSnapshot, LogMetricsTokenTotals, LogMetricsToolCost, LogMetricsToolLatency, MAX_RETAINED_METRIC_NAMES, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, METRIC_OVERFLOW_NAME, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD } from "./services/metrics/index.mjs";
|
|
2
|
+
import { SinkStatus } from "./types/sinkStatus.mjs";
|
|
3
|
+
import { PiTelemetryExtensionOptions } from "./types/piTelemetry.mjs";
|
|
4
|
+
import { LogMetricsOverlayComponent, LogMetricsView } from "./tui/logMetricsOverlay.mjs";
|
|
5
|
+
import { openLogMetricsOverlay } from "./tui/logRuntime.mjs";
|
|
6
|
+
import { LogMetricsFinding, LogMetricsFindingSeverity, deriveFindings } from "./services/findings/index.mjs";
|
|
7
|
+
export { API_ERROR_RECORD, LogMetricsAggregator, type LogMetricsAggregatorOptions, type LogMetricsError, type LogMetricsEventCount, type LogMetricsFinding, type LogMetricsFindingSeverity, type LogMetricsOperationDuration, LogMetricsOverlayComponent, type LogMetricsRecorder, type LogMetricsSnapshot, type LogMetricsTokenTotals, type LogMetricsToolCost, type LogMetricsToolLatency, type LogMetricsView, MAX_RETAINED_METRIC_NAMES, MAX_RETAINED_STATE_PER_TOOL, MAX_RETAINED_TOKEN_BUCKETS, METRIC_OVERFLOW_NAME, type PiTelemetryExtensionOptions, type SinkStatus, TOOL_RESULT_RECORD, TOOL_TOKEN_SAMPLE_RECORD, TURN_FAILED_RECORD, TURN_FINISHED_RECORD, deriveFindings, openLogMetricsOverlay };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{API_ERROR_RECORD as e,LogMetricsAggregator as t,
|
|
1
|
+
import{API_ERROR_RECORD as e,LogMetricsAggregator as t,MAX_RETAINED_METRIC_NAMES as n,MAX_RETAINED_STATE_PER_TOOL as r,MAX_RETAINED_TOKEN_BUCKETS as i,METRIC_OVERFLOW_NAME as a,TOOL_RESULT_RECORD as o,TOOL_TOKEN_SAMPLE_RECORD as s,TURN_FAILED_RECORD as c,TURN_FINISHED_RECORD as l}from"./services/metrics/index.mjs";import{deriveFindings as u}from"./services/findings/index.mjs";import{LogMetricsOverlayComponent as d}from"./tui/logMetricsOverlay.mjs";import{openLogMetricsOverlay as f}from"./tui/logRuntime.mjs";export{e as API_ERROR_RECORD,t as LogMetricsAggregator,d as LogMetricsOverlayComponent,n as MAX_RETAINED_METRIC_NAMES,r as MAX_RETAINED_STATE_PER_TOOL,i as MAX_RETAINED_TOKEN_BUCKETS,a as METRIC_OVERFLOW_NAME,o as TOOL_RESULT_RECORD,s as TOOL_TOKEN_SAMPLE_RECORD,c as TURN_FAILED_RECORD,l as TURN_FINISHED_RECORD,u as deriveFindings,f as openLogMetricsOverlay};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./tui/logMetricsOverlay.cjs");exports.LogMetricsOverlayComponent=e.LogMetricsOverlayComponent;
|