@robota-sdk/agent-session-analytics 3.0.0-beta.100
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/CHANGELOG.md +382 -0
- package/LICENSE +661 -0
- package/README.md +76 -0
- package/dist/node/index.cjs +5 -0
- package/dist/node/index.d.cts +251 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +251 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +6 -0
- package/dist/node/index.js.map +1 -0
- package/package.json +77 -0
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# @robota-sdk/agent-session-analytics
|
|
2
|
+
|
|
3
|
+
Timing and usage analysis over persisted agent session records.
|
|
4
|
+
|
|
5
|
+
Given session records, it computes per-turn timing intervals (LLM wait vs. tool execution), attributes
|
|
6
|
+
token usage and cost to the source that spent it (main thread, subagents, background tasks, tools,
|
|
7
|
+
commands, skills), aggregates cross-session personal usage into
|
|
8
|
+
complete local-calendar days, projects usage into OpenTelemetry (OTLP) payloads, and renders text
|
|
9
|
+
reports. Everything is a pure function: no file I/O, no `process.*`, no network. Callers load the
|
|
10
|
+
records and write or send the output; a host CLI's `usage` command is one such caller.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @robota-sdk/agent-session-analytics
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import {
|
|
22
|
+
analyzeSession,
|
|
23
|
+
formatSingleSession,
|
|
24
|
+
formatUsageReport,
|
|
25
|
+
summarizeUsageBySource,
|
|
26
|
+
} from '@robota-sdk/agent-session-analytics';
|
|
27
|
+
import type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';
|
|
28
|
+
|
|
29
|
+
declare const record: IInteractiveSessionRecord; // loaded by the caller
|
|
30
|
+
|
|
31
|
+
process.stdout.write(formatSingleSession(analyzeSession(record)) + '\n');
|
|
32
|
+
process.stdout.write(formatUsageReport(summarizeUsageBySource(record)) + '\n');
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## API
|
|
36
|
+
|
|
37
|
+
### Timing
|
|
38
|
+
|
|
39
|
+
| Export | Description |
|
|
40
|
+
| --------------------------- | ---------------------------------------------------- |
|
|
41
|
+
| `analyzeSession(record)` | Compute the timing report for one session record |
|
|
42
|
+
| `aggregateReports(reports)` | Aggregate multiple single-session reports |
|
|
43
|
+
| `computeTimingIntervals(h)` | Classify a history array into timing intervals |
|
|
44
|
+
| `gapMs(from, to)` | Millisecond gap between two timestamps (string/Date) |
|
|
45
|
+
| `formatSingleSession(r)` | Render a single-session report as text |
|
|
46
|
+
| `formatAggregateReport(a)` | Render an aggregate report as text |
|
|
47
|
+
|
|
48
|
+
### Usage and cost
|
|
49
|
+
|
|
50
|
+
| Export | Description |
|
|
51
|
+
| ----------------------------------- | ----------------------------------------------------------------------------------- |
|
|
52
|
+
| `summarizeUsageBySource(record)` | Token and cost totals per source, the top consumer, and the per-turn span timeline |
|
|
53
|
+
| `formatUsageReport(report)` | Render the per-source usage report as text |
|
|
54
|
+
| `summarizePersonalUsage(snapshot)` | Build a deterministic 7- or 30-day personal-usage report from an immutable snapshot |
|
|
55
|
+
| `formatPersonalUsageReport(report)` | Render the personal-usage report as text |
|
|
56
|
+
|
|
57
|
+
### OTLP export
|
|
58
|
+
|
|
59
|
+
These build OTLP/JSON payloads from session records; sending them to a collector is the caller's job
|
|
60
|
+
(a host CLI's `usage export` command does it). The payloads are content-free: no prompt or reply text, no session
|
|
61
|
+
identity.
|
|
62
|
+
|
|
63
|
+
| Export | Description |
|
|
64
|
+
| ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
|
|
65
|
+
| `createOtlpUsageSnapshot(records, at, version)` | Usage metrics as Gauges, so re-exporting a snapshot never counts the same usage twice |
|
|
66
|
+
| `createOtlpPromptRootTraces(records, version)` | Prompt-root trace spans, only for roots whose identity validates; returns coverage counts for what it skipped |
|
|
67
|
+
| `createOtlpPromptEvents(records, version, observedAt)` | One outcome-only completion log event per accepted span |
|
|
68
|
+
|
|
69
|
+
Unknown cost or token splits stay visible as unknown rather than being reported as zero.
|
|
70
|
+
|
|
71
|
+
The record and history types are the canonical `IInteractiveSessionRecord` and `IHistoryEntry`; this
|
|
72
|
+
package defines no duplicates. The usage and trace report types (`IUsageBySourceReport`,
|
|
73
|
+
`IUsageSourceTotals`, `IRunTraceSpan`, `IRunTraceTurn`) are owned by
|
|
74
|
+
`@robota-sdk/agent-interface-analytics` and re-exported here for convenience.
|
|
75
|
+
|
|
76
|
+
See [docs/SPEC.md](./docs/SPEC.md) for the full contract.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@robota-sdk/agent-core");function t(e,t){return new Date(t).getTime()-new Date(e).getTime()}function n(e){return new Date(e).toISOString()}function r(e,r,i,a,o,s){return{kind:e,fromType:r,toType:i,fromTimestamp:n(a),toTimestamp:n(o),durationMs:t(a,o),turnIndex:s}}function i(e){let t=[],n=0,i=0;for(;i<e.length;){let a=e[i];if(!a){i++;continue}if(a.category===`chat`&&a.type===`user`){n++;let o=a.timestamp,s=!1,c=null,l=i+1;for(;l<e.length;){let i=e[l];if(!i){l++;continue}if(i.category===`chat`&&i.type===`user`)break;if(i.category===`event`&&i.type===`tool-start`){s?c!==null&&t.push(r(`llm_between_tools`,`tool-end`,`tool-start`,c,i.timestamp,n)):(t.push(r(`user_to_first_tool`,`user`,`tool-start`,o,i.timestamp,n)),s=!0),c=null,l++;continue}if(i.category===`event`&&i.type===`tool-end`){let a=e[l-1];a&&a.category===`event`&&a.type===`tool-start`&&t.push(r(`tool_exec`,`tool-start`,`tool-end`,a.timestamp,i.timestamp,n)),c=i.timestamp,l++;continue}if(i.category===`event`&&i.type===`tool-summary`){c!==null&&t.push(r(`llm_final_response`,`tool-end`,`tool-summary`,c,i.timestamp,n)),l++;continue}if(i.category===`chat`&&i.type===`assistant`){s?c!==null&&t.push(r(`llm_final_response`,`tool-end`,`assistant`,c,i.timestamp,n)):t.push(r(`user_to_assistant`,`user`,`assistant`,o,i.timestamp,n)),l++;break}l++}i=l}else i++}return t}function a(e){let t=e.filter(e=>e.kind===`user_to_first_tool`||e.kind===`user_to_assistant`||e.kind===`llm_between_tools`||e.kind===`llm_final_response`),n=e.filter(e=>e.kind===`tool_exec`),r=e.filter(e=>e.kind===`user_to_assistant`),i=e=>e.length?Math.round(e.reduce((e,t)=>e+t.durationMs,0)/e.length):0,a=e=>e.length?Math.max(...e.map(e=>e.durationMs)):0;return{llmWaitMs:{avg:i(t),max:a(t),total:t.reduce((e,t)=>e+t.durationMs,0),count:t.length},toolExecMs:{avg:i(n),max:a(n),median:(e=>{if(!e.length)return 0;let t=[...e].sort((e,t)=>e.durationMs-t.durationMs),n=Math.floor(t.length/2);return t.length%2==0?Math.round(((t[n-1]?.durationMs??0)+(t[n]?.durationMs??0))/2):t[n]?.durationMs??0})(n),count:n.length},userToAssistantMs:{avg:i(r),max:a(r),count:r.length}}}function o(e){let t=i(e.history??[]),n=t.filter(e=>e.durationMs>=1e4);return{sessionId:e.id,cwd:e.cwd,createdAt:e.createdAt,totalIntervals:t.length,intervals:t,slowIntervals:n,stats:a(t)}}function s(e){let t=e.flatMap(e=>e.intervals.filter(e=>e.kind===`user_to_first_tool`||e.kind===`user_to_assistant`||e.kind===`llm_between_tools`||e.kind===`llm_final_response`)),n=e.flatMap(e=>e.intervals.filter(e=>e.kind===`tool_exec`)),r=t.length?Math.round(t.reduce((e,t)=>e+t.durationMs,0)/t.length):0,i=n.length?Math.round(n.reduce((e,t)=>e+t.durationMs,0)/n.length):0,a=0,o=``,s=0,c=``;for(let t of e)for(let e of t.intervals)e.durationMs>a&&(a=e.durationMs,o=t.sessionId,s=e.turnIndex,c=e.kind);let l=e.map(e=>e.createdAt).sort();return{sessionCount:e.length,fromDate:l[0]??``,toDate:l[l.length-1]??``,avgLlmResponseMs:r,avgToolExecMs:i,maxSingleDelayMs:a,maxSingleDelaySession:o,maxSingleDelayTurn:s,maxSingleDelayKind:c}}function c(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:`${e}`}function l(e,t){return`${t?``:`~`}$${e.toFixed(4)}`}function u(e){let t=[];if(t.push(`Token usage — session ${e.sessionId}`),t.push(` total ${c(e.totalTokens)} (prompt ${c(e.promptTokens)} · completion ${c(e.completionTokens)})`),t.push(` cost ${l(e.costUsd,e.costExact)}`),e.bySource.length===0)t.push(` (no usage recorded)`);else{t.push(` by source (most tokens first):`);for(let n of e.bySource)t.push(` ${n.label.padEnd(24)} ${String(n.percentage).padStart(5)}% ${c(n.totalTokens).padStart(7)} ${l(n.costUsd,n.costExact).padStart(10)} (${n.turns} turn${n.turns===1?``:`s`})`);e.topConsumer&&t.push(` top consumer: ${e.topConsumer.label} (${e.topConsumer.percentage}%)`)}if(e.timeline.length>0){t.push(` trace (per turn, sub-turn spans):`);for(let n of e.timeline){t.push(` turn ${n.turnIndex} — ${n.label} · ${d(n.totalDurationMs)} across ${n.spans.length} op${n.spans.length===1?``:`s`}`);for(let e of n.spans)t.push(` ${e.op.padEnd(22)} ${d(e.durationMs).padStart(8)}`)}}return t.join(`
|
|
2
|
+
`)}function d(e){return e>=1e3?`${(e/1e3).toFixed(1)}s`:`${e}ms`}function f(e){return new Date(e).toLocaleString(`sv-SE`,{timeZoneName:`short`}).slice(0,16)}function p(e){return{user_to_first_tool:`user → tool-start`,user_to_assistant:`user → assistant`,tool_exec:`tool-start → tool-end`,llm_between_tools:`tool-end → tool-start`,llm_final_response:`tool-end → assistant/summary`}[e.kind]??e.kind}function m(e){let{stats:t}=e,n=t.llmWaitMs.total+t.toolExecMs.avg*t.toolExecMs.count;if(n===0)return`Verdict: No timing data available.`;let r=Math.round(t.llmWaitMs.total/n*100);return`Verdict: LLM API wait ${r}% | Code processing ${100-r}%`}function h(e){let t=[],{stats:n}=e;if(t.push(`Session: ${e.sessionId}`),t.push(`Created: ${f(e.createdAt)} | cwd: ${e.cwd}`),t.push(``),t.push(`Timing Summary:`),n.llmWaitMs.count>0&&t.push(` LLM API wait ${d(n.llmWaitMs.avg).padStart(8)} avg | ${d(n.llmWaitMs.max).padStart(8)} max`),n.toolExecMs.count>0&&t.push(` Tool execution ${d(n.toolExecMs.avg).padStart(8)} avg | ${d(n.toolExecMs.median).padStart(8)} median`),n.userToAssistantMs.count>0&&t.push(` user → assistant ${d(n.userToAssistantMs.avg).padStart(8)} avg`),n.llmWaitMs.count===0&&n.toolExecMs.count===0&&t.push(` (no intervals computed — session may have no history)`),e.slowIntervals.length>0){t.push(``),t.push(`Slow intervals (>10s):`);for(let n of e.slowIntervals)t.push(` turn ${n.turnIndex} ${p(n).padEnd(32)} ${d(n.durationMs).padStart(8)}`)}return t.push(``),t.push(m(e)),t.join(`
|
|
3
|
+
`)}function g(e){let t=[];return t.push(`Analyzed ${e.sessionCount} sessions (${f(e.fromDate)} ~ ${f(e.toDate)})`),t.push(` Avg LLM response: ${d(e.avgLlmResponseMs)}`),t.push(` Avg tool exec: ${d(e.avgToolExecMs)}`),e.maxSingleDelayMs>0&&t.push(` Max single delay: ${d(e.maxSingleDelayMs)} (${e.maxSingleDelaySession}, turn ${e.maxSingleDelayTurn}, ${e.maxSingleDelayKind})`),t.join(`
|
|
4
|
+
`)}const _={scope:`main`,label:`main thread`};function v(e){return`${e.scope}:${e.id??``}`}function y(e){return e.label??(e.id?`${e.scope} ${e.id}`:e.scope)}function ee(e){return e.type===`usage-summary`&&typeof e.data==`object`&&e.data!==null}function te(e){if(e.type!==`span`||typeof e.data!=`object`||e.data===null)return!1;let t=e.data;return typeof t.spanId==`string`&&typeof t.op==`string`&&typeof t.durationMs==`number`}function b(e,t){return t<=0?0:Math.round(e/t*1e3)/10}function x(e){let t=new Map,n=0,r=0,i=0,a=0,o=!0,s=[],c=[],l=e=>{c.length!==0&&(s.push({turnIndex:s.length,source:e,label:y(e),spans:c,totalDurationMs:c.reduce((e,t)=>e+t.durationMs,0)}),c=[])};for(let s of e.history??[]){if(te(s)){let e=s.data;c.push({spanId:e.spanId,op:e.op,durationMs:e.durationMs});continue}if(!ee(s))continue;let e=s.data,u=e.source??_,d=v(u),f=e.promptTokens??0,p=e.completionTokens??0,m=e.totalTokens,h=e.costUsd??0;n+=f,r+=p,i+=m,a+=h,o=!1;let g=t.get(d);g?(g.promptTokens+=f,g.completionTokens+=p,g.totalTokens+=m,g.turns+=1,g.costUsd+=h,g.costExact=!1):t.set(d,{key:d,source:u,label:y(u),promptTokens:f,completionTokens:p,totalTokens:m,turns:1,percentage:0,costUsd:h,costExact:!1}),l(u)}l(_);let u=[...t.values()].map(e=>({...e,percentage:b(e.totalTokens,i)})).sort((e,t)=>t.totalTokens-e.totalTokens);return{sessionId:e.id,totalTokens:i,promptTokens:n,completionTokens:r,costUsd:a,costExact:o,bySource:u,...u[0]?{topConsumer:u[0]}:{},timeline:s}}function S(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&!(e instanceof Date)}function C(e){return typeof e==`string`&&e.length>0?e:void 0}function w(e){return typeof e==`number`&&Number.isFinite(e)&&e>=0}function T(e){return e===`unknown`||e===`estimated`||e===`exact`}function ne(e){return(e.promptTokens===void 0||w(e.promptTokens))&&(e.completionTokens===void 0||w(e.completionTokens))}function re(e){let t=e.costStatus;return T(t)?t===`unknown`?e.costUsd===void 0:w(e.costUsd):!1}function ie(e){return w(e.totalTokens)&&w(e.contextUsedTokens)&&w(e.contextMaxTokens)&&w(e.contextUsedPercentage)}function ae(e){return e===`cli`||e===`desktop-app`||e===`browser`||e===`remote`||e===`attach`||e===`unknown`}function E(e){if(!S(e))return;let t=e.scope;if(t!==`main`&&t!==`subagent`&&t!==`background`&&t!==`tool`&&t!==`command`&&t!==`skill`)return;let n=C(e.id),r=C(e.label);return{scope:t,...n?{id:n}:{},...r?{label:r}:{}}}function D(e){if(!S(e)||e.kind!==`exact`&&e.kind!==`estimated`||e.scope!==`turn`||!ne(e)||!re(e)||!ie(e)||!T(e.costStatus))return;let t=E(e.source);return{kind:e.kind,scope:`turn`,totalTokens:e.totalTokens,contextUsedTokens:e.contextUsedTokens,contextMaxTokens:e.contextMaxTokens,contextUsedPercentage:e.contextUsedPercentage,costStatus:e.costStatus===`exact`?`estimated`:e.costStatus,...w(e.promptTokens)?{promptTokens:e.promptTokens}:{},...w(e.completionTokens)?{completionTokens:e.completionTokens}:{},...w(e.costUsd)?{costUsd:e.costUsd}:{},...t?{source:t}:{}}}function O(e){if(!S(e))return;let t=C(e.providerId),n=C(e.modelId),{promptTokens:r,completionTokens:i,totalTokens:a,costStatus:o,costUsd:s}=e;if(!(!t||!n||!w(r)||!w(i)||!w(a)||o!==`unknown`&&o!==`estimated`||(o===`estimated`?!w(s):s!==void 0)))return{providerId:t,modelId:n,promptTokens:r,completionTokens:i,totalTokens:a,costStatus:o,...o===`estimated`&&w(s)?{costUsd:s}:{}}}function k(e){if(!Array.isArray(e)||e.length===0)return;let t=e.map(O);return t.every(e=>e!==void 0)?t:void 0}function A(e){if(!S(e))return;let t=C(e.usageObservationId),n=C(e.turnId),r=e.outcome;if(!t||!n||r!==`success`&&r!==`failure`&&r!==`interrupted`)return;let i=e.surface,a=E(e.source),o=D(e.usage),s=k(e.modelShares);return{usageObservationId:t,turnId:n,outcome:r,...C(e.modelId)?{modelId:C(e.modelId)}:{},...C(e.providerId)?{providerId:C(e.providerId)}:{},...s?{modelShares:s}:{},...ae(i)?{surface:i}:{},...a?{source:a}:{},...o?{usage:o}:{}}}function j(e){let t=new Date(e.timestamp);return Number.isNaN(t.getTime())?void 0:t}function M(e){let t=[],n=e.history??[],r=new Set,i=n.flatMap((e,t)=>{if(e.type!==`usage-summary`)return[];let n=D(e.data);return n?[{id:e.id,index:t,usage:n}]:[]});for(let[e,t]of n.entries()){if(t.type!==`usage-observation`)continue;let n=A(t.data);if(!n?.usage)continue;let a=JSON.stringify(n.usage),o=i.map((e,t)=>({candidate:e,index:t})).filter(({candidate:e})=>JSON.stringify(e.usage)===a).sort((t,n)=>Math.abs(t.candidate.index-e)-Math.abs(n.candidate.index-e))[0];o&&(r.add(o.candidate.id),i.splice(o.index,1))}for(let i of n){let n=j(i);if(!n)continue;let a=i.type===`usage-observation`?A(i.data):void 0;if(a){t.push({id:a.usageObservationId,sessionId:e.id,at:n,legacy:!1,observation:a});continue}if(i.type!==`usage-summary`||r.has(i.id))continue;let o=D(i.data);if(!o)continue;let s=`legacy:${e.id}:${i.id}`;t.push({id:s,sessionId:e.id,at:n,legacy:!0,observation:{usageObservationId:s,turnId:`legacy:${i.id}`,outcome:`success`,usage:o,...o.source?{source:o.source}:{},surface:`unknown`}})}return t}function N(e){let t=[];for(let n of e.history??[]){let r=n.data;if(n.type!==`tool-start`||!S(r))continue;let i=C(r.toolName),a=j(n);!i||!a||t.push({id:`tool:${e.id}:${n.id}`,sessionId:e.id,key:`tool:${i}`,label:i,kind:`tool`,at:a})}for(let n of e.skillActivationEvents??[]){if(n.status!==`started`)continue;let r=new Date(n.timestamp);Number.isNaN(r.getTime())||t.push({id:`activation:${e.id}:${n.timestamp}:${n.source}:${n.qualifiedName??n.skillName}:${n.invocation}`,sessionId:e.id,key:n.qualifiedName??`${n.source}:${n.skillName}`,label:n.qualifiedName??n.skillName,kind:n.source,at:r})}return t}function P(){return{sessions:new Set,observations:0,turns:0,promptTokens:0,completionTokens:0,totalTokens:0,costUsd:0,hasUnknownCost:!1,hasEstimatedCost:!1,unpricedTurns:0}}function F(e,t){let n=new Intl.DateTimeFormat(`en-CA`,{timeZone:t,year:`numeric`,month:`2-digit`,day:`2-digit`}).formatToParts(e),r=e=>n.find(t=>t.type===e)?.value??``;return`${r(`year`)}-${r(`month`)}-${r(`day`)}`}function oe(e,t,n){let[r,i,a]=F(e,t).split(`-`).map(Number),o=new Date(Date.UTC(r,i-1,a));return Array.from({length:n},(e,t)=>{let r=new Date(o);return r.setUTCDate(r.getUTCDate()-(n-t-1)),r.toISOString().slice(0,10)})}function se(e){let t=new Map;for(let n of e){let e=t.get(n.key);t.set(n.key,{item:n,count:(e?.count??0)+1})}return[...t.values()].map(({item:e,count:t})=>({key:e.key,label:e.label,kind:e.kind,count:t})).sort((e,t)=>t.count-e.count||e.key.localeCompare(t.key))}function I(e,t){let n=t.observation.usage;e.sessions.add(t.sessionId),e.observations+=1;let r=!t.legacy&&t.observation.source?.scope!==`tool`;r&&(e.turns+=1),e.promptTokens+=n?.promptTokens??0,e.completionTokens+=n?.completionTokens??0,e.totalTokens+=n?.totalTokens??0,e.costUsd+=n?.costUsd??0,n?.costStatus===`estimated`?e.hasEstimatedCost=!0:(e.hasUnknownCost=!0,r&&(e.unpricedTurns+=1))}function L(e,t){let n=[];for(let r of e){let e=r.observation.modelShares;if(e===void 0){n.push(r);continue}let i=new Map;for(let n of e){let e=t(n),a=i.get(e)?.observation.usage,o=r.observation.usage,s=n.costStatus===`unknown`||a?.costStatus===`unknown`?`unknown`:`estimated`;i.set(e,{...r,observation:{...r.observation,providerId:n.providerId,modelId:n.modelId,usage:{kind:o?.kind??`estimated`,scope:`turn`,contextUsedTokens:o?.contextUsedTokens??0,contextMaxTokens:o?.contextMaxTokens??0,contextUsedPercentage:o?.contextUsedPercentage??0,promptTokens:(a?.promptTokens??0)+n.promptTokens,completionTokens:(a?.completionTokens??0)+n.completionTokens,totalTokens:(a?.totalTokens??0)+n.totalTokens,costStatus:s,...s===`estimated`&&{costUsd:(a?.costUsd??0)+(n.costUsd??0)}}}})}n.push(...i.values())}return z(n,e=>t(e.observation))}function R(e){return{sessions:e.sessions.size,turns:e.turns,promptTokens:e.promptTokens,completionTokens:e.completionTokens,totalTokens:e.totalTokens,costUsd:e.costUsd,costStatus:e.hasEstimatedCost?`estimated`:`unknown`,...e.unpricedTurns>0?{unpricedTurns:e.unpricedTurns}:{}}}function z(e,t){let n=new Map;for(let r of e){let e=t(r),i=n.get(e)??P();I(i,r),n.set(e,i)}return[...n.entries()].map(([e,t])=>({key:e,label:e,frozen:R(t),sessionIds:[...t.sessions].sort()})).map(({key:e,label:t,frozen:n,sessionIds:r})=>({key:e,label:t,turns:n.turns,promptTokens:n.promptTokens,completionTokens:n.completionTokens,totalTokens:n.totalTokens,costUsd:n.costUsd,costStatus:n.costStatus,sessionIds:r})).sort((e,t)=>t.totalTokens-e.totalTokens||e.key.localeCompare(t.key))}function B(e){return e?e.id?`${e.scope}:${e.id}`:e.scope:`unknown`}function V(e,t,n){return e.getTime()<=t.now.getTime()&&n.has(F(e,t.request.timezone))}function H(e,t){let n=[],r=[],i=new Set,a=new Set,o=0,s=0;for(let c of e.records){for(let r of M(c))if(V(r.at,e,t)){if(i.has(r.id)){o+=1;continue}i.add(r.id),n.push(r),r.legacy&&(s+=1)}for(let n of N(c))!V(n.at,e,t)||a.has(n.id)||(a.add(n.id),r.push(n))}return{observations:n,activities:r,duplicateObservations:o,legacyObservations:s}}function U(e){let t=new Map,n=(e,n)=>{let r=n.getTime(),i=t.get(e);(i===void 0||r<i)&&t.set(e,r)};for(let t of e.observations)n(t.sessionId,t.at);for(let t of e.activities)n(t.sessionId,t.at);let r={};for(let[e,n]of t)r[e]=new Date(n).toISOString();return r}function W(e,t,n){let r=P(),i=new Map(t.map(e=>[e,P()]));for(let t of n.observations)I(r,t),I(i.get(F(t.at,e.request.timezone)),t);for(let t of n.activities)r.sessions.add(t.sessionId),i.get(F(t.at,e.request.timezone))?.sessions.add(t.sessionId);return{totals:r,byDay:i}}function G(e){let t=e.request.period===`30d`?30:7,n=oe(e.now,e.request.timezone,t),r=H(e,new Set(n)),{totals:i,byDay:a}=W(e,n,r),o=r.observations;return{schemaVersion:1,generatedAt:e.now.toISOString(),period:e.request.period,timezone:e.request.timezone,interval:{startDate:n[0],endDate:n.at(-1)},totals:R(i),daily:n.map((e,t)=>({date:e,partial:t===n.length-1,totals:R(a.get(e)),sessionIds:[...a.get(e).sessions].sort()})),byModel:L(o,e=>e.modelId??`unknown`),byProvider:L(o,e=>e.providerId??`unknown`),bySurface:z(o,e=>e.observation.surface??`unknown`),bySource:z(o,e=>B(e.observation.source)),byActivity:se(r.activities),sessionIds:[...i.sessions].sort(),sessionFirstSeen:U(r),coverage:{validSessions:e.records.length,corruptSessions:e.corruptSessionIds?.length??0,unsupportedSessions:e.unsupportedSessionIds?.length??0,duplicateObservations:r.duplicateObservations,legacyObservations:r.legacyObservations,unknownModelObservations:o.filter(e=>!e.observation.modelId&&!e.observation.modelShares).length,unknownProviderObservations:o.filter(e=>!e.observation.providerId&&!e.observation.modelShares).length,unknownSurfaceObservations:o.filter(e=>!e.observation.surface||e.observation.surface===`unknown`).length,corruptSessionIds:[...e.corruptSessionIds??[]].sort(),unsupportedSessionIds:[...e.unsupportedSessionIds??[]].sort()}}}const ce=(1n<<64n)-1n,K=/^(?!0{32}$)[0-9a-f]{32}$/,q=/^(?!0{16}$)[0-9a-f]{16}$/,le=[`promptExecutionStartedAt`,`promptExecutionEndedAt`,`promptExecutionOutcome`,`promptExecutionTraceId`,`promptExecutionSpanId`];function J(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Y(e,t){e.set(t,(e.get(t)??0)+1)}function X(e){if(typeof e!=`string`)return;let t=new Date(e);if(Number.isNaN(t.getTime())||t.toISOString()!==e)return;let n=BigInt(t.getTime())*1000000n;return n>=0n&&n<=ce?n:void 0}function ue(e){let t=X(e.promptExecutionStartedAt),n=X(e.promptExecutionEndedAt),r=e.promptExecutionOutcome,i=e.promptExecutionTraceId,a=e.promptExecutionSpanId;if(!(t===void 0||n===void 0||t>n||r!==`success`&&r!==`failure`&&r!==`interrupted`||typeof i!=`string`||!K.test(i)||typeof a!=`string`||!q.test(a)||typeof e.usageObservationId!=`string`||e.usageObservationId.length===0||e.turnId!==e.usageObservationId||e.outcome!==`success`&&e.outcome!==`failure`&&e.outcome!==`interrupted`))return{traceId:i,spanId:a,name:`agent.prompt_execution`,kind:1,startTimeUnixNano:String(t),endTimeUnixNano:String(n),attributes:[{key:`agent.prompt.outcome`,value:{stringValue:r}}],status:{code:r===`success`?1:r===`failure`?2:0}}}function de(e,t){if(!J(e))return;let n=e.traceId,r=e.parentSpanId,i=e.spanId,a=X(e.startedAt),o=X(e.endedAt),s=e.outcome;if(!(typeof n!=`string`||!K.test(n)||typeof r!=`string`||!q.test(r)||typeof i!=`string`||!q.test(i)||a===void 0||o===void 0||a>o||s!==`success`&&s!==`failure`&&s!==`interrupted`||t===`provider`&&(!Number.isSafeInteger(e.round)||e.round<1)))return{traceId:n,parentSpanId:r,spanId:i,name:t===`provider`?`agent.provider_call`:`agent.tool_body`,kind:1,startTimeUnixNano:String(a),endTimeUnixNano:String(o),attributes:[{key:t===`provider`?`agent.provider.outcome`:`agent.tool.outcome`,value:{stringValue:s}}],status:{code:s===`success`?1:s===`failure`?2:0}}}function fe(e){if(!J(e))return;let t=e.traceId,n=e.spanId;return typeof t==`string`&&K.test(t)&&typeof n==`string`&&q.test(n)?`${t}:${n}`:void 0}function Z(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=0}function pe(t,n,r,i){if(!J(n)||n.disposition===void 0)return r.legacy+=1,t;if(n.disposition!==`invoked`)return r.invalid+=1,t;i.invoked+=1;let a=n.usageProvenance;if(a===`partial`)return r.partial+=1,i.unknownCost+=1,t;if(a===`absent`||a===void 0)return r.absent+=1,i.unknownCost+=1,t;let o=n.promptTokens,s=n.completionTokens,c=n.totalTokens;if(a!==`complete`||!Z(o)||!Z(s)||!Number.isSafeInteger(o+s)||c!==o+s)return r.invalid+=1,i.unknownCost+=1,t;i.completeUsage+=1,i.inputTokens+=o,i.outputTokens+=s;let l=[...t.attributes,{key:`agent.provider.usage.input_tokens`,value:{intValue:String(o)}},{key:`agent.provider.usage.output_tokens`,value:{intValue:String(s)}}],u=n.modelId,d=typeof u==`string`&&u.length>0&&u.length<=128&&[...u].every(e=>e.charCodeAt(0)>=32)?(0,e.calculateModelCost)(u,o,s):void 0;return d===void 0||!Number.isFinite(d)||d<0?i.unknownCost+=1:(i.estimatedCostUsd+=d,typeof u==`string`&&Object.hasOwn(e.MODEL_PRICES,u)?i.exactPriceMatches+=1:i.familyPriceMatches+=1,l.push({key:`agent.provider.cost.usd.estimated`,value:{doubleValue:d}})),{...t,attributes:l}}function Q(e,t,n){let r=[],i=new Map,a=new Map;for(let t of e)for(let e of t.history??[]){if(e.type!==`usage-observation`)continue;let n=e.data,o=J(n)?n.usageObservationId:void 0,s=J(n)?n.promptExecutionTraceId:void 0,c=typeof o==`string`&&o.length>0?JSON.stringify([t.id,o]):void 0,l=typeof s==`string`&&K.test(s)?s:void 0;c&&Y(i,c),l&&Y(a,l),r.push({record:t,data:n,observationKey:c,traceId:l})}let o=[],s=new Map,c=0,l=0,u=0;for(let e of r){if(e.observationKey&&(i.get(e.observationKey)??0)>1||e.traceId&&(a.get(e.traceId)??0)>1){u+=1;continue}let t=e.data;if(!J(t)){l+=1;continue}if(le.every(e=>t[e]===void 0)){c+=1;continue}let n=ue(t);if(!n){l+=1;continue}o.push(n);let r=s.get(e.record)??new Map;r.set(n.traceId,n),s.set(e.record,r)}let d=[],f=new Map;for(let t of e)for(let e of t.history??[]){let n=e.type===`provider-call-trace`?`provider`:e.type===`tool-body-trace`?`tool`:void 0;if(!n)continue;let r=fe(e.data),i=de(e.data,n);r&&Y(f,r),d.push({record:t,span:i,identity:r,kind:n,data:e.data})}let p={provider:{exported:0,invalid:0,orphaned:0,duplicate:0},tool:{exported:0,invalid:0,orphaned:0,duplicate:0}},m={legacy:0,invalid:0,partial:0,absent:0,nonInvoked:0},h={invoked:0,completeUsage:0,inputTokens:0,outputTokens:0,estimatedCostUsd:0,unknownCost:0,exactPriceMatches:0,familyPriceMatches:0};for(let e of d){let{span:t}=e,n=p[e.kind];if(e.identity&&(f.get(e.identity)??0)>1){n.duplicate+=1;continue}if(!t){n.invalid+=1;continue}let r=s.get(e.record)?.get(t.traceId);if(!r||t.parentSpanId!==r.spanId||t.spanId===r.spanId){n.orphaned+=1;continue}if(BigInt(t.startTimeUnixNano)<BigInt(r.startTimeUnixNano)||BigInt(t.endTimeUnixNano)>BigInt(r.endTimeUnixNano)){n.invalid+=1;continue}if(e.kind===`provider`&&J(e.data)&&(e.data.disposition===`cache-hit`||e.data.disposition===`preflight-refused`)){m.nonInvoked+=1,(e.data.promptTokens!==void 0||e.data.completionTokens!==void 0||e.data.totalTokens!==void 0||e.data.usageProvenance===`complete`)&&(m.invalid+=1);continue}o.push(e.kind===`provider`?pe(t,e.data,m,h):t),n.exported+=1}return{payload:{resourceSpans:o.length?[{resource:{attributes:[{key:`service.name`,value:{stringValue:n}},{key:`service.version`,value:{stringValue:t}}]},scopeSpans:[{scope:{name:`@robota-sdk/agent-session-analytics`},spans:o}]}]:[]},coverage:{exported:o.length-p.provider.exported-p.tool.exported,missing:c,invalid:l,duplicate:u,providerChildren:p.provider,providerUsage:m,toolChildren:p.tool},callMetrics:h}}function me(e,t,n,r){let i=new Set,a=0,o=0,s=0,c=0,l=0,u=0,d=0,f=0;for(let t of e)for(let e of M(t)){if(i.has(e.id))continue;i.add(e.id),e.legacy||(a+=1);let t=e.observation.usage;if(!t){d+=1,l+=1;continue}o+=t.totalTokens,s+=t.promptTokens??0,c+=t.completionTokens??0,(t.promptTokens===void 0||t.completionTokens===void 0)&&(l+=1),t.costStatus===`unknown`||t.costUsd===void 0?d+=1:(u+=t.costUsd,t.costStatus===`estimated`&&(f+=1))}let p=Q(e,n,r),m=p.callMetrics,h=String(BigInt(t.getTime())*1000000n),g=(e,t,n)=>({name:e,unit:t,gauge:{dataPoints:[{timeUnixNano:h,asDouble:n}]}});return{resourceMetrics:[{resource:{attributes:[{key:`service.name`,value:{stringValue:r}},{key:`service.version`,value:{stringValue:n}}]},scopeMetrics:[{scope:{name:`@robota-sdk/agent-session-analytics`},metrics:[g(`agent.session.count`,`{session}`,e.length),g(`agent.turn.count`,`{turn}`,a),g(`agent.token.total`,`{token}`,o),g(`agent.token.input.known`,`{token}`,s),g(`agent.token.output.known`,`{token}`,c),g(`agent.token.split_unknown_observations`,`{observation}`,l),g(`agent.cost.usd.known`,`USD`,u),g(`agent.cost.unknown_observations`,`{observation}`,d),g(`agent.cost.estimated_observations`,`{observation}`,f),g(`agent.provider_call.count`,`{call}`,m.invoked),g(`agent.provider_call.usage.complete_count`,`{call}`,m.completeUsage),g(`agent.provider_call.token.input.known`,`{token}`,m.inputTokens),g(`agent.provider_call.token.output.known`,`{token}`,m.outputTokens),g(`agent.provider_call.cost.usd.estimated`,`USD`,m.estimatedCostUsd),g(`agent.provider_call.cost.unknown_count`,`{call}`,m.unknownCost),g(`agent.provider_call.usage.partial_count`,`{call}`,p.coverage.providerUsage.partial),g(`agent.provider_call.usage.invalid_count`,`{call}`,p.coverage.providerUsage.invalid),g(`agent.provider_call.usage.legacy_count`,`{call}`,p.coverage.providerUsage.legacy),g(`agent.provider_call.price.exact_id_count`,`{call}`,m.exactPriceMatches),g(`agent.provider_call.price.family_fallback_count`,`{call}`,m.familyPriceMatches)]}]}]}}const he=(1n<<64n)-1n;function ge(e){let t=e.getTime();if(!Number.isSafeInteger(t)||t<0)throw RangeError(`OTLP log observation time must be a valid post-epoch date.`);let n=BigInt(t)*1000000n;if(n>he)throw RangeError(`OTLP log observation time is out of range.`);return String(n)}function _e(e,t,n,r){let i=Q(e,t,r),a=i.payload.resourceSpans[0]?.scopeSpans[0]?.spans??[];if(a.length===0)return{exported:0,coverage:i.coverage,payload:{resourceLogs:[]}};let o=ge(n),s=a.map(e=>{let t=e.attributes.find(e=>e.key===`agent.prompt.outcome`||e.key===`agent.provider.outcome`||e.key===`agent.tool.outcome`),n=e.name===`agent.prompt_execution`?`agent.prompt_execution.completed`:e.name===`agent.provider_call`?`agent.provider_call.completed`:`agent.tool_body.completed`,r=e.status.code===1?9:e.status.code===2?17:13,i=r===9?`INFO`:r===17?`ERROR`:`WARN`;return{timeUnixNano:e.endTimeUnixNano,observedTimeUnixNano:o,severityNumber:r,severityText:i,eventName:n,traceId:e.traceId,spanId:e.spanId,attributes:t&&`stringValue`in t.value?[{key:t.key,value:{stringValue:t.value.stringValue}}]:[]}});return{exported:s.length,coverage:i.coverage,payload:{resourceLogs:[{resource:i.payload.resourceSpans[0].resource,scopeLogs:[{scope:{name:`@robota-sdk/agent-session-analytics`},logRecords:s}]}]}}}function $(e,t){return[``,e,...t.length===0?[`(none)`]:t.map(e=>`${e.label} ${e.turns} turns ${e.totalTokens} tokens cost ${e.costStatus===`unknown`?`unknown`:`$${e.costUsd.toFixed(4)}`}`)]}function ve(e){let t=e.totals.costStatus===`unknown`?`unknown`:`$${e.totals.costUsd.toFixed(4)} (${e.totals.costStatus})`;return[`Personal usage — ${e.period} (${e.timezone})`,`${e.interval.startDate} → ${e.interval.endDate}`,`Sessions ${e.totals.sessions} · Turns ${e.totals.turns} · Tokens ${e.totals.totalTokens} (${e.totals.promptTokens} input / ${e.totals.completionTokens} output) · Cost ${t}`,``,`Daily usage`,...e.daily.map(e=>`${e.date}${e.partial?` (partial)`:``} ${e.totals.turns} turns ${e.totals.totalTokens} tokens`),...$(`By model`,e.byModel),...$(`By provider`,e.byProvider),...$(`By surface`,e.bySurface),...$(`By source`,e.bySource),``,`Activity`,...e.byActivity.length===0?[`(none)`]:e.byActivity.map(e=>`${e.kind}:${e.label} ${e.count} calls`),``,`Coverage: legacy ${e.coverage.legacyObservations}, corrupt ${e.coverage.corruptSessions}, unsupported ${e.coverage.unsupportedSessions}, duplicates ${e.coverage.duplicateObservations}`,`Local estimates from recorded sessions; costs are not an invoice.`].join(`
|
|
5
|
+
`)}exports.aggregateReports=s,exports.analyzeSession=o,exports.computeTimingIntervals=i,exports.createOtlpPromptEvents=_e,exports.createOtlpPromptRootTraces=Q,exports.createOtlpUsageSnapshot=me,exports.formatAggregateReport=g,exports.formatPersonalUsageReport=ve,exports.formatSingleSession=h,exports.formatUsageReport=u,exports.gapMs=t,exports.summarizePersonalUsage=G,exports.summarizeUsageBySource=x;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { IInteractiveSessionRecord } from "@robota-sdk/agent-interface-session";
|
|
2
|
+
import { IHistoryEntry } from "@robota-sdk/agent-core";
|
|
3
|
+
import { IPersonalUsageReport, IPersonalUsageReport as IPersonalUsageReport$1, IPersonalUsageRequest, IPersonalUsageRequest as IPersonalUsageRequest$1, IRunTraceSpan, IRunTraceTurn, IUsageBySourceReport, IUsageBySourceReport as IUsageBySourceReport$1, IUsageSourceTotals } from "@robota-sdk/agent-interface-analytics";
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
/** Minimal session-record projection the analyzer reads — SSOT-derived, not a duplicate. */
|
|
6
|
+
type TSessionAnalysisInput = Pick<IInteractiveSessionRecord, 'id' | 'cwd' | 'createdAt' | 'history'>;
|
|
7
|
+
type TIntervalKind = 'user_to_first_tool' | 'user_to_assistant' | 'tool_exec' | 'llm_between_tools' | 'llm_final_response';
|
|
8
|
+
interface ITimingInterval {
|
|
9
|
+
kind: TIntervalKind;
|
|
10
|
+
fromType: string;
|
|
11
|
+
toType: string;
|
|
12
|
+
fromTimestamp: string;
|
|
13
|
+
toTimestamp: string;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
turnIndex: number;
|
|
16
|
+
}
|
|
17
|
+
interface ITimingStats {
|
|
18
|
+
llmWaitMs: {
|
|
19
|
+
avg: number;
|
|
20
|
+
max: number;
|
|
21
|
+
total: number;
|
|
22
|
+
count: number;
|
|
23
|
+
};
|
|
24
|
+
toolExecMs: {
|
|
25
|
+
avg: number;
|
|
26
|
+
max: number;
|
|
27
|
+
median: number;
|
|
28
|
+
count: number;
|
|
29
|
+
};
|
|
30
|
+
userToAssistantMs: {
|
|
31
|
+
avg: number;
|
|
32
|
+
max: number;
|
|
33
|
+
count: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
interface ISessionTimingReport {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
cwd: string;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
totalIntervals: number;
|
|
41
|
+
intervals: ITimingInterval[];
|
|
42
|
+
slowIntervals: ITimingInterval[];
|
|
43
|
+
stats: ITimingStats;
|
|
44
|
+
}
|
|
45
|
+
interface IAggregateReport {
|
|
46
|
+
sessionCount: number;
|
|
47
|
+
fromDate: string;
|
|
48
|
+
toDate: string;
|
|
49
|
+
avgLlmResponseMs: number;
|
|
50
|
+
avgToolExecMs: number;
|
|
51
|
+
maxSingleDelayMs: number;
|
|
52
|
+
maxSingleDelaySession: string;
|
|
53
|
+
maxSingleDelayTurn: number;
|
|
54
|
+
maxSingleDelayKind: string;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/analyze.d.ts
|
|
58
|
+
/** Gap in milliseconds between two timestamps (ISO string or Date). */
|
|
59
|
+
declare function gapMs(from: string | Date, to: string | Date): number;
|
|
60
|
+
/**
|
|
61
|
+
* Compute timing intervals from a history entry array.
|
|
62
|
+
*
|
|
63
|
+
* Strategy: scan the timeline turn by turn. A "turn" starts with a user message. After user, we
|
|
64
|
+
* expect zero or more tool-start → tool-end cycles, optionally a tool-summary, then an assistant
|
|
65
|
+
* message.
|
|
66
|
+
*/
|
|
67
|
+
declare function computeTimingIntervals(history: readonly IHistoryEntry[]): ITimingInterval[];
|
|
68
|
+
/** Compute the timing report for a single session record. */
|
|
69
|
+
declare function analyzeSession(record: TSessionAnalysisInput): ISessionTimingReport;
|
|
70
|
+
/** Aggregate multiple single-session reports into a fleet-level summary. */
|
|
71
|
+
declare function aggregateReports(reports: readonly ISessionTimingReport[]): IAggregateReport;
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/usage.d.ts
|
|
74
|
+
/** SSOT-derived projection the usage reducer reads. */
|
|
75
|
+
type TUsageAnalysisInput = Pick<IInteractiveSessionRecord, 'id' | 'history'>;
|
|
76
|
+
/**
|
|
77
|
+
* Aggregate a session's `usage-summary` entries into a per-source token+cost breakdown AND assemble
|
|
78
|
+
* the per-operation span timeline (SELFHOST-004).
|
|
79
|
+
*
|
|
80
|
+
* Token/cost: usage snapshots with no `source` are attributed to the main thread; sources are grouped
|
|
81
|
+
* by `<scope>:<id>` and sorted by total tokens descending with the top consumer surfaced. Cost sums
|
|
82
|
+
* each turn's known `IUsageSnapshot.costUsd` (unpriced turns contribute 0). Historical `exact`
|
|
83
|
+
* snapshots were local price-table estimates, so they cannot certify billed cost.
|
|
84
|
+
*
|
|
85
|
+
* Timeline: history is a chronological timeline where each turn ends with its `usage-summary` entry,
|
|
86
|
+
* so the `span` entries that appear before a given usage-summary are the sub-turn spans of THAT turn.
|
|
87
|
+
* The reducer walks in order, buffering spans, and flushes the buffer as the turn's spans at each
|
|
88
|
+
* usage-summary boundary — grouping sub-turn spans under their owning turn. Spans after the last
|
|
89
|
+
* usage-summary (an in-progress turn) form a trailing timeline entry. Pure: no I/O, stays within the
|
|
90
|
+
* declared `agent-core`, `agent-interface-analytics` and `agent-interface-session` dependencies
|
|
91
|
+
* (NO `agent-plugin` read — enforcement lives elsewhere).
|
|
92
|
+
*/
|
|
93
|
+
declare function summarizeUsageBySource(input: TUsageAnalysisInput): IUsageBySourceReport$1;
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/report.d.ts
|
|
96
|
+
/**
|
|
97
|
+
* ANALYTICS-001 + SELFHOST-004: render a per-source token+cost breakdown and the per-operation span
|
|
98
|
+
* timeline — which part of the session burned the most tokens/cost, and where the wall-clock went.
|
|
99
|
+
* Pure; the caller owns writing it (the headless CLI path for the TUI/GUI trace/cost view).
|
|
100
|
+
*/
|
|
101
|
+
declare function formatUsageReport(report: IUsageBySourceReport): string;
|
|
102
|
+
declare function formatSingleSession(report: ISessionTimingReport): string;
|
|
103
|
+
declare function formatAggregateReport(aggregate: IAggregateReport): string;
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/personal-usage.d.ts
|
|
106
|
+
interface IPersonalUsageSnapshot {
|
|
107
|
+
readonly request: IPersonalUsageRequest$1;
|
|
108
|
+
readonly now: Date;
|
|
109
|
+
readonly records: readonly IInteractiveSessionRecord[];
|
|
110
|
+
readonly corruptSessionIds?: readonly string[];
|
|
111
|
+
readonly unsupportedSessionIds?: readonly string[];
|
|
112
|
+
}
|
|
113
|
+
declare function summarizePersonalUsage(input: IPersonalUsageSnapshot): IPersonalUsageReport$1;
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/otlp-usage-snapshot.d.ts
|
|
116
|
+
/** A non-additive, content-free snapshot. Consumers must read the latest Gauge, not sum exports. */
|
|
117
|
+
declare function createOtlpUsageSnapshot(records: readonly IInteractiveSessionRecord[], at: Date, version: string, serviceName: string): {
|
|
118
|
+
resourceMetrics: unknown[];
|
|
119
|
+
};
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/otlp-prompt-root-traces.d.ts
|
|
122
|
+
interface IOtlpSpan {
|
|
123
|
+
readonly traceId: string;
|
|
124
|
+
readonly spanId: string;
|
|
125
|
+
readonly parentSpanId?: string;
|
|
126
|
+
readonly name: 'agent.prompt_execution' | 'agent.provider_call' | 'agent.tool_body';
|
|
127
|
+
readonly kind: 1;
|
|
128
|
+
readonly startTimeUnixNano: string;
|
|
129
|
+
readonly endTimeUnixNano: string;
|
|
130
|
+
readonly attributes: readonly {
|
|
131
|
+
readonly key: string;
|
|
132
|
+
readonly value: {
|
|
133
|
+
readonly stringValue: string;
|
|
134
|
+
} | {
|
|
135
|
+
readonly intValue: string;
|
|
136
|
+
} | {
|
|
137
|
+
readonly doubleValue: number;
|
|
138
|
+
};
|
|
139
|
+
}[];
|
|
140
|
+
readonly status: {
|
|
141
|
+
readonly code: number;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
interface IOtlpPromptRootTraces {
|
|
145
|
+
readonly resourceSpans: readonly {
|
|
146
|
+
readonly resource: {
|
|
147
|
+
readonly attributes: readonly {
|
|
148
|
+
readonly key: string;
|
|
149
|
+
readonly value: {
|
|
150
|
+
readonly stringValue: string;
|
|
151
|
+
};
|
|
152
|
+
}[];
|
|
153
|
+
};
|
|
154
|
+
readonly scopeSpans: readonly {
|
|
155
|
+
readonly scope: {
|
|
156
|
+
readonly name: string;
|
|
157
|
+
};
|
|
158
|
+
readonly spans: readonly IOtlpSpan[];
|
|
159
|
+
}[];
|
|
160
|
+
}[];
|
|
161
|
+
}
|
|
162
|
+
interface IPromptRootTraceCoverage {
|
|
163
|
+
readonly exported: number;
|
|
164
|
+
readonly missing: number;
|
|
165
|
+
readonly invalid: number;
|
|
166
|
+
readonly duplicate: number;
|
|
167
|
+
readonly providerChildren: {
|
|
168
|
+
readonly exported: number;
|
|
169
|
+
readonly invalid: number;
|
|
170
|
+
readonly orphaned: number;
|
|
171
|
+
readonly duplicate: number;
|
|
172
|
+
};
|
|
173
|
+
readonly providerUsage: {
|
|
174
|
+
readonly legacy: number;
|
|
175
|
+
readonly invalid: number;
|
|
176
|
+
readonly partial: number;
|
|
177
|
+
readonly absent: number;
|
|
178
|
+
readonly nonInvoked: number;
|
|
179
|
+
};
|
|
180
|
+
readonly toolChildren: {
|
|
181
|
+
readonly exported: number;
|
|
182
|
+
readonly invalid: number;
|
|
183
|
+
readonly orphaned: number;
|
|
184
|
+
readonly duplicate: number;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
interface IAcceptedProviderCallMetrics {
|
|
188
|
+
readonly invoked: number;
|
|
189
|
+
readonly completeUsage: number;
|
|
190
|
+
readonly inputTokens: number;
|
|
191
|
+
readonly outputTokens: number;
|
|
192
|
+
readonly estimatedCostUsd: number;
|
|
193
|
+
readonly unknownCost: number;
|
|
194
|
+
readonly exactPriceMatches: number;
|
|
195
|
+
readonly familyPriceMatches: number;
|
|
196
|
+
}
|
|
197
|
+
/** Project only proven canonical prompt roots; never infer spans from legacy usage or tool events. */
|
|
198
|
+
declare function createOtlpPromptRootTraces(records: readonly IInteractiveSessionRecord[], version: string, serviceName: string): {
|
|
199
|
+
readonly payload: IOtlpPromptRootTraces;
|
|
200
|
+
readonly coverage: IPromptRootTraceCoverage;
|
|
201
|
+
readonly callMetrics: IAcceptedProviderCallMetrics;
|
|
202
|
+
};
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/otlp-prompt-events.d.ts
|
|
205
|
+
type TEventName = 'agent.prompt_execution.completed' | 'agent.provider_call.completed' | 'agent.tool_body.completed';
|
|
206
|
+
interface IOtlpLogRecord {
|
|
207
|
+
readonly timeUnixNano: string;
|
|
208
|
+
readonly observedTimeUnixNano: string;
|
|
209
|
+
readonly severityNumber: 9 | 13 | 17;
|
|
210
|
+
readonly severityText: 'INFO' | 'WARN' | 'ERROR';
|
|
211
|
+
readonly eventName: TEventName;
|
|
212
|
+
readonly traceId: string;
|
|
213
|
+
readonly spanId: string;
|
|
214
|
+
readonly attributes: readonly {
|
|
215
|
+
readonly key: string;
|
|
216
|
+
readonly value: {
|
|
217
|
+
readonly stringValue: string;
|
|
218
|
+
};
|
|
219
|
+
}[];
|
|
220
|
+
}
|
|
221
|
+
interface IOtlpPromptEvents {
|
|
222
|
+
readonly resourceLogs: readonly {
|
|
223
|
+
readonly resource: {
|
|
224
|
+
readonly attributes: readonly {
|
|
225
|
+
readonly key: string;
|
|
226
|
+
readonly value: {
|
|
227
|
+
readonly stringValue: string;
|
|
228
|
+
};
|
|
229
|
+
}[];
|
|
230
|
+
};
|
|
231
|
+
readonly scopeLogs: readonly {
|
|
232
|
+
readonly scope: {
|
|
233
|
+
readonly name: string;
|
|
234
|
+
};
|
|
235
|
+
readonly logRecords: readonly IOtlpLogRecord[];
|
|
236
|
+
}[];
|
|
237
|
+
}[];
|
|
238
|
+
}
|
|
239
|
+
/** Explicit local OTLP log projection of canonical prompt completion events. */
|
|
240
|
+
declare function createOtlpPromptEvents(records: readonly IInteractiveSessionRecord[], version: string, observedAt: Date, serviceName: string): {
|
|
241
|
+
readonly exported: number;
|
|
242
|
+
readonly coverage: IPromptRootTraceCoverage;
|
|
243
|
+
readonly payload: IOtlpPromptEvents;
|
|
244
|
+
};
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/personal-usage-format.d.ts
|
|
247
|
+
/** Render the provider-neutral personal-usage report for the terminal surface. */
|
|
248
|
+
declare function formatPersonalUsageReport(report: IPersonalUsageReport$1): string;
|
|
249
|
+
//#endregion
|
|
250
|
+
export { type IAggregateReport, type IPersonalUsageReport, type IPersonalUsageRequest, type IPersonalUsageSnapshot, type IRunTraceSpan, type IRunTraceTurn, type ISessionTimingReport, type ITimingInterval, type ITimingStats, type IUsageBySourceReport, type IUsageSourceTotals, type TIntervalKind, type TSessionAnalysisInput, type TUsageAnalysisInput, aggregateReports, analyzeSession, computeTimingIntervals, createOtlpPromptEvents, createOtlpPromptRootTraces, createOtlpUsageSnapshot, formatAggregateReport, formatPersonalUsageReport, formatSingleSession, formatUsageReport, gapMs, summarizePersonalUsage, summarizeUsageBySource };
|
|
251
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/types.ts","../../src/analyze.ts","../../src/usage.ts","../../src/report.ts","../../src/personal-usage.ts","../../src/otlp-usage-snapshot.ts","../../src/otlp-prompt-root-traces.ts","../../src/otlp-prompt-events.ts","../../src/personal-usage-format.ts"],"mappings":";;;;;KAWY,wBAAwB,KAClC;KAIU;UAOK;EACf,MAAM;EACN;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;IAAa;IAAa;IAAa;IAAe;;EACtD;IAAc;IAAa;IAAa;IAAgB;;EACxD;IAAqB;IAAa;IAAa;;;UAGhC;EACf;EACA;EACA;EACA;EACA,WAAW;EACX,eAAe;EACf,OAAO;;UAGQ;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;iBCtCc,MAAM,eAAe,MAAM,aAAa;;;;;;;;iBAkCxC,uBAAuB,kBAAkB,kBAAkB;;iBA6K3D,eAAe,QAAQ,wBAAwB;;iBAiB/C,iBAAiB,kBAAkB,yBAAyB;;;;KC9MhE,sBAAsB,KAAK;;;;;;;;;;;;;;;;;;iBAqDvB,uBAAuB,OAAO,sBAAsB;;;;;;;;iBCnEpD,kBAAkB,QAAQ;iBAiE1B,oBAAoB,QAAQ;iBA0C5B,sBAAsB,WAAW;;;UC9GhC;WACN,SAAS;WACT,KAAK;WACL,kBAAkB;WAClB;WACA;;iBAgRK,uBAAuB,OAAO,yBAAyB;;;;iBCpSvD,wBACd,kBAAkB,6BAClB,IAAI,MACJ,iBACA;EACG;;;;UCWK;WACC;WACA;WACA;WACA;WACA;WACA;WACA;WACA;aACE;aACA;eAAkB;;eAAmC;;eAAgC;;;WAEvF;aAAmB;;;UAGb;WACN;aACE;eACE;iBACE;iBACA;mBAAkB;;;;aAGtB;eACE;iBAAkB;;eAClB,gBAAgB;;;;UAKd;WACN;WACA;WACA;WACA;WACA;aACE;aACA;aACA;aACA;;WAEF;aACE;aACA;aACA;aACA;aACA;;WAEF;aACE;aACA;aACA;aACA;;;UAII;WACN;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;iBA6KK,2BACd,kBAAkB,6BAClB,iBACA;WACY,SAAS;WAAgC,UAAU;WAAmC,aAAa;;;;KClQ5G;UAKK;WACC;WACA;WACA;WACA;WACA,WAAW;WACX;WACA;WACA;aACE;aACA;eAAkB;;;;UAId;WACN;aACE;eACE;iBACE;iBACA;mBAAkB;;;;aAGtB;eACE;iBAAkB;;eAClB,qBAAqB;;;;;iBAkBpB,uBACd,kBAAkB,6BAClB,iBACA,YAAY,MACZ;WAES;WACA,UAAU;WACV,SAAS;;;;;iBCvCJ,0BAA0B,QAAQ"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { IHistoryEntry } from "@robota-sdk/agent-core";
|
|
2
|
+
import { IInteractiveSessionRecord } from "@robota-sdk/agent-interface-session";
|
|
3
|
+
import { IPersonalUsageReport, IPersonalUsageReport as IPersonalUsageReport$1, IPersonalUsageRequest, IPersonalUsageRequest as IPersonalUsageRequest$1, IRunTraceSpan, IRunTraceTurn, IUsageBySourceReport, IUsageBySourceReport as IUsageBySourceReport$1, IUsageSourceTotals } from "@robota-sdk/agent-interface-analytics";
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
/** Minimal session-record projection the analyzer reads — SSOT-derived, not a duplicate. */
|
|
6
|
+
type TSessionAnalysisInput = Pick<IInteractiveSessionRecord, 'id' | 'cwd' | 'createdAt' | 'history'>;
|
|
7
|
+
type TIntervalKind = 'user_to_first_tool' | 'user_to_assistant' | 'tool_exec' | 'llm_between_tools' | 'llm_final_response';
|
|
8
|
+
interface ITimingInterval {
|
|
9
|
+
kind: TIntervalKind;
|
|
10
|
+
fromType: string;
|
|
11
|
+
toType: string;
|
|
12
|
+
fromTimestamp: string;
|
|
13
|
+
toTimestamp: string;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
turnIndex: number;
|
|
16
|
+
}
|
|
17
|
+
interface ITimingStats {
|
|
18
|
+
llmWaitMs: {
|
|
19
|
+
avg: number;
|
|
20
|
+
max: number;
|
|
21
|
+
total: number;
|
|
22
|
+
count: number;
|
|
23
|
+
};
|
|
24
|
+
toolExecMs: {
|
|
25
|
+
avg: number;
|
|
26
|
+
max: number;
|
|
27
|
+
median: number;
|
|
28
|
+
count: number;
|
|
29
|
+
};
|
|
30
|
+
userToAssistantMs: {
|
|
31
|
+
avg: number;
|
|
32
|
+
max: number;
|
|
33
|
+
count: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
interface ISessionTimingReport {
|
|
37
|
+
sessionId: string;
|
|
38
|
+
cwd: string;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
totalIntervals: number;
|
|
41
|
+
intervals: ITimingInterval[];
|
|
42
|
+
slowIntervals: ITimingInterval[];
|
|
43
|
+
stats: ITimingStats;
|
|
44
|
+
}
|
|
45
|
+
interface IAggregateReport {
|
|
46
|
+
sessionCount: number;
|
|
47
|
+
fromDate: string;
|
|
48
|
+
toDate: string;
|
|
49
|
+
avgLlmResponseMs: number;
|
|
50
|
+
avgToolExecMs: number;
|
|
51
|
+
maxSingleDelayMs: number;
|
|
52
|
+
maxSingleDelaySession: string;
|
|
53
|
+
maxSingleDelayTurn: number;
|
|
54
|
+
maxSingleDelayKind: string;
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
//#region src/analyze.d.ts
|
|
58
|
+
/** Gap in milliseconds between two timestamps (ISO string or Date). */
|
|
59
|
+
declare function gapMs(from: string | Date, to: string | Date): number;
|
|
60
|
+
/**
|
|
61
|
+
* Compute timing intervals from a history entry array.
|
|
62
|
+
*
|
|
63
|
+
* Strategy: scan the timeline turn by turn. A "turn" starts with a user message. After user, we
|
|
64
|
+
* expect zero or more tool-start → tool-end cycles, optionally a tool-summary, then an assistant
|
|
65
|
+
* message.
|
|
66
|
+
*/
|
|
67
|
+
declare function computeTimingIntervals(history: readonly IHistoryEntry[]): ITimingInterval[];
|
|
68
|
+
/** Compute the timing report for a single session record. */
|
|
69
|
+
declare function analyzeSession(record: TSessionAnalysisInput): ISessionTimingReport;
|
|
70
|
+
/** Aggregate multiple single-session reports into a fleet-level summary. */
|
|
71
|
+
declare function aggregateReports(reports: readonly ISessionTimingReport[]): IAggregateReport;
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/usage.d.ts
|
|
74
|
+
/** SSOT-derived projection the usage reducer reads. */
|
|
75
|
+
type TUsageAnalysisInput = Pick<IInteractiveSessionRecord, 'id' | 'history'>;
|
|
76
|
+
/**
|
|
77
|
+
* Aggregate a session's `usage-summary` entries into a per-source token+cost breakdown AND assemble
|
|
78
|
+
* the per-operation span timeline (SELFHOST-004).
|
|
79
|
+
*
|
|
80
|
+
* Token/cost: usage snapshots with no `source` are attributed to the main thread; sources are grouped
|
|
81
|
+
* by `<scope>:<id>` and sorted by total tokens descending with the top consumer surfaced. Cost sums
|
|
82
|
+
* each turn's known `IUsageSnapshot.costUsd` (unpriced turns contribute 0). Historical `exact`
|
|
83
|
+
* snapshots were local price-table estimates, so they cannot certify billed cost.
|
|
84
|
+
*
|
|
85
|
+
* Timeline: history is a chronological timeline where each turn ends with its `usage-summary` entry,
|
|
86
|
+
* so the `span` entries that appear before a given usage-summary are the sub-turn spans of THAT turn.
|
|
87
|
+
* The reducer walks in order, buffering spans, and flushes the buffer as the turn's spans at each
|
|
88
|
+
* usage-summary boundary — grouping sub-turn spans under their owning turn. Spans after the last
|
|
89
|
+
* usage-summary (an in-progress turn) form a trailing timeline entry. Pure: no I/O, stays within the
|
|
90
|
+
* declared `agent-core`, `agent-interface-analytics` and `agent-interface-session` dependencies
|
|
91
|
+
* (NO `agent-plugin` read — enforcement lives elsewhere).
|
|
92
|
+
*/
|
|
93
|
+
declare function summarizeUsageBySource(input: TUsageAnalysisInput): IUsageBySourceReport$1;
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/report.d.ts
|
|
96
|
+
/**
|
|
97
|
+
* ANALYTICS-001 + SELFHOST-004: render a per-source token+cost breakdown and the per-operation span
|
|
98
|
+
* timeline — which part of the session burned the most tokens/cost, and where the wall-clock went.
|
|
99
|
+
* Pure; the caller owns writing it (the headless CLI path for the TUI/GUI trace/cost view).
|
|
100
|
+
*/
|
|
101
|
+
declare function formatUsageReport(report: IUsageBySourceReport): string;
|
|
102
|
+
declare function formatSingleSession(report: ISessionTimingReport): string;
|
|
103
|
+
declare function formatAggregateReport(aggregate: IAggregateReport): string;
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/personal-usage.d.ts
|
|
106
|
+
interface IPersonalUsageSnapshot {
|
|
107
|
+
readonly request: IPersonalUsageRequest$1;
|
|
108
|
+
readonly now: Date;
|
|
109
|
+
readonly records: readonly IInteractiveSessionRecord[];
|
|
110
|
+
readonly corruptSessionIds?: readonly string[];
|
|
111
|
+
readonly unsupportedSessionIds?: readonly string[];
|
|
112
|
+
}
|
|
113
|
+
declare function summarizePersonalUsage(input: IPersonalUsageSnapshot): IPersonalUsageReport$1;
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/otlp-usage-snapshot.d.ts
|
|
116
|
+
/** A non-additive, content-free snapshot. Consumers must read the latest Gauge, not sum exports. */
|
|
117
|
+
declare function createOtlpUsageSnapshot(records: readonly IInteractiveSessionRecord[], at: Date, version: string, serviceName: string): {
|
|
118
|
+
resourceMetrics: unknown[];
|
|
119
|
+
};
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/otlp-prompt-root-traces.d.ts
|
|
122
|
+
interface IOtlpSpan {
|
|
123
|
+
readonly traceId: string;
|
|
124
|
+
readonly spanId: string;
|
|
125
|
+
readonly parentSpanId?: string;
|
|
126
|
+
readonly name: 'agent.prompt_execution' | 'agent.provider_call' | 'agent.tool_body';
|
|
127
|
+
readonly kind: 1;
|
|
128
|
+
readonly startTimeUnixNano: string;
|
|
129
|
+
readonly endTimeUnixNano: string;
|
|
130
|
+
readonly attributes: readonly {
|
|
131
|
+
readonly key: string;
|
|
132
|
+
readonly value: {
|
|
133
|
+
readonly stringValue: string;
|
|
134
|
+
} | {
|
|
135
|
+
readonly intValue: string;
|
|
136
|
+
} | {
|
|
137
|
+
readonly doubleValue: number;
|
|
138
|
+
};
|
|
139
|
+
}[];
|
|
140
|
+
readonly status: {
|
|
141
|
+
readonly code: number;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
interface IOtlpPromptRootTraces {
|
|
145
|
+
readonly resourceSpans: readonly {
|
|
146
|
+
readonly resource: {
|
|
147
|
+
readonly attributes: readonly {
|
|
148
|
+
readonly key: string;
|
|
149
|
+
readonly value: {
|
|
150
|
+
readonly stringValue: string;
|
|
151
|
+
};
|
|
152
|
+
}[];
|
|
153
|
+
};
|
|
154
|
+
readonly scopeSpans: readonly {
|
|
155
|
+
readonly scope: {
|
|
156
|
+
readonly name: string;
|
|
157
|
+
};
|
|
158
|
+
readonly spans: readonly IOtlpSpan[];
|
|
159
|
+
}[];
|
|
160
|
+
}[];
|
|
161
|
+
}
|
|
162
|
+
interface IPromptRootTraceCoverage {
|
|
163
|
+
readonly exported: number;
|
|
164
|
+
readonly missing: number;
|
|
165
|
+
readonly invalid: number;
|
|
166
|
+
readonly duplicate: number;
|
|
167
|
+
readonly providerChildren: {
|
|
168
|
+
readonly exported: number;
|
|
169
|
+
readonly invalid: number;
|
|
170
|
+
readonly orphaned: number;
|
|
171
|
+
readonly duplicate: number;
|
|
172
|
+
};
|
|
173
|
+
readonly providerUsage: {
|
|
174
|
+
readonly legacy: number;
|
|
175
|
+
readonly invalid: number;
|
|
176
|
+
readonly partial: number;
|
|
177
|
+
readonly absent: number;
|
|
178
|
+
readonly nonInvoked: number;
|
|
179
|
+
};
|
|
180
|
+
readonly toolChildren: {
|
|
181
|
+
readonly exported: number;
|
|
182
|
+
readonly invalid: number;
|
|
183
|
+
readonly orphaned: number;
|
|
184
|
+
readonly duplicate: number;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
interface IAcceptedProviderCallMetrics {
|
|
188
|
+
readonly invoked: number;
|
|
189
|
+
readonly completeUsage: number;
|
|
190
|
+
readonly inputTokens: number;
|
|
191
|
+
readonly outputTokens: number;
|
|
192
|
+
readonly estimatedCostUsd: number;
|
|
193
|
+
readonly unknownCost: number;
|
|
194
|
+
readonly exactPriceMatches: number;
|
|
195
|
+
readonly familyPriceMatches: number;
|
|
196
|
+
}
|
|
197
|
+
/** Project only proven canonical prompt roots; never infer spans from legacy usage or tool events. */
|
|
198
|
+
declare function createOtlpPromptRootTraces(records: readonly IInteractiveSessionRecord[], version: string, serviceName: string): {
|
|
199
|
+
readonly payload: IOtlpPromptRootTraces;
|
|
200
|
+
readonly coverage: IPromptRootTraceCoverage;
|
|
201
|
+
readonly callMetrics: IAcceptedProviderCallMetrics;
|
|
202
|
+
};
|
|
203
|
+
//#endregion
|
|
204
|
+
//#region src/otlp-prompt-events.d.ts
|
|
205
|
+
type TEventName = 'agent.prompt_execution.completed' | 'agent.provider_call.completed' | 'agent.tool_body.completed';
|
|
206
|
+
interface IOtlpLogRecord {
|
|
207
|
+
readonly timeUnixNano: string;
|
|
208
|
+
readonly observedTimeUnixNano: string;
|
|
209
|
+
readonly severityNumber: 9 | 13 | 17;
|
|
210
|
+
readonly severityText: 'INFO' | 'WARN' | 'ERROR';
|
|
211
|
+
readonly eventName: TEventName;
|
|
212
|
+
readonly traceId: string;
|
|
213
|
+
readonly spanId: string;
|
|
214
|
+
readonly attributes: readonly {
|
|
215
|
+
readonly key: string;
|
|
216
|
+
readonly value: {
|
|
217
|
+
readonly stringValue: string;
|
|
218
|
+
};
|
|
219
|
+
}[];
|
|
220
|
+
}
|
|
221
|
+
interface IOtlpPromptEvents {
|
|
222
|
+
readonly resourceLogs: readonly {
|
|
223
|
+
readonly resource: {
|
|
224
|
+
readonly attributes: readonly {
|
|
225
|
+
readonly key: string;
|
|
226
|
+
readonly value: {
|
|
227
|
+
readonly stringValue: string;
|
|
228
|
+
};
|
|
229
|
+
}[];
|
|
230
|
+
};
|
|
231
|
+
readonly scopeLogs: readonly {
|
|
232
|
+
readonly scope: {
|
|
233
|
+
readonly name: string;
|
|
234
|
+
};
|
|
235
|
+
readonly logRecords: readonly IOtlpLogRecord[];
|
|
236
|
+
}[];
|
|
237
|
+
}[];
|
|
238
|
+
}
|
|
239
|
+
/** Explicit local OTLP log projection of canonical prompt completion events. */
|
|
240
|
+
declare function createOtlpPromptEvents(records: readonly IInteractiveSessionRecord[], version: string, observedAt: Date, serviceName: string): {
|
|
241
|
+
readonly exported: number;
|
|
242
|
+
readonly coverage: IPromptRootTraceCoverage;
|
|
243
|
+
readonly payload: IOtlpPromptEvents;
|
|
244
|
+
};
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/personal-usage-format.d.ts
|
|
247
|
+
/** Render the provider-neutral personal-usage report for the terminal surface. */
|
|
248
|
+
declare function formatPersonalUsageReport(report: IPersonalUsageReport$1): string;
|
|
249
|
+
//#endregion
|
|
250
|
+
export { type IAggregateReport, type IPersonalUsageReport, type IPersonalUsageRequest, type IPersonalUsageSnapshot, type IRunTraceSpan, type IRunTraceTurn, type ISessionTimingReport, type ITimingInterval, type ITimingStats, type IUsageBySourceReport, type IUsageSourceTotals, type TIntervalKind, type TSessionAnalysisInput, type TUsageAnalysisInput, aggregateReports, analyzeSession, computeTimingIntervals, createOtlpPromptEvents, createOtlpPromptRootTraces, createOtlpUsageSnapshot, formatAggregateReport, formatPersonalUsageReport, formatSingleSession, formatUsageReport, gapMs, summarizePersonalUsage, summarizeUsageBySource };
|
|
251
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/types.ts","../../src/analyze.ts","../../src/usage.ts","../../src/report.ts","../../src/personal-usage.ts","../../src/otlp-usage-snapshot.ts","../../src/otlp-prompt-root-traces.ts","../../src/otlp-prompt-events.ts","../../src/personal-usage-format.ts"],"mappings":";;;;;KAWY,wBAAwB,KAClC;KAIU;UAOK;EACf,MAAM;EACN;EACA;EACA;EACA;EACA;EACA;;UAGe;EACf;IAAa;IAAa;IAAa;IAAe;;EACtD;IAAc;IAAa;IAAa;IAAgB;;EACxD;IAAqB;IAAa;IAAa;;;UAGhC;EACf;EACA;EACA;EACA;EACA,WAAW;EACX,eAAe;EACf,OAAO;;UAGQ;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;iBCtCc,MAAM,eAAe,MAAM,aAAa;;;;;;;;iBAkCxC,uBAAuB,kBAAkB,kBAAkB;;iBA6K3D,eAAe,QAAQ,wBAAwB;;iBAiB/C,iBAAiB,kBAAkB,yBAAyB;;;;KC9MhE,sBAAsB,KAAK;;;;;;;;;;;;;;;;;;iBAqDvB,uBAAuB,OAAO,sBAAsB;;;;;;;;iBCnEpD,kBAAkB,QAAQ;iBAiE1B,oBAAoB,QAAQ;iBA0C5B,sBAAsB,WAAW;;;UC9GhC;WACN,SAAS;WACT,KAAK;WACL,kBAAkB;WAClB;WACA;;iBAgRK,uBAAuB,OAAO,yBAAyB;;;;iBCpSvD,wBACd,kBAAkB,6BAClB,IAAI,MACJ,iBACA;EACG;;;;UCWK;WACC;WACA;WACA;WACA;WACA;WACA;WACA;WACA;aACE;aACA;eAAkB;;eAAmC;;eAAgC;;;WAEvF;aAAmB;;;UAGb;WACN;aACE;eACE;iBACE;iBACA;mBAAkB;;;;aAGtB;eACE;iBAAkB;;eAClB,gBAAgB;;;;UAKd;WACN;WACA;WACA;WACA;WACA;aACE;aACA;aACA;aACA;;WAEF;aACE;aACA;aACA;aACA;aACA;;WAEF;aACE;aACA;aACA;aACA;;;UAII;WACN;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;;iBA6KK,2BACd,kBAAkB,6BAClB,iBACA;WACY,SAAS;WAAgC,UAAU;WAAmC,aAAa;;;;KClQ5G;UAKK;WACC;WACA;WACA;WACA;WACA,WAAW;WACX;WACA;WACA;aACE;aACA;eAAkB;;;;UAId;WACN;aACE;eACE;iBACE;iBACA;mBAAkB;;;;aAGtB;eACE;iBAAkB;;eAClB,qBAAqB;;;;;iBAkBpB,uBACd,kBAAkB,6BAClB,iBACA,YAAY,MACZ;WAES;WACA,UAAU;WACV,SAAS;;;;;iBCvCJ,0BAA0B,QAAQ"}
|