@robota-sdk/agent-session-analytics 3.0.0-beta.82 → 3.0.0-beta.83
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 +215 -0
- package/dist/node/index.cjs +1 -1
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/package.json +11 -6
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# @robota-sdk/agent-session-analytics
|
|
2
|
+
|
|
3
|
+
## 3.0.0-beta.83
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 3c81769: A supervised session accepts attach connections on its control socket.
|
|
8
|
+
|
|
9
|
+
- A terminal of the same user sends `{"command":"attach","id","generation","mode":"drive"|"observe","protocol":1}`.
|
|
10
|
+
The session compares the generation with its own. It refuses another generation, an unknown mode or
|
|
11
|
+
protocol, or a fifth concurrent attach. Otherwise it answers `{"status":"attached","driverId":"attach:<n>"}`
|
|
12
|
+
and carries the ordinary session protocol as newline-delimited JSON on the same connection.
|
|
13
|
+
- The driver id is assigned by the session and a client-sent one is ignored. Turns submitted from an
|
|
14
|
+
attached terminal are attributed to it and counted under the new `attach` usage surface.
|
|
15
|
+
- `drive` sends prompts and answers the session's questions under the usual co-drive rules. `observe`
|
|
16
|
+
is read-only and never counts as a surface that can answer, so an unattended session still denies
|
|
17
|
+
its prompts at once.
|
|
18
|
+
- Detaching or crashing ends only that connection: a turn in progress keeps running, a prompt no other
|
|
19
|
+
surface can answer is denied, and the session returns to its unattached posture. A reader that falls
|
|
20
|
+
1 MiB behind is disconnected, and an oversize frame closes only its own connection. Stopping the
|
|
21
|
+
session ends attached connections with it.
|
|
22
|
+
- Commands from an attached terminal carry the remote origin, so pairing, revoking and reading the
|
|
23
|
+
pairing link stay refused. An attached terminal is never an operator approver; supervised sessions
|
|
24
|
+
keep refusing mesh connections that need one.
|
|
25
|
+
- A client may send its first frames in the same write as the handshake; only the handshake line
|
|
26
|
+
itself is held to the control endpoint's line limit.
|
|
27
|
+
- `agent-framework`: the surface a turn was submitted on now reaches its usage observation. It was
|
|
28
|
+
dropped before, so remote-control turns were counted as `unknown`.
|
|
29
|
+
- The terminal client (`robota session attach`) and the view keys come separately.
|
|
30
|
+
|
|
31
|
+
- 57280bf: Every published package now declares `"engines": { "node": ">=22.12.0" }`. Before, 27 of the 38
|
|
32
|
+
packages declared no floor (`agent-core`, `agent-tools` and every provider among them),
|
|
33
|
+
`agent-session` and `agent-file-authority` declared `>=20.19.0`, and the other nine declared
|
|
34
|
+
`>=22.0.0`, so a consumer on Node 20 saw at most a warning from a transitive dependency.
|
|
35
|
+
|
|
36
|
+
Why 22.12: `agent-cli` and `agent-ui-terminal` need Node 22 through `ink` 7, and the CommonJS entries
|
|
37
|
+
of `agent-tools` and its dependents, `agent-transport`/`node` and its dependents, and
|
|
38
|
+
`agent-ui-terminal` `require()` ESM-only dependencies (`p-limit`, `jose`, `chalk`), which Node 22
|
|
39
|
+
supports unflagged only from 22.12. `engines` is advisory unless the consumer enables `engine-strict`.
|
|
40
|
+
|
|
41
|
+
No code changes: `tsdown` now reads `node22.12.0` as its build target from the field.
|
|
42
|
+
|
|
43
|
+
- 18c0d5c: Every published package now exports `./package.json`, so `require('<package>/package.json')` and
|
|
44
|
+
`import('<package>/package.json', { with: { type: 'json' } })` work instead of failing with
|
|
45
|
+
`ERR_PACKAGE_PATH_NOT_EXPORTED`, and each tarball now ships the package's `CHANGELOG.md`.
|
|
46
|
+
- Updated dependencies [3c81769]
|
|
47
|
+
- Updated dependencies [e689c8e]
|
|
48
|
+
- Updated dependencies [7b72344]
|
|
49
|
+
- Updated dependencies [6ae3f28]
|
|
50
|
+
- Updated dependencies [be0e53c]
|
|
51
|
+
- Updated dependencies [57f57f5]
|
|
52
|
+
- Updated dependencies [ba822c1]
|
|
53
|
+
- Updated dependencies [6e6b06b]
|
|
54
|
+
- Updated dependencies [8bd5fac]
|
|
55
|
+
- Updated dependencies [57280bf]
|
|
56
|
+
- Updated dependencies [5033dd9]
|
|
57
|
+
- Updated dependencies [18c0d5c]
|
|
58
|
+
- Updated dependencies [dbd888d]
|
|
59
|
+
- Updated dependencies [1887e54]
|
|
60
|
+
- @robota-sdk/agent-interface-analytics@3.0.0-beta.83
|
|
61
|
+
- @robota-sdk/agent-core@3.0.0-beta.83
|
|
62
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.83
|
|
63
|
+
|
|
64
|
+
## 3.0.0-beta.82
|
|
65
|
+
|
|
66
|
+
### Patch Changes
|
|
67
|
+
|
|
68
|
+
- Updated dependencies [c7f9203]
|
|
69
|
+
- @robota-sdk/agent-core@3.0.0-beta.82
|
|
70
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.82
|
|
71
|
+
- @robota-sdk/agent-interface-analytics@3.0.0-beta.82
|
|
72
|
+
|
|
73
|
+
## 3.0.0-beta.81
|
|
74
|
+
|
|
75
|
+
### Patch Changes
|
|
76
|
+
|
|
77
|
+
- Updated dependencies [3038eb7]
|
|
78
|
+
- Updated dependencies [02b7452]
|
|
79
|
+
- Updated dependencies [ec5e477]
|
|
80
|
+
- @robota-sdk/agent-core@3.0.0-beta.81
|
|
81
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.81
|
|
82
|
+
- @robota-sdk/agent-interface-analytics@3.0.0-beta.81
|
|
83
|
+
|
|
84
|
+
## 3.0.0-beta.80
|
|
85
|
+
|
|
86
|
+
### Minor Changes
|
|
87
|
+
|
|
88
|
+
- 4078a72: Model fallback chain. `--fallback-model a,b` (or the `fallbackModel` settings array; the flag wins) names up to three models a turn moves to when its model is overloaded, unavailable or failing on the server. An entry is a provider profile, `profile:model`, a bare model on the primary's provider, or `default`. The move happens only before any output has streamed, lasts for the current turn, and is shown as a system note; entries that cannot be built are passed over and entries outside the organization's `allowedProviders` are dropped with a notice. `FallbackProvider` in agent-framework implements it over the session's provider; `IChatOptions` gains `executionId`, `onModelFallback` and `preserveContextWindow`, `IAIProvider` gains optional `resolveModelRoute`, and the execution loop emits a `provider_fallback` event and attributes requests, call observations, committed replies, the response cache and usage to the model that answered. A turn that ran on more than one model records per-model `modelShares` on its usage observation, which personal usage reports split by model and provider. A `/provider` switch keeps the chain, read again for the new primary.
|
|
89
|
+
|
|
90
|
+
### Patch Changes
|
|
91
|
+
|
|
92
|
+
- 9368d00: Advisor escalation: the main model can consult a second model at the decision points it chooses.
|
|
93
|
+
|
|
94
|
+
With an advisor configured (`--advisor <profile>[:<model>]`, or the `advisorModel` setting that
|
|
95
|
+
`/advisor` saves; the flag wins), the session gets an `Advisor({ question? })` tool. The advisor reads
|
|
96
|
+
the whole conversation — system prompt, messages, tool calls and results — serialized into one prompt
|
|
97
|
+
and sent with `toolChoice: 'none'`, truncated from the front to fit its window with the system prompt
|
|
98
|
+
kept, and declines when even that does not fit. Its answer comes back framed as guidance to verify;
|
|
99
|
+
an empty or refusing answer reads as declined. Calls are limited to two per turn and a fixed number
|
|
100
|
+
per session, parallel calls in one round share those limits, and a repeated question in the same
|
|
101
|
+
turn returns the earlier answer. A request that was sent counts even when the provider failed (the
|
|
102
|
+
decline is reported by class, never by its text); only a call declined before sending gives its slot
|
|
103
|
+
back.
|
|
104
|
+
|
|
105
|
+
Advisor usage, including in-process subagents', is recorded where each turn's usage is recorded —
|
|
106
|
+
the persisted session history, under the advisor's own provider and model — so `/cost`, usage reports
|
|
107
|
+
and resumed sessions include it. `/cost` now totals that history and prices each part on its own
|
|
108
|
+
model, showing "mixed" when more than one model was priced.
|
|
109
|
+
|
|
110
|
+
`/advisor <model>` and `/advisor off` change only where calls go, never the tool list, so the main
|
|
111
|
+
model's prompt cache is not invalidated mid-session; the tool is added only when a session starts
|
|
112
|
+
with an advisor. Sending history to a destination (provider type and endpoint host) the main model
|
|
113
|
+
does not already use needs a one-time consent per destination, kept in the user settings file; a
|
|
114
|
+
refusal is remembered for the session. The organization's `allowedProviders` applies, and
|
|
115
|
+
`ROBOTA_DISABLE_ADVISOR=1` turns it off completely. In-process subagents inherit the advisor, bound
|
|
116
|
+
to their own conversation; child-process subagents do not get it.
|
|
117
|
+
|
|
118
|
+
**`@robota-sdk/agent-framework` is `major` for one reason: `ICommandHostSessionAccess` gains a
|
|
119
|
+
required member,** `getSessionUsage()`, the session's persisted usage records. An external
|
|
120
|
+
implementation of that role port stops compiling until it adds the method. The rest is additive.
|
|
121
|
+
|
|
122
|
+
- `agent-session` — `formatConversationEntries`, the one text rendering of a conversation, now used
|
|
123
|
+
by compaction too. It keeps tool calls and results, marks a user message a peer session sent
|
|
124
|
+
(`user [from "peer:<id>"]`), and JSON-encodes every message onto one line so no content can forge
|
|
125
|
+
another entry; compaction previously flattened all of this. `Session.getProvider()` returns the
|
|
126
|
+
provider the session currently uses.
|
|
127
|
+
- `agent-framework` — `AdvisorController`, `createAdvisorTool`, the advisor spec helpers, provider
|
|
128
|
+
destinations (`describeProviderDestination`, `rememberProviderDestination`), `getSessionUsage` and
|
|
129
|
+
`ISessionUsageRecord`, the `onUsageRecorded` session option, and the optional `advisor` command
|
|
130
|
+
host adapter. Session assembly binds a host-supplied Advisor tool to the session holding it.
|
|
131
|
+
- `agent-command` — the `/advisor` command module; `/cost` reads the session's persisted usage.
|
|
132
|
+
- `agent-cli` — the `--advisor` flag, `advisorModel` setting, per-destination consent store and kill
|
|
133
|
+
switch.
|
|
134
|
+
- `agent-ui-terminal` — a usage line from another source (the advisor, a background task) names that
|
|
135
|
+
source and leaves out the context window it does not have.
|
|
136
|
+
- `agent-session-analytics` — personal usage counts an advisor call's tokens and cost toward its
|
|
137
|
+
turn without counting it as a turn.
|
|
138
|
+
|
|
139
|
+
- 4f3c075: Assemble complete, verified package generations before switching build output; preserve the previous generation on build failure and pack only verified regular-file images. Include copied CLI web assets in affected-build ordering and artifact transfer. Preserve the CLI version in managed build paths. Public runtime contracts remain compatible (patch).
|
|
140
|
+
- Updated dependencies [7b6234c]
|
|
141
|
+
- Updated dependencies [37b4bd7]
|
|
142
|
+
- Updated dependencies [4eea54b]
|
|
143
|
+
- Updated dependencies [1698be4]
|
|
144
|
+
- Updated dependencies [d4189b9]
|
|
145
|
+
- Updated dependencies [9edae52]
|
|
146
|
+
- Updated dependencies [4078a72]
|
|
147
|
+
- Updated dependencies [4c73a0b]
|
|
148
|
+
- Updated dependencies [196a900]
|
|
149
|
+
- Updated dependencies [f336838]
|
|
150
|
+
- Updated dependencies [34e50f0]
|
|
151
|
+
- Updated dependencies [722e88a]
|
|
152
|
+
- Updated dependencies [d23c848]
|
|
153
|
+
- Updated dependencies [fec722f]
|
|
154
|
+
- Updated dependencies [2d3b2c0]
|
|
155
|
+
- Updated dependencies [4772067]
|
|
156
|
+
- Updated dependencies [9fbab1b]
|
|
157
|
+
- Updated dependencies [a009f5b]
|
|
158
|
+
- Updated dependencies [4f3c075]
|
|
159
|
+
- Updated dependencies [475e085]
|
|
160
|
+
- Updated dependencies [e477440]
|
|
161
|
+
- Updated dependencies [9dcb5da]
|
|
162
|
+
- Updated dependencies [a95ca85]
|
|
163
|
+
- Updated dependencies [b6d14ce]
|
|
164
|
+
- Updated dependencies [0382a51]
|
|
165
|
+
- Updated dependencies [93d061d]
|
|
166
|
+
- Updated dependencies [39554a1]
|
|
167
|
+
- Updated dependencies [d28430a]
|
|
168
|
+
- Updated dependencies [07b627f]
|
|
169
|
+
- Updated dependencies [d0de5b2]
|
|
170
|
+
- Updated dependencies [d6b9404]
|
|
171
|
+
- Updated dependencies [9814afc]
|
|
172
|
+
- @robota-sdk/agent-core@3.0.0-beta.80
|
|
173
|
+
- @robota-sdk/agent-interface-session@3.0.0-beta.80
|
|
174
|
+
- @robota-sdk/agent-interface-analytics@3.0.0-beta.80
|
|
175
|
+
|
|
176
|
+
## 3.0.0-beta.79
|
|
177
|
+
|
|
178
|
+
### Patch Changes
|
|
179
|
+
|
|
180
|
+
- @robota-sdk/agent-core@3.0.0-beta.79
|
|
181
|
+
- @robota-sdk/agent-interface-transport@3.0.0-beta.79
|
|
182
|
+
|
|
183
|
+
## 3.0.0-beta.78
|
|
184
|
+
|
|
185
|
+
### Patch Changes
|
|
186
|
+
|
|
187
|
+
- Updated dependencies [6f308d1]
|
|
188
|
+
- @robota-sdk/agent-core@3.0.0-beta.78
|
|
189
|
+
- @robota-sdk/agent-interface-transport@3.0.0-beta.78
|
|
190
|
+
|
|
191
|
+
## 3.0.0-beta.77
|
|
192
|
+
|
|
193
|
+
### Patch Changes
|
|
194
|
+
|
|
195
|
+
- Updated dependencies
|
|
196
|
+
- @robota-sdk/agent-core@3.0.0-beta.77
|
|
197
|
+
- @robota-sdk/agent-interface-transport@3.0.0-beta.77
|
|
198
|
+
|
|
199
|
+
## 3.0.0-beta.76
|
|
200
|
+
|
|
201
|
+
### Minor Changes
|
|
202
|
+
|
|
203
|
+
- c0a6287: Relocate session feature logic out of the CLI shell and the transport (DQ-AUDIT-004):
|
|
204
|
+
|
|
205
|
+
- Extract session-log timing analysis into the new `@robota-sdk/agent-session-analytics` package (pure analysis over canonical session records — no duplicate types, no file I/O). `agent-cli`'s `session analyze` command shrinks to thin wiring and loads records via the new `createUserSessionStore()` / existing `createProjectSessionStore()` framework facades.
|
|
206
|
+
- Move LLM-based session auto-naming (`generateSessionName`) from `agent-transport/tui` into `agent-framework` (session-lifecycle owner); the TUI transport now invokes it through the framework.
|
|
207
|
+
|
|
208
|
+
### Patch Changes
|
|
209
|
+
|
|
210
|
+
- Updated dependencies
|
|
211
|
+
- Updated dependencies
|
|
212
|
+
- Updated dependencies
|
|
213
|
+
- Updated dependencies [576af62]
|
|
214
|
+
- @robota-sdk/agent-core@3.0.0-beta.76
|
|
215
|
+
- @robota-sdk/agent-interface-transport@3.0.0-beta.76
|
package/dist/node/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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
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
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 b(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 x(e,t){return t<=0?0:Math.round(e/t*1e3)/10}function te(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(b(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:x(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===`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}}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,!t.legacy&&t.observation.source?.scope!==`tool`&&(e.turns+=1),e.promptTokens+=n?.promptTokens??0,e.completionTokens+=n?.completionTokens??0,e.totalTokens+=n?.totalTokens??0,e.costUsd+=n?.costUsd??0,(!n||n.costStatus===`unknown`)&&(e.hasUnknownCost=!0),n?.costStatus===`estimated`&&(e.hasEstimatedCost=!0)}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.observations===0||e.hasUnknownCost?`unknown`:e.hasEstimatedCost?`estimated`:`exact`}}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,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 W(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}=U(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(),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 G=(1n<<64n)-1n,K=/^(?!0{32}$)[0-9a-f]{32}$/,q=/^(?!0{16}$)[0-9a-f]{16}$/,ce=[`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<=G?n:void 0}function le(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:`robota.prompt_execution`,kind:1,startTimeUnixNano:String(t),endTimeUnixNano:String(n),attributes:[{key:`robota.prompt.outcome`,value:{stringValue:r}}],status:{code:r===`success`?1:r===`failure`?2:0}}}function ue(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`?`robota.provider_call`:`robota.tool_body`,kind:1,startTimeUnixNano:String(a),endTimeUnixNano:String(o),attributes:[{key:t===`provider`?`robota.provider.outcome`:`robota.tool.outcome`,value:{stringValue:s}}],status:{code:s===`success`?1:s===`failure`?2:0}}}function de(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 fe(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:`robota.provider.usage.input_tokens`,value:{intValue:String(o)}},{key:`robota.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:`robota.provider.cost.usd.estimated`,value:{doubleValue:d}})),{...t,attributes:l}}function Q(e,t){let n=[],r=new Map,i=new Map;for(let t of e)for(let e of t.history??[]){if(e.type!==`usage-observation`)continue;let a=e.data,o=J(a)?a.usageObservationId:void 0,s=J(a)?a.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(r,c),l&&Y(i,l),n.push({record:t,data:a,observationKey:c,traceId:l})}let a=[],o=new Map,s=0,c=0,l=0;for(let e of n){if(e.observationKey&&(r.get(e.observationKey)??0)>1||e.traceId&&(i.get(e.traceId)??0)>1){l+=1;continue}let t=e.data;if(!J(t)){c+=1;continue}if(ce.every(e=>t[e]===void 0)){s+=1;continue}let n=le(t);if(!n){c+=1;continue}a.push(n);let u=o.get(e.record)??new Map;u.set(n.traceId,n),o.set(e.record,u)}let u=[],d=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=de(e.data),i=ue(e.data,n);r&&Y(d,r),u.push({record:t,span:i,identity:r,kind:n,data:e.data})}let f={provider:{exported:0,invalid:0,orphaned:0,duplicate:0},tool:{exported:0,invalid:0,orphaned:0,duplicate:0}},p={legacy:0,invalid:0,partial:0,absent:0,nonInvoked:0},m={invoked:0,completeUsage:0,inputTokens:0,outputTokens:0,estimatedCostUsd:0,unknownCost:0,exactPriceMatches:0,familyPriceMatches:0};for(let e of u){let{span:t}=e,n=f[e.kind];if(e.identity&&(d.get(e.identity)??0)>1){n.duplicate+=1;continue}if(!t){n.invalid+=1;continue}let r=o.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`)){p.nonInvoked+=1,(e.data.promptTokens!==void 0||e.data.completionTokens!==void 0||e.data.totalTokens!==void 0||e.data.usageProvenance===`complete`)&&(p.invalid+=1);continue}a.push(e.kind===`provider`?fe(t,e.data,p,m):t),n.exported+=1}return{payload:{resourceSpans:a.length?[{resource:{attributes:[{key:`service.name`,value:{stringValue:`robota`}},{key:`service.version`,value:{stringValue:t}}]},scopeSpans:[{scope:{name:`@robota-sdk/agent-session-analytics`},spans:a}]}]:[]},coverage:{exported:a.length-f.provider.exported-f.tool.exported,missing:s,invalid:c,duplicate:l,providerChildren:f.provider,providerUsage:p,toolChildren:f.tool},callMetrics:m}}function pe(e,t,n){let r=new Set,i=0,a=0,o=0,s=0,c=0,l=0,u=0,d=0;for(let t of e)for(let e of M(t)){if(r.has(e.id))continue;r.add(e.id),e.legacy||(i+=1);let t=e.observation.usage;if(!t){u+=1,c+=1;continue}a+=t.totalTokens,o+=t.promptTokens??0,s+=t.completionTokens??0,(t.promptTokens===void 0||t.completionTokens===void 0)&&(c+=1),t.costStatus===`unknown`||t.costUsd===void 0?u+=1:(l+=t.costUsd,t.costStatus===`estimated`&&(d+=1))}let f=Q(e,n),p=f.callMetrics,m=String(BigInt(t.getTime())*1000000n),h=(e,t,n)=>({name:e,unit:t,gauge:{dataPoints:[{timeUnixNano:m,asDouble:n}]}});return{resourceMetrics:[{resource:{attributes:[{key:`service.name`,value:{stringValue:`robota`}},{key:`service.version`,value:{stringValue:n}}]},scopeMetrics:[{scope:{name:`@robota-sdk/agent-session-analytics`},metrics:[h(`robota.session.count`,`{session}`,e.length),h(`robota.turn.count`,`{turn}`,i),h(`robota.token.total`,`{token}`,a),h(`robota.token.input.known`,`{token}`,o),h(`robota.token.output.known`,`{token}`,s),h(`robota.token.split_unknown_observations`,`{observation}`,c),h(`robota.cost.usd.known`,`USD`,l),h(`robota.cost.unknown_observations`,`{observation}`,u),h(`robota.cost.estimated_observations`,`{observation}`,d),h(`robota.provider_call.count`,`{call}`,p.invoked),h(`robota.provider_call.usage.complete_count`,`{call}`,p.completeUsage),h(`robota.provider_call.token.input.known`,`{token}`,p.inputTokens),h(`robota.provider_call.token.output.known`,`{token}`,p.outputTokens),h(`robota.provider_call.cost.usd.estimated`,`USD`,p.estimatedCostUsd),h(`robota.provider_call.cost.unknown_count`,`{call}`,p.unknownCost),h(`robota.provider_call.usage.partial_count`,`{call}`,f.coverage.providerUsage.partial),h(`robota.provider_call.usage.invalid_count`,`{call}`,f.coverage.providerUsage.invalid),h(`robota.provider_call.usage.legacy_count`,`{call}`,f.coverage.providerUsage.legacy),h(`robota.provider_call.price.exact_id_count`,`{call}`,p.exactPriceMatches),h(`robota.provider_call.price.family_fallback_count`,`{call}`,p.familyPriceMatches)]}]}]}}const me=(1n<<64n)-1n;function he(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>me)throw RangeError(`OTLP log observation time is out of range.`);return String(n)}function ge(e,t,n){let r=Q(e,t),i=r.payload.resourceSpans[0]?.scopeSpans[0]?.spans??[];if(i.length===0)return{exported:0,coverage:r.coverage,payload:{resourceLogs:[]}};let a=he(n),o=i.map(e=>{let t=e.attributes.find(e=>e.key===`robota.prompt.outcome`||e.key===`robota.provider.outcome`||e.key===`robota.tool.outcome`),n=e.name===`robota.prompt_execution`?`robota.prompt_execution.completed`:e.name===`robota.provider_call`?`robota.provider_call.completed`:`robota.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:a,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:o.length,coverage:r.coverage,payload:{resourceLogs:[{resource:r.payload.resourceSpans[0].resource,scopeLogs:[{scope:{name:`@robota-sdk/agent-session-analytics`},logRecords:o}]}]}}}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 _e(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(`
|
|
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 b(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 x(e,t){return t<=0?0:Math.round(e/t*1e3)/10}function te(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(b(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:x(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}}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,!t.legacy&&t.observation.source?.scope!==`tool`&&(e.turns+=1),e.promptTokens+=n?.promptTokens??0,e.completionTokens+=n?.completionTokens??0,e.totalTokens+=n?.totalTokens??0,e.costUsd+=n?.costUsd??0,(!n||n.costStatus===`unknown`)&&(e.hasUnknownCost=!0),n?.costStatus===`estimated`&&(e.hasEstimatedCost=!0)}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.observations===0||e.hasUnknownCost?`unknown`:e.hasEstimatedCost?`estimated`:`exact`}}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,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 W(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}=U(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(),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 G=(1n<<64n)-1n,K=/^(?!0{32}$)[0-9a-f]{32}$/,q=/^(?!0{16}$)[0-9a-f]{16}$/,ce=[`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<=G?n:void 0}function le(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:`robota.prompt_execution`,kind:1,startTimeUnixNano:String(t),endTimeUnixNano:String(n),attributes:[{key:`robota.prompt.outcome`,value:{stringValue:r}}],status:{code:r===`success`?1:r===`failure`?2:0}}}function ue(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`?`robota.provider_call`:`robota.tool_body`,kind:1,startTimeUnixNano:String(a),endTimeUnixNano:String(o),attributes:[{key:t===`provider`?`robota.provider.outcome`:`robota.tool.outcome`,value:{stringValue:s}}],status:{code:s===`success`?1:s===`failure`?2:0}}}function de(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 fe(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:`robota.provider.usage.input_tokens`,value:{intValue:String(o)}},{key:`robota.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:`robota.provider.cost.usd.estimated`,value:{doubleValue:d}})),{...t,attributes:l}}function Q(e,t){let n=[],r=new Map,i=new Map;for(let t of e)for(let e of t.history??[]){if(e.type!==`usage-observation`)continue;let a=e.data,o=J(a)?a.usageObservationId:void 0,s=J(a)?a.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(r,c),l&&Y(i,l),n.push({record:t,data:a,observationKey:c,traceId:l})}let a=[],o=new Map,s=0,c=0,l=0;for(let e of n){if(e.observationKey&&(r.get(e.observationKey)??0)>1||e.traceId&&(i.get(e.traceId)??0)>1){l+=1;continue}let t=e.data;if(!J(t)){c+=1;continue}if(ce.every(e=>t[e]===void 0)){s+=1;continue}let n=le(t);if(!n){c+=1;continue}a.push(n);let u=o.get(e.record)??new Map;u.set(n.traceId,n),o.set(e.record,u)}let u=[],d=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=de(e.data),i=ue(e.data,n);r&&Y(d,r),u.push({record:t,span:i,identity:r,kind:n,data:e.data})}let f={provider:{exported:0,invalid:0,orphaned:0,duplicate:0},tool:{exported:0,invalid:0,orphaned:0,duplicate:0}},p={legacy:0,invalid:0,partial:0,absent:0,nonInvoked:0},m={invoked:0,completeUsage:0,inputTokens:0,outputTokens:0,estimatedCostUsd:0,unknownCost:0,exactPriceMatches:0,familyPriceMatches:0};for(let e of u){let{span:t}=e,n=f[e.kind];if(e.identity&&(d.get(e.identity)??0)>1){n.duplicate+=1;continue}if(!t){n.invalid+=1;continue}let r=o.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`)){p.nonInvoked+=1,(e.data.promptTokens!==void 0||e.data.completionTokens!==void 0||e.data.totalTokens!==void 0||e.data.usageProvenance===`complete`)&&(p.invalid+=1);continue}a.push(e.kind===`provider`?fe(t,e.data,p,m):t),n.exported+=1}return{payload:{resourceSpans:a.length?[{resource:{attributes:[{key:`service.name`,value:{stringValue:`robota`}},{key:`service.version`,value:{stringValue:t}}]},scopeSpans:[{scope:{name:`@robota-sdk/agent-session-analytics`},spans:a}]}]:[]},coverage:{exported:a.length-f.provider.exported-f.tool.exported,missing:s,invalid:c,duplicate:l,providerChildren:f.provider,providerUsage:p,toolChildren:f.tool},callMetrics:m}}function pe(e,t,n){let r=new Set,i=0,a=0,o=0,s=0,c=0,l=0,u=0,d=0;for(let t of e)for(let e of M(t)){if(r.has(e.id))continue;r.add(e.id),e.legacy||(i+=1);let t=e.observation.usage;if(!t){u+=1,c+=1;continue}a+=t.totalTokens,o+=t.promptTokens??0,s+=t.completionTokens??0,(t.promptTokens===void 0||t.completionTokens===void 0)&&(c+=1),t.costStatus===`unknown`||t.costUsd===void 0?u+=1:(l+=t.costUsd,t.costStatus===`estimated`&&(d+=1))}let f=Q(e,n),p=f.callMetrics,m=String(BigInt(t.getTime())*1000000n),h=(e,t,n)=>({name:e,unit:t,gauge:{dataPoints:[{timeUnixNano:m,asDouble:n}]}});return{resourceMetrics:[{resource:{attributes:[{key:`service.name`,value:{stringValue:`robota`}},{key:`service.version`,value:{stringValue:n}}]},scopeMetrics:[{scope:{name:`@robota-sdk/agent-session-analytics`},metrics:[h(`robota.session.count`,`{session}`,e.length),h(`robota.turn.count`,`{turn}`,i),h(`robota.token.total`,`{token}`,a),h(`robota.token.input.known`,`{token}`,o),h(`robota.token.output.known`,`{token}`,s),h(`robota.token.split_unknown_observations`,`{observation}`,c),h(`robota.cost.usd.known`,`USD`,l),h(`robota.cost.unknown_observations`,`{observation}`,u),h(`robota.cost.estimated_observations`,`{observation}`,d),h(`robota.provider_call.count`,`{call}`,p.invoked),h(`robota.provider_call.usage.complete_count`,`{call}`,p.completeUsage),h(`robota.provider_call.token.input.known`,`{token}`,p.inputTokens),h(`robota.provider_call.token.output.known`,`{token}`,p.outputTokens),h(`robota.provider_call.cost.usd.estimated`,`USD`,p.estimatedCostUsd),h(`robota.provider_call.cost.unknown_count`,`{call}`,p.unknownCost),h(`robota.provider_call.usage.partial_count`,`{call}`,f.coverage.providerUsage.partial),h(`robota.provider_call.usage.invalid_count`,`{call}`,f.coverage.providerUsage.invalid),h(`robota.provider_call.usage.legacy_count`,`{call}`,f.coverage.providerUsage.legacy),h(`robota.provider_call.price.exact_id_count`,`{call}`,p.exactPriceMatches),h(`robota.provider_call.price.family_fallback_count`,`{call}`,p.familyPriceMatches)]}]}]}}const me=(1n<<64n)-1n;function he(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>me)throw RangeError(`OTLP log observation time is out of range.`);return String(n)}function ge(e,t,n){let r=Q(e,t),i=r.payload.resourceSpans[0]?.scopeSpans[0]?.spans??[];if(i.length===0)return{exported:0,coverage:r.coverage,payload:{resourceLogs:[]}};let a=he(n),o=i.map(e=>{let t=e.attributes.find(e=>e.key===`robota.prompt.outcome`||e.key===`robota.provider.outcome`||e.key===`robota.tool.outcome`),n=e.name===`robota.prompt_execution`?`robota.prompt_execution.completed`:e.name===`robota.provider_call`?`robota.provider_call.completed`:`robota.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:a,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:o.length,coverage:r.coverage,payload:{resourceLogs:[{resource:r.payload.resourceSpans[0].resource,scopeLogs:[{scope:{name:`@robota-sdk/agent-session-analytics`},logRecords:o}]}]}}}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 _e(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
5
|
`)}exports.aggregateReports=s,exports.analyzeSession=o,exports.computeTimingIntervals=i,exports.createOtlpPromptEvents=ge,exports.createOtlpPromptRootTraces=Q,exports.createOtlpUsageSnapshot=pe,exports.formatAggregateReport=g,exports.formatPersonalUsageReport=_e,exports.formatSingleSession=h,exports.formatUsageReport=u,exports.gapMs=t,exports.summarizePersonalUsage=W,exports.summarizeUsageBySource=te;
|
package/dist/node/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import{MODEL_PRICES as e,calculateModelCost as t}from"@robota-sdk/agent-core";function n(e,t){return new Date(t).getTime()-new Date(e).getTime()}function r(e){return new Date(e).toISOString()}function i(e,t,i,a,o,s){return{kind:e,fromType:t,toType:i,fromTimestamp:r(a),toTimestamp:r(o),durationMs:n(a,o),turnIndex:s}}function a(e){let t=[],n=0,r=0;for(;r<e.length;){let a=e[r];if(!a){r++;continue}if(a.category===`chat`&&a.type===`user`){n++;let o=a.timestamp,s=!1,c=null,l=r+1;for(;l<e.length;){let r=e[l];if(!r){l++;continue}if(r.category===`chat`&&r.type===`user`)break;if(r.category===`event`&&r.type===`tool-start`){s?c!==null&&t.push(i(`llm_between_tools`,`tool-end`,`tool-start`,c,r.timestamp,n)):(t.push(i(`user_to_first_tool`,`user`,`tool-start`,o,r.timestamp,n)),s=!0),c=null,l++;continue}if(r.category===`event`&&r.type===`tool-end`){let a=e[l-1];a&&a.category===`event`&&a.type===`tool-start`&&t.push(i(`tool_exec`,`tool-start`,`tool-end`,a.timestamp,r.timestamp,n)),c=r.timestamp,l++;continue}if(r.category===`event`&&r.type===`tool-summary`){c!==null&&t.push(i(`llm_final_response`,`tool-end`,`tool-summary`,c,r.timestamp,n)),l++;continue}if(r.category===`chat`&&r.type===`assistant`){s?c!==null&&t.push(i(`llm_final_response`,`tool-end`,`assistant`,c,r.timestamp,n)):t.push(i(`user_to_assistant`,`user`,`assistant`,o,r.timestamp,n)),l++;break}l++}r=l}else r++}return t}function o(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 s(e){let t=a(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:o(t)}}function c(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 l(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:`${e}`}function u(e,t){return`${t?``:`~`}$${e.toFixed(4)}`}function d(e){let t=[];if(t.push(`Token usage — session ${e.sessionId}`),t.push(` total ${l(e.totalTokens)} (prompt ${l(e.promptTokens)} · completion ${l(e.completionTokens)})`),t.push(` cost ${u(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)}% ${l(n.totalTokens).padStart(7)} ${u(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} · ${f(n.totalDurationMs)} across ${n.spans.length} op${n.spans.length===1?``:`s`}`);for(let e of n.spans)t.push(` ${e.op.padEnd(22)} ${f(e.durationMs).padStart(8)}`)}}return t.join(`
|
|
2
2
|
`)}function f(e){return e>=1e3?`${(e/1e3).toFixed(1)}s`:`${e}ms`}function p(e){return new Date(e).toLocaleString(`sv-SE`,{timeZoneName:`short`}).slice(0,16)}function m(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 h(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 g(e){let t=[],{stats:n}=e;if(t.push(`Session: ${e.sessionId}`),t.push(`Created: ${p(e.createdAt)} | cwd: ${e.cwd}`),t.push(``),t.push(`Timing Summary:`),n.llmWaitMs.count>0&&t.push(` LLM API wait ${f(n.llmWaitMs.avg).padStart(8)} avg | ${f(n.llmWaitMs.max).padStart(8)} max`),n.toolExecMs.count>0&&t.push(` Tool execution ${f(n.toolExecMs.avg).padStart(8)} avg | ${f(n.toolExecMs.median).padStart(8)} median`),n.userToAssistantMs.count>0&&t.push(` user → assistant ${f(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} ${m(n).padEnd(32)} ${f(n.durationMs).padStart(8)}`)}return t.push(``),t.push(h(e)),t.join(`
|
|
3
3
|
`)}function ee(e){let t=[];return t.push(`Analyzed ${e.sessionCount} sessions (${p(e.fromDate)} ~ ${p(e.toDate)})`),t.push(` Avg LLM response: ${f(e.avgLlmResponseMs)}`),t.push(` Avg tool exec: ${f(e.avgToolExecMs)}`),e.maxSingleDelayMs>0&&t.push(` Max single delay: ${f(e.maxSingleDelayMs)} (${e.maxSingleDelaySession}, turn ${e.maxSingleDelayTurn}, ${e.maxSingleDelayKind})`),t.join(`
|
|
4
|
-
`)}const _={scope:`main`,label:`main thread`};function te(e){return`${e.scope}:${e.id??``}`}function v(e){return e.label??(e.id?`${e.scope} ${e.id}`:e.scope)}function ne(e){return e.type===`usage-summary`&&typeof e.data==`object`&&e.data!==null}function re(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 y(e,t){return t<=0?0:Math.round(e/t*1e3)/10}function b(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:v(e),spans:c,totalDurationMs:c.reduce((e,t)=>e+t.durationMs,0)}),c=[])};for(let s of e.history??[]){if(re(s)){let e=s.data;c.push({spanId:e.spanId,op:e.op,durationMs:e.durationMs});continue}if(!ne(s))continue;let e=s.data,u=e.source??_,d=te(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:v(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:y(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 x(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&!(e instanceof Date)}function S(e){return typeof e==`string`&&e.length>0?e:void 0}function C(e){return typeof e==`number`&&Number.isFinite(e)&&e>=0}function w(e){return e===`unknown`||e===`estimated`||e===`exact`}function ie(e){return(e.promptTokens===void 0||C(e.promptTokens))&&(e.completionTokens===void 0||C(e.completionTokens))}function T(e){let t=e.costStatus;return w(t)?t===`unknown`?e.costUsd===void 0:C(e.costUsd):!1}function E(e){return C(e.totalTokens)&&C(e.contextUsedTokens)&&C(e.contextMaxTokens)&&C(e.contextUsedPercentage)}function D(e){return e===`cli`||e===`desktop-app`||e===`browser`||e===`remote`||e===`unknown`}function O(e){if(!x(e))return;let t=e.scope;if(t!==`main`&&t!==`subagent`&&t!==`background`&&t!==`tool`&&t!==`command`&&t!==`skill`)return;let n=S(e.id),r=S(e.label);return{scope:t,...n?{id:n}:{},...r?{label:r}:{}}}function k(e){if(!x(e)||e.kind!==`exact`&&e.kind!==`estimated`||e.scope!==`turn`||!ie(e)||!T(e)||!E(e)||!w(e.costStatus))return;let t=O(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,...C(e.promptTokens)?{promptTokens:e.promptTokens}:{},...C(e.completionTokens)?{completionTokens:e.completionTokens}:{},...C(e.costUsd)?{costUsd:e.costUsd}:{},...t?{source:t}:{}}}function ae(e){if(!x(e))return;let t=S(e.providerId),n=S(e.modelId),{promptTokens:r,completionTokens:i,totalTokens:a,costStatus:o,costUsd:s}=e;if(!(!t||!n||!C(r)||!C(i)||!C(a)||o!==`unknown`&&o!==`estimated`||(o===`estimated`?!C(s):s!==void 0)))return{providerId:t,modelId:n,promptTokens:r,completionTokens:i,totalTokens:a,costStatus:o,...o===`estimated`&&C(s)?{costUsd:s}:{}}}function oe(e){if(!Array.isArray(e)||e.length===0)return;let t=e.map(ae);return t.every(e=>e!==void 0)?t:void 0}function A(e){if(!x(e))return;let t=S(e.usageObservationId),n=S(e.turnId),r=e.outcome;if(!t||!n||r!==`success`&&r!==`failure`&&r!==`interrupted`)return;let i=e.surface,a=O(e.source),o=k(e.usage),s=oe(e.modelShares);return{usageObservationId:t,turnId:n,outcome:r,...S(e.modelId)?{modelId:S(e.modelId)}:{},...S(e.providerId)?{providerId:S(e.providerId)}:{},...s?{modelShares:s}:{},...D(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=k(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=k(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 se(e){let t=[];for(let n of e.history??[]){let r=n.data;if(n.type!==`tool-start`||!x(r))continue;let i=S(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 N(){return{sessions:new Set,observations:0,turns:0,promptTokens:0,completionTokens:0,totalTokens:0,costUsd:0,hasUnknownCost:!1,hasEstimatedCost:!1}}function P(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 ce(e,t,n){let[r,i,a]=P(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 F(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,!t.legacy&&t.observation.source?.scope!==`tool`&&(e.turns+=1),e.promptTokens+=n?.promptTokens??0,e.completionTokens+=n?.completionTokens??0,e.totalTokens+=n?.totalTokens??0,e.costUsd+=n?.costUsd??0,(!n||n.costStatus===`unknown`)&&(e.hasUnknownCost=!0),n?.costStatus===`estimated`&&(e.hasEstimatedCost=!0)}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.observations===0||e.hasUnknownCost?`unknown`:e.hasEstimatedCost?`estimated`:`exact`}}function z(e,t){let n=new Map;for(let r of e){let e=t(r),i=n.get(e)??N();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(P(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 se(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,t,n){let r=N(),i=new Map(t.map(e=>[e,N()]));for(let t of n.observations)I(r,t),I(i.get(P(t.at,e.request.timezone)),t);for(let t of n.activities)r.sessions.add(t.sessionId),i.get(P(t.at,e.request.timezone))?.sessions.add(t.sessionId);return{totals:r,byDay:i}}function W(e){let t=e.request.period===`30d`?30:7,n=ce(e.now,e.request.timezone,t),r=H(e,new Set(n)),{totals:i,byDay:a}=U(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:F(r.activities),sessionIds:[...i.sessions].sort(),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 le=(1n<<64n)-1n,G=/^(?!0{32}$)[0-9a-f]{32}$/,K=/^(?!0{16}$)[0-9a-f]{16}$/,ue=[`promptExecutionStartedAt`,`promptExecutionEndedAt`,`promptExecutionOutcome`,`promptExecutionTraceId`,`promptExecutionSpanId`];function q(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function J(e,t){e.set(t,(e.get(t)??0)+1)}function Y(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<=le?n:void 0}function de(e){let t=Y(e.promptExecutionStartedAt),n=Y(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`||!G.test(i)||typeof a!=`string`||!K.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:`robota.prompt_execution`,kind:1,startTimeUnixNano:String(t),endTimeUnixNano:String(n),attributes:[{key:`robota.prompt.outcome`,value:{stringValue:r}}],status:{code:r===`success`?1:r===`failure`?2:0}}}function fe(e,t){if(!q(e))return;let n=e.traceId,r=e.parentSpanId,i=e.spanId,a=Y(e.startedAt),o=Y(e.endedAt),s=e.outcome;if(!(typeof n!=`string`||!G.test(n)||typeof r!=`string`||!K.test(r)||typeof i!=`string`||!K.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`?`robota.provider_call`:`robota.tool_body`,kind:1,startTimeUnixNano:String(a),endTimeUnixNano:String(o),attributes:[{key:t===`provider`?`robota.provider.outcome`:`robota.tool.outcome`,value:{stringValue:s}}],status:{code:s===`success`?1:s===`failure`?2:0}}}function pe(e){if(!q(e))return;let t=e.traceId,n=e.spanId;return typeof t==`string`&&G.test(t)&&typeof n==`string`&&K.test(n)?`${t}:${n}`:void 0}function X(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=0}function me(n,r,i,a){if(!q(r)||r.disposition===void 0)return i.legacy+=1,n;if(r.disposition!==`invoked`)return i.invalid+=1,n;a.invoked+=1;let o=r.usageProvenance;if(o===`partial`)return i.partial+=1,a.unknownCost+=1,n;if(o===`absent`||o===void 0)return i.absent+=1,a.unknownCost+=1,n;let s=r.promptTokens,c=r.completionTokens,l=r.totalTokens;if(o!==`complete`||!X(s)||!X(c)||!Number.isSafeInteger(s+c)||l!==s+c)return i.invalid+=1,a.unknownCost+=1,n;a.completeUsage+=1,a.inputTokens+=s,a.outputTokens+=c;let u=[...n.attributes,{key:`robota.provider.usage.input_tokens`,value:{intValue:String(s)}},{key:`robota.provider.usage.output_tokens`,value:{intValue:String(c)}}],d=r.modelId,f=typeof d==`string`&&d.length>0&&d.length<=128&&[...d].every(e=>e.charCodeAt(0)>=32)?t(d,s,c):void 0;return f===void 0||!Number.isFinite(f)||f<0?a.unknownCost+=1:(a.estimatedCostUsd+=f,typeof d==`string`&&Object.hasOwn(e,d)?a.exactPriceMatches+=1:a.familyPriceMatches+=1,u.push({key:`robota.provider.cost.usd.estimated`,value:{doubleValue:f}})),{...n,attributes:u}}function Z(e,t){let n=[],r=new Map,i=new Map;for(let t of e)for(let e of t.history??[]){if(e.type!==`usage-observation`)continue;let a=e.data,o=q(a)?a.usageObservationId:void 0,s=q(a)?a.promptExecutionTraceId:void 0,c=typeof o==`string`&&o.length>0?JSON.stringify([t.id,o]):void 0,l=typeof s==`string`&&G.test(s)?s:void 0;c&&J(r,c),l&&J(i,l),n.push({record:t,data:a,observationKey:c,traceId:l})}let a=[],o=new Map,s=0,c=0,l=0;for(let e of n){if(e.observationKey&&(r.get(e.observationKey)??0)>1||e.traceId&&(i.get(e.traceId)??0)>1){l+=1;continue}let t=e.data;if(!q(t)){c+=1;continue}if(ue.every(e=>t[e]===void 0)){s+=1;continue}let n=de(t);if(!n){c+=1;continue}a.push(n);let u=o.get(e.record)??new Map;u.set(n.traceId,n),o.set(e.record,u)}let u=[],d=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=pe(e.data),i=fe(e.data,n);r&&J(d,r),u.push({record:t,span:i,identity:r,kind:n,data:e.data})}let f={provider:{exported:0,invalid:0,orphaned:0,duplicate:0},tool:{exported:0,invalid:0,orphaned:0,duplicate:0}},p={legacy:0,invalid:0,partial:0,absent:0,nonInvoked:0},m={invoked:0,completeUsage:0,inputTokens:0,outputTokens:0,estimatedCostUsd:0,unknownCost:0,exactPriceMatches:0,familyPriceMatches:0};for(let e of u){let{span:t}=e,n=f[e.kind];if(e.identity&&(d.get(e.identity)??0)>1){n.duplicate+=1;continue}if(!t){n.invalid+=1;continue}let r=o.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`&&q(e.data)&&(e.data.disposition===`cache-hit`||e.data.disposition===`preflight-refused`)){p.nonInvoked+=1,(e.data.promptTokens!==void 0||e.data.completionTokens!==void 0||e.data.totalTokens!==void 0||e.data.usageProvenance===`complete`)&&(p.invalid+=1);continue}a.push(e.kind===`provider`?me(t,e.data,p,m):t),n.exported+=1}return{payload:{resourceSpans:a.length?[{resource:{attributes:[{key:`service.name`,value:{stringValue:`robota`}},{key:`service.version`,value:{stringValue:t}}]},scopeSpans:[{scope:{name:`@robota-sdk/agent-session-analytics`},spans:a}]}]:[]},coverage:{exported:a.length-f.provider.exported-f.tool.exported,missing:s,invalid:c,duplicate:l,providerChildren:f.provider,providerUsage:p,toolChildren:f.tool},callMetrics:m}}function Q(e,t,n){let r=new Set,i=0,a=0,o=0,s=0,c=0,l=0,u=0,d=0;for(let t of e)for(let e of M(t)){if(r.has(e.id))continue;r.add(e.id),e.legacy||(i+=1);let t=e.observation.usage;if(!t){u+=1,c+=1;continue}a+=t.totalTokens,o+=t.promptTokens??0,s+=t.completionTokens??0,(t.promptTokens===void 0||t.completionTokens===void 0)&&(c+=1),t.costStatus===`unknown`||t.costUsd===void 0?u+=1:(l+=t.costUsd,t.costStatus===`estimated`&&(d+=1))}let f=Z(e,n),p=f.callMetrics,m=String(BigInt(t.getTime())*1000000n),h=(e,t,n)=>({name:e,unit:t,gauge:{dataPoints:[{timeUnixNano:m,asDouble:n}]}});return{resourceMetrics:[{resource:{attributes:[{key:`service.name`,value:{stringValue:`robota`}},{key:`service.version`,value:{stringValue:n}}]},scopeMetrics:[{scope:{name:`@robota-sdk/agent-session-analytics`},metrics:[h(`robota.session.count`,`{session}`,e.length),h(`robota.turn.count`,`{turn}`,i),h(`robota.token.total`,`{token}`,a),h(`robota.token.input.known`,`{token}`,o),h(`robota.token.output.known`,`{token}`,s),h(`robota.token.split_unknown_observations`,`{observation}`,c),h(`robota.cost.usd.known`,`USD`,l),h(`robota.cost.unknown_observations`,`{observation}`,u),h(`robota.cost.estimated_observations`,`{observation}`,d),h(`robota.provider_call.count`,`{call}`,p.invoked),h(`robota.provider_call.usage.complete_count`,`{call}`,p.completeUsage),h(`robota.provider_call.token.input.known`,`{token}`,p.inputTokens),h(`robota.provider_call.token.output.known`,`{token}`,p.outputTokens),h(`robota.provider_call.cost.usd.estimated`,`USD`,p.estimatedCostUsd),h(`robota.provider_call.cost.unknown_count`,`{call}`,p.unknownCost),h(`robota.provider_call.usage.partial_count`,`{call}`,f.coverage.providerUsage.partial),h(`robota.provider_call.usage.invalid_count`,`{call}`,f.coverage.providerUsage.invalid),h(`robota.provider_call.usage.legacy_count`,`{call}`,f.coverage.providerUsage.legacy),h(`robota.provider_call.price.exact_id_count`,`{call}`,p.exactPriceMatches),h(`robota.provider_call.price.family_fallback_count`,`{call}`,p.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){let r=Z(e,t),i=r.payload.resourceSpans[0]?.scopeSpans[0]?.spans??[];if(i.length===0)return{exported:0,coverage:r.coverage,payload:{resourceLogs:[]}};let a=ge(n),o=i.map(e=>{let t=e.attributes.find(e=>e.key===`robota.prompt.outcome`||e.key===`robota.provider.outcome`||e.key===`robota.tool.outcome`),n=e.name===`robota.prompt_execution`?`robota.prompt_execution.completed`:e.name===`robota.provider_call`?`robota.provider_call.completed`:`robota.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:a,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:o.length,coverage:r.coverage,payload:{resourceLogs:[{resource:r.payload.resourceSpans[0].resource,scopeLogs:[{scope:{name:`@robota-sdk/agent-session-analytics`},logRecords:o}]}]}}}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(`
|
|
4
|
+
`)}const _={scope:`main`,label:`main thread`};function te(e){return`${e.scope}:${e.id??``}`}function v(e){return e.label??(e.id?`${e.scope} ${e.id}`:e.scope)}function ne(e){return e.type===`usage-summary`&&typeof e.data==`object`&&e.data!==null}function re(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 y(e,t){return t<=0?0:Math.round(e/t*1e3)/10}function b(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:v(e),spans:c,totalDurationMs:c.reduce((e,t)=>e+t.durationMs,0)}),c=[])};for(let s of e.history??[]){if(re(s)){let e=s.data;c.push({spanId:e.spanId,op:e.op,durationMs:e.durationMs});continue}if(!ne(s))continue;let e=s.data,u=e.source??_,d=te(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:v(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:y(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 x(e){return typeof e==`object`&&!!e&&!Array.isArray(e)&&!(e instanceof Date)}function S(e){return typeof e==`string`&&e.length>0?e:void 0}function C(e){return typeof e==`number`&&Number.isFinite(e)&&e>=0}function w(e){return e===`unknown`||e===`estimated`||e===`exact`}function ie(e){return(e.promptTokens===void 0||C(e.promptTokens))&&(e.completionTokens===void 0||C(e.completionTokens))}function T(e){let t=e.costStatus;return w(t)?t===`unknown`?e.costUsd===void 0:C(e.costUsd):!1}function E(e){return C(e.totalTokens)&&C(e.contextUsedTokens)&&C(e.contextMaxTokens)&&C(e.contextUsedPercentage)}function D(e){return e===`cli`||e===`desktop-app`||e===`browser`||e===`remote`||e===`attach`||e===`unknown`}function O(e){if(!x(e))return;let t=e.scope;if(t!==`main`&&t!==`subagent`&&t!==`background`&&t!==`tool`&&t!==`command`&&t!==`skill`)return;let n=S(e.id),r=S(e.label);return{scope:t,...n?{id:n}:{},...r?{label:r}:{}}}function k(e){if(!x(e)||e.kind!==`exact`&&e.kind!==`estimated`||e.scope!==`turn`||!ie(e)||!T(e)||!E(e)||!w(e.costStatus))return;let t=O(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,...C(e.promptTokens)?{promptTokens:e.promptTokens}:{},...C(e.completionTokens)?{completionTokens:e.completionTokens}:{},...C(e.costUsd)?{costUsd:e.costUsd}:{},...t?{source:t}:{}}}function ae(e){if(!x(e))return;let t=S(e.providerId),n=S(e.modelId),{promptTokens:r,completionTokens:i,totalTokens:a,costStatus:o,costUsd:s}=e;if(!(!t||!n||!C(r)||!C(i)||!C(a)||o!==`unknown`&&o!==`estimated`||(o===`estimated`?!C(s):s!==void 0)))return{providerId:t,modelId:n,promptTokens:r,completionTokens:i,totalTokens:a,costStatus:o,...o===`estimated`&&C(s)?{costUsd:s}:{}}}function oe(e){if(!Array.isArray(e)||e.length===0)return;let t=e.map(ae);return t.every(e=>e!==void 0)?t:void 0}function A(e){if(!x(e))return;let t=S(e.usageObservationId),n=S(e.turnId),r=e.outcome;if(!t||!n||r!==`success`&&r!==`failure`&&r!==`interrupted`)return;let i=e.surface,a=O(e.source),o=k(e.usage),s=oe(e.modelShares);return{usageObservationId:t,turnId:n,outcome:r,...S(e.modelId)?{modelId:S(e.modelId)}:{},...S(e.providerId)?{providerId:S(e.providerId)}:{},...s?{modelShares:s}:{},...D(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=k(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=k(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 se(e){let t=[];for(let n of e.history??[]){let r=n.data;if(n.type!==`tool-start`||!x(r))continue;let i=S(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 N(){return{sessions:new Set,observations:0,turns:0,promptTokens:0,completionTokens:0,totalTokens:0,costUsd:0,hasUnknownCost:!1,hasEstimatedCost:!1}}function P(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 ce(e,t,n){let[r,i,a]=P(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 F(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,!t.legacy&&t.observation.source?.scope!==`tool`&&(e.turns+=1),e.promptTokens+=n?.promptTokens??0,e.completionTokens+=n?.completionTokens??0,e.totalTokens+=n?.totalTokens??0,e.costUsd+=n?.costUsd??0,(!n||n.costStatus===`unknown`)&&(e.hasUnknownCost=!0),n?.costStatus===`estimated`&&(e.hasEstimatedCost=!0)}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.observations===0||e.hasUnknownCost?`unknown`:e.hasEstimatedCost?`estimated`:`exact`}}function z(e,t){let n=new Map;for(let r of e){let e=t(r),i=n.get(e)??N();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(P(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 se(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,t,n){let r=N(),i=new Map(t.map(e=>[e,N()]));for(let t of n.observations)I(r,t),I(i.get(P(t.at,e.request.timezone)),t);for(let t of n.activities)r.sessions.add(t.sessionId),i.get(P(t.at,e.request.timezone))?.sessions.add(t.sessionId);return{totals:r,byDay:i}}function W(e){let t=e.request.period===`30d`?30:7,n=ce(e.now,e.request.timezone,t),r=H(e,new Set(n)),{totals:i,byDay:a}=U(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:F(r.activities),sessionIds:[...i.sessions].sort(),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 le=(1n<<64n)-1n,G=/^(?!0{32}$)[0-9a-f]{32}$/,K=/^(?!0{16}$)[0-9a-f]{16}$/,ue=[`promptExecutionStartedAt`,`promptExecutionEndedAt`,`promptExecutionOutcome`,`promptExecutionTraceId`,`promptExecutionSpanId`];function q(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function J(e,t){e.set(t,(e.get(t)??0)+1)}function Y(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<=le?n:void 0}function de(e){let t=Y(e.promptExecutionStartedAt),n=Y(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`||!G.test(i)||typeof a!=`string`||!K.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:`robota.prompt_execution`,kind:1,startTimeUnixNano:String(t),endTimeUnixNano:String(n),attributes:[{key:`robota.prompt.outcome`,value:{stringValue:r}}],status:{code:r===`success`?1:r===`failure`?2:0}}}function fe(e,t){if(!q(e))return;let n=e.traceId,r=e.parentSpanId,i=e.spanId,a=Y(e.startedAt),o=Y(e.endedAt),s=e.outcome;if(!(typeof n!=`string`||!G.test(n)||typeof r!=`string`||!K.test(r)||typeof i!=`string`||!K.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`?`robota.provider_call`:`robota.tool_body`,kind:1,startTimeUnixNano:String(a),endTimeUnixNano:String(o),attributes:[{key:t===`provider`?`robota.provider.outcome`:`robota.tool.outcome`,value:{stringValue:s}}],status:{code:s===`success`?1:s===`failure`?2:0}}}function pe(e){if(!q(e))return;let t=e.traceId,n=e.spanId;return typeof t==`string`&&G.test(t)&&typeof n==`string`&&K.test(n)?`${t}:${n}`:void 0}function X(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=0}function me(n,r,i,a){if(!q(r)||r.disposition===void 0)return i.legacy+=1,n;if(r.disposition!==`invoked`)return i.invalid+=1,n;a.invoked+=1;let o=r.usageProvenance;if(o===`partial`)return i.partial+=1,a.unknownCost+=1,n;if(o===`absent`||o===void 0)return i.absent+=1,a.unknownCost+=1,n;let s=r.promptTokens,c=r.completionTokens,l=r.totalTokens;if(o!==`complete`||!X(s)||!X(c)||!Number.isSafeInteger(s+c)||l!==s+c)return i.invalid+=1,a.unknownCost+=1,n;a.completeUsage+=1,a.inputTokens+=s,a.outputTokens+=c;let u=[...n.attributes,{key:`robota.provider.usage.input_tokens`,value:{intValue:String(s)}},{key:`robota.provider.usage.output_tokens`,value:{intValue:String(c)}}],d=r.modelId,f=typeof d==`string`&&d.length>0&&d.length<=128&&[...d].every(e=>e.charCodeAt(0)>=32)?t(d,s,c):void 0;return f===void 0||!Number.isFinite(f)||f<0?a.unknownCost+=1:(a.estimatedCostUsd+=f,typeof d==`string`&&Object.hasOwn(e,d)?a.exactPriceMatches+=1:a.familyPriceMatches+=1,u.push({key:`robota.provider.cost.usd.estimated`,value:{doubleValue:f}})),{...n,attributes:u}}function Z(e,t){let n=[],r=new Map,i=new Map;for(let t of e)for(let e of t.history??[]){if(e.type!==`usage-observation`)continue;let a=e.data,o=q(a)?a.usageObservationId:void 0,s=q(a)?a.promptExecutionTraceId:void 0,c=typeof o==`string`&&o.length>0?JSON.stringify([t.id,o]):void 0,l=typeof s==`string`&&G.test(s)?s:void 0;c&&J(r,c),l&&J(i,l),n.push({record:t,data:a,observationKey:c,traceId:l})}let a=[],o=new Map,s=0,c=0,l=0;for(let e of n){if(e.observationKey&&(r.get(e.observationKey)??0)>1||e.traceId&&(i.get(e.traceId)??0)>1){l+=1;continue}let t=e.data;if(!q(t)){c+=1;continue}if(ue.every(e=>t[e]===void 0)){s+=1;continue}let n=de(t);if(!n){c+=1;continue}a.push(n);let u=o.get(e.record)??new Map;u.set(n.traceId,n),o.set(e.record,u)}let u=[],d=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=pe(e.data),i=fe(e.data,n);r&&J(d,r),u.push({record:t,span:i,identity:r,kind:n,data:e.data})}let f={provider:{exported:0,invalid:0,orphaned:0,duplicate:0},tool:{exported:0,invalid:0,orphaned:0,duplicate:0}},p={legacy:0,invalid:0,partial:0,absent:0,nonInvoked:0},m={invoked:0,completeUsage:0,inputTokens:0,outputTokens:0,estimatedCostUsd:0,unknownCost:0,exactPriceMatches:0,familyPriceMatches:0};for(let e of u){let{span:t}=e,n=f[e.kind];if(e.identity&&(d.get(e.identity)??0)>1){n.duplicate+=1;continue}if(!t){n.invalid+=1;continue}let r=o.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`&&q(e.data)&&(e.data.disposition===`cache-hit`||e.data.disposition===`preflight-refused`)){p.nonInvoked+=1,(e.data.promptTokens!==void 0||e.data.completionTokens!==void 0||e.data.totalTokens!==void 0||e.data.usageProvenance===`complete`)&&(p.invalid+=1);continue}a.push(e.kind===`provider`?me(t,e.data,p,m):t),n.exported+=1}return{payload:{resourceSpans:a.length?[{resource:{attributes:[{key:`service.name`,value:{stringValue:`robota`}},{key:`service.version`,value:{stringValue:t}}]},scopeSpans:[{scope:{name:`@robota-sdk/agent-session-analytics`},spans:a}]}]:[]},coverage:{exported:a.length-f.provider.exported-f.tool.exported,missing:s,invalid:c,duplicate:l,providerChildren:f.provider,providerUsage:p,toolChildren:f.tool},callMetrics:m}}function Q(e,t,n){let r=new Set,i=0,a=0,o=0,s=0,c=0,l=0,u=0,d=0;for(let t of e)for(let e of M(t)){if(r.has(e.id))continue;r.add(e.id),e.legacy||(i+=1);let t=e.observation.usage;if(!t){u+=1,c+=1;continue}a+=t.totalTokens,o+=t.promptTokens??0,s+=t.completionTokens??0,(t.promptTokens===void 0||t.completionTokens===void 0)&&(c+=1),t.costStatus===`unknown`||t.costUsd===void 0?u+=1:(l+=t.costUsd,t.costStatus===`estimated`&&(d+=1))}let f=Z(e,n),p=f.callMetrics,m=String(BigInt(t.getTime())*1000000n),h=(e,t,n)=>({name:e,unit:t,gauge:{dataPoints:[{timeUnixNano:m,asDouble:n}]}});return{resourceMetrics:[{resource:{attributes:[{key:`service.name`,value:{stringValue:`robota`}},{key:`service.version`,value:{stringValue:n}}]},scopeMetrics:[{scope:{name:`@robota-sdk/agent-session-analytics`},metrics:[h(`robota.session.count`,`{session}`,e.length),h(`robota.turn.count`,`{turn}`,i),h(`robota.token.total`,`{token}`,a),h(`robota.token.input.known`,`{token}`,o),h(`robota.token.output.known`,`{token}`,s),h(`robota.token.split_unknown_observations`,`{observation}`,c),h(`robota.cost.usd.known`,`USD`,l),h(`robota.cost.unknown_observations`,`{observation}`,u),h(`robota.cost.estimated_observations`,`{observation}`,d),h(`robota.provider_call.count`,`{call}`,p.invoked),h(`robota.provider_call.usage.complete_count`,`{call}`,p.completeUsage),h(`robota.provider_call.token.input.known`,`{token}`,p.inputTokens),h(`robota.provider_call.token.output.known`,`{token}`,p.outputTokens),h(`robota.provider_call.cost.usd.estimated`,`USD`,p.estimatedCostUsd),h(`robota.provider_call.cost.unknown_count`,`{call}`,p.unknownCost),h(`robota.provider_call.usage.partial_count`,`{call}`,f.coverage.providerUsage.partial),h(`robota.provider_call.usage.invalid_count`,`{call}`,f.coverage.providerUsage.invalid),h(`robota.provider_call.usage.legacy_count`,`{call}`,f.coverage.providerUsage.legacy),h(`robota.provider_call.price.exact_id_count`,`{call}`,p.exactPriceMatches),h(`robota.provider_call.price.family_fallback_count`,`{call}`,p.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){let r=Z(e,t),i=r.payload.resourceSpans[0]?.scopeSpans[0]?.spans??[];if(i.length===0)return{exported:0,coverage:r.coverage,payload:{resourceLogs:[]}};let a=ge(n),o=i.map(e=>{let t=e.attributes.find(e=>e.key===`robota.prompt.outcome`||e.key===`robota.provider.outcome`||e.key===`robota.tool.outcome`),n=e.name===`robota.prompt_execution`?`robota.prompt_execution.completed`:e.name===`robota.provider_call`?`robota.provider_call.completed`:`robota.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:a,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:o.length,coverage:r.coverage,payload:{resourceLogs:[{resource:r.payload.resourceSpans[0].resource,scopeLogs:[{scope:{name:`@robota-sdk/agent-session-analytics`},logRecords:o}]}]}}}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
5
|
`)}export{c as aggregateReports,s as analyzeSession,a as computeTimingIntervals,_e as createOtlpPromptEvents,Z as createOtlpPromptRootTraces,Q as createOtlpUsageSnapshot,ee as formatAggregateReport,ve as formatPersonalUsageReport,g as formatSingleSession,d as formatUsageReport,n as gapMs,W as summarizePersonalUsage,b as summarizeUsageBySource};
|
|
6
6
|
//# sourceMappingURL=index.js.map
|
package/dist/node/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["sourceKey","isRecord","MAX_UINT64"],"sources":["../../src/analyze.ts","../../src/report.ts","../../src/usage.ts","../../src/personal-usage-decode.ts","../../src/personal-usage.ts","../../src/otlp-prompt-root-traces.ts","../../src/otlp-usage-snapshot.ts","../../src/otlp-prompt-events.ts","../../src/personal-usage-format.ts"],"sourcesContent":["/**\n * Session-log timing-interval computation and aggregation.\n *\n * Operates on canonical history entries (`IHistoryEntry`); persisted timestamps arrive as ISO\n * strings at runtime (JSON has no Date), so all timestamp math goes through `new Date(...)`.\n */\n\nimport type {\n IAggregateReport,\n TSessionAnalysisInput,\n ISessionTimingReport,\n ITimingInterval,\n ITimingStats,\n TIntervalKind,\n} from './types.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\n\nconst SLOW_THRESHOLD_MS = 10_000;\n\n/** Gap in milliseconds between two timestamps (ISO string or Date). */\nexport function gapMs(from: string | Date, to: string | Date): number {\n return new Date(to).getTime() - new Date(from).getTime();\n}\n\nfunction toIso(value: string | Date): string {\n return new Date(value).toISOString();\n}\n\nfunction makeInterval(\n kind: TIntervalKind,\n fromType: string,\n toType: string,\n fromTimestamp: string | Date,\n toTimestamp: string | Date,\n turnIndex: number,\n): ITimingInterval {\n return {\n kind,\n fromType,\n toType,\n fromTimestamp: toIso(fromTimestamp),\n toTimestamp: toIso(toTimestamp),\n durationMs: gapMs(fromTimestamp, toTimestamp),\n turnIndex,\n };\n}\n\n/**\n * Compute timing intervals from a history entry array.\n *\n * Strategy: scan the timeline turn by turn. A \"turn\" starts with a user message. After user, we\n * expect zero or more tool-start → tool-end cycles, optionally a tool-summary, then an assistant\n * message.\n */\nexport function computeTimingIntervals(history: readonly IHistoryEntry[]): ITimingInterval[] {\n const intervals: ITimingInterval[] = [];\n let turnIndex = 0;\n\n let i = 0;\n while (i < history.length) {\n const entry = history[i];\n if (!entry) {\n i++;\n continue;\n }\n\n if (entry.category === 'chat' && entry.type === 'user') {\n turnIndex++;\n const userTs = entry.timestamp;\n let foundFirstTool = false;\n let lastToolEndTs: IHistoryEntry['timestamp'] | null = null;\n let j = i + 1;\n\n while (j < history.length) {\n const next = history[j];\n if (!next) {\n j++;\n continue;\n }\n\n if (next.category === 'chat' && next.type === 'user') {\n break;\n }\n\n if (next.category === 'event' && next.type === 'tool-start') {\n if (!foundFirstTool) {\n intervals.push(\n makeInterval(\n 'user_to_first_tool',\n 'user',\n 'tool-start',\n userTs,\n next.timestamp,\n turnIndex,\n ),\n );\n foundFirstTool = true;\n } else if (lastToolEndTs !== null) {\n intervals.push(\n makeInterval(\n 'llm_between_tools',\n 'tool-end',\n 'tool-start',\n lastToolEndTs,\n next.timestamp,\n turnIndex,\n ),\n );\n }\n lastToolEndTs = null;\n j++;\n continue;\n }\n\n if (next.category === 'event' && next.type === 'tool-end') {\n const prev = history[j - 1];\n if (prev && prev.category === 'event' && prev.type === 'tool-start') {\n intervals.push(\n makeInterval(\n 'tool_exec',\n 'tool-start',\n 'tool-end',\n prev.timestamp,\n next.timestamp,\n turnIndex,\n ),\n );\n }\n lastToolEndTs = next.timestamp;\n j++;\n continue;\n }\n\n if (next.category === 'event' && next.type === 'tool-summary') {\n if (lastToolEndTs !== null) {\n intervals.push(\n makeInterval(\n 'llm_final_response',\n 'tool-end',\n 'tool-summary',\n lastToolEndTs,\n next.timestamp,\n turnIndex,\n ),\n );\n }\n j++;\n continue;\n }\n\n if (next.category === 'chat' && next.type === 'assistant') {\n if (!foundFirstTool) {\n intervals.push(\n makeInterval(\n 'user_to_assistant',\n 'user',\n 'assistant',\n userTs,\n next.timestamp,\n turnIndex,\n ),\n );\n } else if (lastToolEndTs !== null) {\n intervals.push(\n makeInterval(\n 'llm_final_response',\n 'tool-end',\n 'assistant',\n lastToolEndTs,\n next.timestamp,\n turnIndex,\n ),\n );\n }\n j++;\n break;\n }\n\n j++;\n }\n\n i = j;\n } else {\n i++;\n }\n }\n\n return intervals;\n}\n\nfunction computeStats(intervals: ITimingInterval[]): ITimingStats {\n const llm = intervals.filter(\n (iv) =>\n iv.kind === 'user_to_first_tool' ||\n iv.kind === 'user_to_assistant' ||\n iv.kind === 'llm_between_tools' ||\n iv.kind === 'llm_final_response',\n );\n const tool = intervals.filter((iv) => iv.kind === 'tool_exec');\n const direct = intervals.filter((iv) => iv.kind === 'user_to_assistant');\n\n const avg = (arr: ITimingInterval[]): number =>\n arr.length ? Math.round(arr.reduce((s, iv) => s + iv.durationMs, 0) / arr.length) : 0;\n const max = (arr: ITimingInterval[]): number =>\n arr.length ? Math.max(...arr.map((iv) => iv.durationMs)) : 0;\n const median = (arr: ITimingInterval[]): number => {\n if (!arr.length) return 0;\n const sorted = [...arr].sort((a, b) => a.durationMs - b.durationMs);\n const mid = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 0\n ? Math.round(((sorted[mid - 1]?.durationMs ?? 0) + (sorted[mid]?.durationMs ?? 0)) / 2)\n : (sorted[mid]?.durationMs ?? 0);\n };\n\n return {\n llmWaitMs: {\n avg: avg(llm),\n max: max(llm),\n total: llm.reduce((s, iv) => s + iv.durationMs, 0),\n count: llm.length,\n },\n toolExecMs: { avg: avg(tool), max: max(tool), median: median(tool), count: tool.length },\n userToAssistantMs: { avg: avg(direct), max: max(direct), count: direct.length },\n };\n}\n\n/** Compute the timing report for a single session record. */\nexport function analyzeSession(record: TSessionAnalysisInput): ISessionTimingReport {\n const history = record.history ?? [];\n const intervals = computeTimingIntervals(history);\n const slowIntervals = intervals.filter((iv) => iv.durationMs >= SLOW_THRESHOLD_MS);\n\n return {\n sessionId: record.id,\n cwd: record.cwd,\n createdAt: record.createdAt,\n totalIntervals: intervals.length,\n intervals,\n slowIntervals,\n stats: computeStats(intervals),\n };\n}\n\n/** Aggregate multiple single-session reports into a fleet-level summary. */\nexport function aggregateReports(reports: readonly ISessionTimingReport[]): IAggregateReport {\n const llmIntervals = reports.flatMap((r) =>\n r.intervals.filter(\n (iv) =>\n iv.kind === 'user_to_first_tool' ||\n iv.kind === 'user_to_assistant' ||\n iv.kind === 'llm_between_tools' ||\n iv.kind === 'llm_final_response',\n ),\n );\n const toolIntervals = reports.flatMap((r) => r.intervals.filter((iv) => iv.kind === 'tool_exec'));\n\n const avgLlmResponseMs = llmIntervals.length\n ? Math.round(llmIntervals.reduce((s, iv) => s + iv.durationMs, 0) / llmIntervals.length)\n : 0;\n const avgToolExecMs = toolIntervals.length\n ? Math.round(toolIntervals.reduce((s, iv) => s + iv.durationMs, 0) / toolIntervals.length)\n : 0;\n\n let maxSingleDelayMs = 0;\n let maxSingleDelaySession = '';\n let maxSingleDelayTurn = 0;\n let maxSingleDelayKind = '';\n\n for (const r of reports) {\n for (const iv of r.intervals) {\n if (iv.durationMs > maxSingleDelayMs) {\n maxSingleDelayMs = iv.durationMs;\n maxSingleDelaySession = r.sessionId;\n maxSingleDelayTurn = iv.turnIndex;\n maxSingleDelayKind = iv.kind;\n }\n }\n }\n\n const dates = reports.map((r) => r.createdAt).sort();\n\n return {\n sessionCount: reports.length,\n fromDate: dates[0] ?? '',\n toDate: dates[dates.length - 1] ?? '',\n avgLlmResponseMs,\n avgToolExecMs,\n maxSingleDelayMs,\n maxSingleDelaySession,\n maxSingleDelayTurn,\n maxSingleDelayKind,\n };\n}\n","/**\n * Text report formatters for session timing analysis. Pure — every function returns a string;\n * callers (e.g. a CLI command) own writing to stdout.\n */\n\nimport type { IAggregateReport, ISessionTimingReport, ITimingInterval } from './types.js';\nimport type { IUsageBySourceReport } from './usage.js';\n\nfunction fmtTokens(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return `${n}`;\n}\n\n/** SELFHOST-004: `~` marks an estimate or incomplete aggregate, never an invoice amount. */\nfunction fmtCost(usd: number, exact: boolean): string {\n return `${exact ? '' : '~'}$${usd.toFixed(4)}`;\n}\n\n/**\n * ANALYTICS-001 + SELFHOST-004: render a per-source token+cost breakdown and the per-operation span\n * timeline — which part of the session burned the most tokens/cost, and where the wall-clock went.\n * Pure; the caller owns writing it (the headless CLI path for the TUI/GUI trace/cost view).\n */\nexport function formatUsageReport(report: IUsageBySourceReport): string {\n const lines: string[] = [];\n lines.push(`Token usage — session ${report.sessionId}`);\n lines.push(\n ` total ${fmtTokens(report.totalTokens)} (prompt ${fmtTokens(report.promptTokens)} · completion ${fmtTokens(report.completionTokens)})`,\n );\n lines.push(` cost ${fmtCost(report.costUsd, report.costExact)}`);\n if (report.bySource.length === 0) {\n lines.push(' (no usage recorded)');\n } else {\n lines.push(' by source (most tokens first):');\n for (const s of report.bySource) {\n lines.push(\n ` ${s.label.padEnd(24)} ${String(s.percentage).padStart(5)}% ${fmtTokens(s.totalTokens).padStart(7)} ${fmtCost(s.costUsd, s.costExact).padStart(10)} (${s.turns} turn${s.turns === 1 ? '' : 's'})`,\n );\n }\n if (report.topConsumer) {\n lines.push(` top consumer: ${report.topConsumer.label} (${report.topConsumer.percentage}%)`);\n }\n }\n\n // SELFHOST-004: the span timeline — sub-turn operations grouped under their owning turn.\n if (report.timeline.length > 0) {\n lines.push(' trace (per turn, sub-turn spans):');\n for (const turn of report.timeline) {\n lines.push(\n ` turn ${turn.turnIndex} — ${turn.label} · ${fmtMs(turn.totalDurationMs)} across ${turn.spans.length} op${turn.spans.length === 1 ? '' : 's'}`,\n );\n for (const span of turn.spans) {\n lines.push(` ${span.op.padEnd(22)} ${fmtMs(span.durationMs).padStart(8)}`);\n }\n }\n }\n return lines.join('\\n');\n}\n\nfunction fmtMs(ms: number): string {\n if (ms >= 1000) return `${(ms / 1000).toFixed(1)}s`;\n return `${ms}ms`;\n}\n\nfunction fmtDate(iso: string): string {\n return new Date(iso).toLocaleString('sv-SE', { timeZoneName: 'short' }).slice(0, 16);\n}\n\nfunction intervalLabel(iv: ITimingInterval): string {\n const map: Record<string, string> = {\n user_to_first_tool: 'user → tool-start',\n user_to_assistant: 'user → assistant',\n tool_exec: 'tool-start → tool-end',\n llm_between_tools: 'tool-end → tool-start',\n llm_final_response: 'tool-end → assistant/summary',\n };\n return map[iv.kind] ?? iv.kind;\n}\n\nfunction verdictLine(report: ISessionTimingReport): string {\n const { stats } = report;\n const totalMs = stats.llmWaitMs.total + stats.toolExecMs.avg * stats.toolExecMs.count;\n if (totalMs === 0) return 'Verdict: No timing data available.';\n const llmPct = Math.round((stats.llmWaitMs.total / totalMs) * 100);\n const toolPct = 100 - llmPct;\n return `Verdict: LLM API wait ${llmPct}% | Code processing ${toolPct}%`;\n}\n\nexport function formatSingleSession(report: ISessionTimingReport): string {\n const lines: string[] = [];\n const { stats } = report;\n\n lines.push(`Session: ${report.sessionId}`);\n lines.push(`Created: ${fmtDate(report.createdAt)} | cwd: ${report.cwd}`);\n lines.push('');\n lines.push('Timing Summary:');\n\n if (stats.llmWaitMs.count > 0) {\n lines.push(\n ` LLM API wait ${fmtMs(stats.llmWaitMs.avg).padStart(8)} avg | ${fmtMs(stats.llmWaitMs.max).padStart(8)} max`,\n );\n }\n if (stats.toolExecMs.count > 0) {\n lines.push(\n ` Tool execution ${fmtMs(stats.toolExecMs.avg).padStart(8)} avg | ${fmtMs(stats.toolExecMs.median).padStart(8)} median`,\n );\n }\n if (stats.userToAssistantMs.count > 0) {\n lines.push(` user → assistant ${fmtMs(stats.userToAssistantMs.avg).padStart(8)} avg`);\n }\n if (stats.llmWaitMs.count === 0 && stats.toolExecMs.count === 0) {\n lines.push(' (no intervals computed — session may have no history)');\n }\n\n if (report.slowIntervals.length > 0) {\n lines.push('');\n lines.push('Slow intervals (>10s):');\n for (const iv of report.slowIntervals) {\n lines.push(\n ` turn ${iv.turnIndex} ${intervalLabel(iv).padEnd(32)} ${fmtMs(iv.durationMs).padStart(8)}`,\n );\n }\n }\n\n lines.push('');\n lines.push(verdictLine(report));\n\n return lines.join('\\n');\n}\n\nexport function formatAggregateReport(aggregate: IAggregateReport): string {\n const lines: string[] = [];\n lines.push(\n `Analyzed ${aggregate.sessionCount} sessions (${fmtDate(aggregate.fromDate)} ~ ${fmtDate(aggregate.toDate)})`,\n );\n lines.push(` Avg LLM response: ${fmtMs(aggregate.avgLlmResponseMs)}`);\n lines.push(` Avg tool exec: ${fmtMs(aggregate.avgToolExecMs)}`);\n if (aggregate.maxSingleDelayMs > 0) {\n lines.push(\n ` Max single delay: ${fmtMs(aggregate.maxSingleDelayMs)} (${aggregate.maxSingleDelaySession}, turn ${aggregate.maxSingleDelayTurn}, ${aggregate.maxSingleDelayKind})`,\n );\n }\n return lines.join('\\n');\n}\n","/**\n * ANALYTICS-001: source-attributed token-usage analysis over a session record.\n *\n * The main session history records one `usage-summary` history entry per turn (an `IUsageSnapshot`,\n * created in agent-framework). This reducer aggregates those snapshots into a per-source breakdown —\n * answering \"which part of the session burned the most tokens?\" — so it can be reported to the user\n * and asserted by the testing framework (budget gates). Pure: no I/O, no process concerns.\n */\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type {\n ISpanEntry,\n IUsageSnapshot,\n IUsageSource,\n IUsageSourceTotals,\n IRunTraceSpan,\n IRunTraceTurn,\n IUsageBySourceReport,\n} from '@robota-sdk/agent-interface-analytics';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\n// SELFHOST-004: the trace/cost read-model is a boundary contract owned by `agent-interface-analytics`\n// (it crosses the sidecar boundary via a TServerMessage carrier). Re-exported here for consumers that\n// import it alongside the reducer that produces it.\nexport type {\n IUsageSourceTotals,\n IRunTraceSpan,\n IRunTraceTurn,\n IUsageBySourceReport,\n} from '@robota-sdk/agent-interface-analytics';\n\n/** The `type` of the per-turn usage history entry (agent-framework `createUsageSummaryEntry`). */\nconst USAGE_SUMMARY_ENTRY_TYPE = 'usage-summary';\n\n/** The `type` of the per-operation span history entry (agent-framework `createSpanEntry`). */\nconst SPAN_ENTRY_TYPE = 'span';\n\n/** SSOT-derived projection the usage reducer reads. */\nexport type TUsageAnalysisInput = Pick<IInteractiveSessionRecord, 'id' | 'history'>;\n\n/** The main thread is the implicit source when a usage snapshot carries none. */\nconst MAIN_THREAD_SOURCE: IUsageSource = { scope: 'main', label: 'main thread' };\n\nfunction sourceKey(source: IUsageSource): string {\n return `${source.scope}:${source.id ?? ''}`;\n}\n\nfunction sourceLabel(source: IUsageSource): string {\n return source.label ?? (source.id ? `${source.scope} ${source.id}` : source.scope);\n}\n\nfunction isUsageSummaryEntry(entry: IHistoryEntry): entry is IHistoryEntry<IUsageSnapshot> {\n return (\n entry.type === USAGE_SUMMARY_ENTRY_TYPE && typeof entry.data === 'object' && entry.data !== null\n );\n}\n\nfunction isSpanEntry(entry: IHistoryEntry): entry is IHistoryEntry<ISpanEntry> {\n if (entry.type !== SPAN_ENTRY_TYPE || typeof entry.data !== 'object' || entry.data === null) {\n return false;\n }\n const data = entry.data as Partial<ISpanEntry>;\n return (\n typeof data.spanId === 'string' &&\n typeof data.op === 'string' &&\n typeof data.durationMs === 'number'\n );\n}\n\nfunction roundPercentage(part: number, whole: number): number {\n if (whole <= 0) return 0;\n return Math.round((part / whole) * 1000) / 10;\n}\n\n/**\n * Aggregate a session's `usage-summary` entries into a per-source token+cost breakdown AND assemble\n * the per-operation span timeline (SELFHOST-004).\n *\n * Token/cost: usage snapshots with no `source` are attributed to the main thread; sources are grouped\n * by `<scope>:<id>` and sorted by total tokens descending with the top consumer surfaced. Cost sums\n * each turn's known `IUsageSnapshot.costUsd` (unpriced turns contribute 0). Historical `exact`\n * snapshots were local price-table estimates, so they cannot certify billed cost.\n *\n * Timeline: history is a chronological timeline where each turn ends with its `usage-summary` entry,\n * so the `span` entries that appear before a given usage-summary are the sub-turn spans of THAT turn.\n * The reducer walks in order, buffering spans, and flushes the buffer as the turn's spans at each\n * usage-summary boundary — grouping sub-turn spans under their owning turn. Spans after the last\n * usage-summary (an in-progress turn) form a trailing timeline entry. Pure: no I/O, stays within the\n * declared `agent-core`, `agent-interface-analytics` and `agent-interface-session` dependencies\n * (NO `agent-plugin` read — enforcement lives elsewhere).\n */\nexport function summarizeUsageBySource(input: TUsageAnalysisInput): IUsageBySourceReport {\n const totalsByKey = new Map<string, IUsageSourceTotals>();\n let sessionPrompt = 0;\n let sessionCompletion = 0;\n let sessionTotal = 0;\n let sessionCost = 0;\n let sessionCostExact = true;\n\n const timeline: IRunTraceTurn[] = [];\n let pendingSpans: IRunTraceSpan[] = [];\n\n const flushTurn = (source: IUsageSource): void => {\n if (pendingSpans.length === 0) return;\n timeline.push({\n turnIndex: timeline.length,\n source,\n label: sourceLabel(source),\n spans: pendingSpans,\n totalDurationMs: pendingSpans.reduce((sum, span) => sum + span.durationMs, 0),\n });\n pendingSpans = [];\n };\n\n for (const entry of input.history ?? []) {\n if (isSpanEntry(entry)) {\n const span = entry.data as ISpanEntry;\n pendingSpans.push({ spanId: span.spanId, op: span.op, durationMs: span.durationMs });\n continue;\n }\n if (!isUsageSummaryEntry(entry)) continue;\n\n const snapshot = entry.data as IUsageSnapshot;\n const source = snapshot.source ?? MAIN_THREAD_SOURCE;\n const key = sourceKey(source);\n const prompt = snapshot.promptTokens ?? 0;\n const completion = snapshot.completionTokens ?? 0;\n const total = snapshot.totalTokens;\n // Historical `exact` labels were local price-table estimates, not billed values. Preserve the\n // amount while keeping the aggregate's exactness claim false.\n const turnCost = snapshot.costUsd ?? 0;\n const turnPriced = false;\n\n sessionPrompt += prompt;\n sessionCompletion += completion;\n sessionTotal += total;\n sessionCost += turnCost;\n if (!turnPriced) sessionCostExact = false;\n\n const existing = totalsByKey.get(key);\n if (existing) {\n existing.promptTokens += prompt;\n existing.completionTokens += completion;\n existing.totalTokens += total;\n existing.turns += 1;\n existing.costUsd += turnCost;\n if (!turnPriced) existing.costExact = false;\n } else {\n totalsByKey.set(key, {\n key,\n source,\n label: sourceLabel(source),\n promptTokens: prompt,\n completionTokens: completion,\n totalTokens: total,\n turns: 1,\n percentage: 0,\n costUsd: turnCost,\n costExact: turnPriced,\n });\n }\n\n // The usage-summary marks the turn boundary: the buffered spans belong to THIS turn.\n flushTurn(source);\n }\n\n // Spans of an in-progress turn (no usage summary yet) form a trailing timeline entry.\n flushTurn(MAIN_THREAD_SOURCE);\n\n const bySource = [...totalsByKey.values()]\n .map((totals) => ({ ...totals, percentage: roundPercentage(totals.totalTokens, sessionTotal) }))\n .sort((left, right) => right.totalTokens - left.totalTokens);\n\n return {\n sessionId: input.id,\n totalTokens: sessionTotal,\n promptTokens: sessionPrompt,\n completionTokens: sessionCompletion,\n costUsd: sessionCost,\n costExact: sessionCostExact,\n bySource,\n ...(bySource[0] ? { topConsumer: bySource[0] } : {}),\n timeline,\n };\n}\n","import type { IHistoryEntry, IUniversalObjectValue, TUniversalValue } from '@robota-sdk/agent-core';\nimport type {\n IPersonalUsageActivity,\n IUsageModelShare,\n IUsageObservation,\n IUsageSnapshot,\n IUsageSource,\n} from '@robota-sdk/agent-interface-analytics';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\nexport interface INormalizedObservation {\n readonly id: string;\n readonly sessionId: string;\n readonly at: Date;\n readonly legacy: boolean;\n readonly observation: IUsageObservation;\n}\n\nexport interface INormalizedActivity {\n readonly id: string;\n readonly sessionId: string;\n readonly key: string;\n readonly label: string;\n readonly kind: IPersonalUsageActivity['kind'];\n readonly at: Date;\n}\n\nfunction isRecord(value: TUniversalValue): value is IUniversalObjectValue {\n return (\n typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)\n );\n}\n\nfunction optionalString(value: TUniversalValue): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction isMetric(value: TUniversalValue): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0;\n}\n\nfunction isCostStatus(value: TUniversalValue): value is IUsageSnapshot['costStatus'] {\n return value === 'unknown' || value === 'estimated' || value === 'exact';\n}\n\nfunction hasValidOptionalMetrics(value: IUniversalObjectValue): boolean {\n return (\n (value['promptTokens'] === undefined || isMetric(value['promptTokens'])) &&\n (value['completionTokens'] === undefined || isMetric(value['completionTokens']))\n );\n}\n\nfunction hasValidCost(value: IUniversalObjectValue): boolean {\n const status = value['costStatus'];\n if (!isCostStatus(status)) return false;\n return status === 'unknown' ? value['costUsd'] === undefined : isMetric(value['costUsd']);\n}\n\ntype TRequiredUsageMetrics = {\n readonly totalTokens: number;\n readonly contextUsedTokens: number;\n readonly contextMaxTokens: number;\n readonly contextUsedPercentage: number;\n};\n\nfunction hasRequiredMetrics(\n value: IUniversalObjectValue,\n): value is IUniversalObjectValue & TRequiredUsageMetrics {\n return (\n isMetric(value['totalTokens']) &&\n isMetric(value['contextUsedTokens']) &&\n isMetric(value['contextMaxTokens']) &&\n isMetric(value['contextUsedPercentage'])\n );\n}\n\nfunction isUsageSurface(\n value: TUniversalValue,\n): value is NonNullable<IUsageObservation['surface']> {\n return (\n value === 'cli' ||\n value === 'desktop-app' ||\n value === 'browser' ||\n value === 'remote' ||\n value === 'unknown'\n );\n}\n\nfunction usageSource(value: TUniversalValue): IUsageSource | undefined {\n if (!isRecord(value)) return undefined;\n const scope = value['scope'];\n if (\n scope !== 'main' &&\n scope !== 'subagent' &&\n scope !== 'background' &&\n scope !== 'tool' &&\n scope !== 'command' &&\n scope !== 'skill'\n ) {\n return undefined;\n }\n const id = optionalString(value['id']);\n const label = optionalString(value['label']);\n return { scope, ...(id ? { id } : {}), ...(label ? { label } : {}) };\n}\n\nfunction usageSnapshot(value: TUniversalValue): IUsageSnapshot | undefined {\n if (!isRecord(value)) return undefined;\n if (value['kind'] !== 'exact' && value['kind'] !== 'estimated') return undefined;\n if (value['scope'] !== 'turn' || !hasValidOptionalMetrics(value) || !hasValidCost(value)) {\n return undefined;\n }\n if (!hasRequiredMetrics(value) || !isCostStatus(value['costStatus'])) {\n return undefined;\n }\n const source = usageSource(value['source']);\n return {\n kind: value['kind'],\n scope: 'turn',\n totalTokens: value['totalTokens'],\n contextUsedTokens: value['contextUsedTokens'],\n contextMaxTokens: value['contextMaxTokens'],\n contextUsedPercentage: value['contextUsedPercentage'],\n // Historical `exact` meant a local price-table calculation, not an invoice.\n costStatus: value['costStatus'] === 'exact' ? 'estimated' : value['costStatus'],\n ...(isMetric(value['promptTokens']) ? { promptTokens: value['promptTokens'] } : {}),\n ...(isMetric(value['completionTokens']) ? { completionTokens: value['completionTokens'] } : {}),\n ...(isMetric(value['costUsd']) ? { costUsd: value['costUsd'] } : {}),\n ...(source ? { source } : {}),\n };\n}\n\nfunction modelShare(value: TUniversalValue): IUsageModelShare | undefined {\n if (!isRecord(value)) return undefined;\n const providerId = optionalString(value['providerId']);\n const modelId = optionalString(value['modelId']);\n const { promptTokens, completionTokens, totalTokens, costStatus, costUsd } = value;\n if (\n !providerId ||\n !modelId ||\n !isMetric(promptTokens) ||\n !isMetric(completionTokens) ||\n !isMetric(totalTokens) ||\n (costStatus !== 'unknown' && costStatus !== 'estimated') ||\n (costStatus === 'estimated' ? !isMetric(costUsd) : costUsd !== undefined)\n ) {\n return undefined;\n }\n return {\n providerId,\n modelId,\n promptTokens,\n completionTokens,\n totalTokens,\n costStatus,\n ...(costStatus === 'estimated' && isMetric(costUsd) ? { costUsd } : {}),\n };\n}\n\n/** Every share, or none: a partly readable split would misstate what each model spent. */\nfunction modelShares(value: TUniversalValue): IUsageModelShare[] | undefined {\n if (!Array.isArray(value) || value.length === 0) return undefined;\n const shares = value.map(modelShare);\n return shares.every((share) => share !== undefined) ? (shares as IUsageModelShare[]) : undefined;\n}\n\nfunction decodeObservation(value: TUniversalValue): IUsageObservation | undefined {\n if (!isRecord(value)) return undefined;\n const usageObservationId = optionalString(value['usageObservationId']);\n const turnId = optionalString(value['turnId']);\n const outcome = value['outcome'];\n if (\n !usageObservationId ||\n !turnId ||\n (outcome !== 'success' && outcome !== 'failure' && outcome !== 'interrupted')\n ) {\n return undefined;\n }\n const surface = value['surface'];\n const source = usageSource(value['source']);\n const usage = usageSnapshot(value['usage']);\n const shares = modelShares(value['modelShares']);\n return {\n usageObservationId,\n turnId,\n outcome,\n ...(optionalString(value['modelId']) ? { modelId: optionalString(value['modelId']) } : {}),\n ...(optionalString(value['providerId'])\n ? { providerId: optionalString(value['providerId']) }\n : {}),\n ...(shares ? { modelShares: shares } : {}),\n ...(isUsageSurface(surface) ? { surface } : {}),\n ...(source ? { source } : {}),\n ...(usage ? { usage } : {}),\n };\n}\n\nfunction entryDate(entry: IHistoryEntry): Date | undefined {\n const at = new Date(entry.timestamp);\n return Number.isNaN(at.getTime()) ? undefined : at;\n}\n\nexport function normalizeRecord(record: IInteractiveSessionRecord): INormalizedObservation[] {\n const observations: INormalizedObservation[] = [];\n const history = record.history ?? [];\n const shadowedLegacyIds = new Set<string>();\n const unmatchedLegacy = history.flatMap((entry, index) => {\n if (entry.type !== 'usage-summary') return [];\n const usage = usageSnapshot(entry.data as TUniversalValue);\n return usage ? [{ id: entry.id, index, usage }] : [];\n });\n for (const [canonicalIndex, entry] of history.entries()) {\n if (entry.type !== 'usage-observation') continue;\n const canonical = decodeObservation(entry.data as TUniversalValue);\n if (!canonical?.usage) continue;\n const canonicalKey = JSON.stringify(canonical.usage);\n const candidates = unmatchedLegacy\n .map((candidate, index) => ({ candidate, index }))\n .filter(({ candidate }) => JSON.stringify(candidate.usage) === canonicalKey)\n .sort(\n (left, right) =>\n Math.abs(left.candidate.index - canonicalIndex) -\n Math.abs(right.candidate.index - canonicalIndex),\n );\n const match = candidates[0];\n if (!match) continue;\n shadowedLegacyIds.add(match.candidate.id);\n unmatchedLegacy.splice(match.index, 1);\n }\n for (const entry of history) {\n const at = entryDate(entry);\n if (!at) continue;\n const observation =\n entry.type === 'usage-observation'\n ? decodeObservation(entry.data as TUniversalValue)\n : undefined;\n if (observation) {\n observations.push({\n id: observation.usageObservationId,\n sessionId: record.id,\n at,\n legacy: false,\n observation,\n });\n continue;\n }\n if (entry.type !== 'usage-summary' || shadowedLegacyIds.has(entry.id)) continue;\n const usage = usageSnapshot(entry.data as TUniversalValue);\n if (!usage) continue;\n const id = `legacy:${record.id}:${entry.id}`;\n observations.push({\n id,\n sessionId: record.id,\n at,\n legacy: true,\n observation: {\n usageObservationId: id,\n turnId: `legacy:${entry.id}`,\n outcome: 'success',\n usage,\n ...(usage.source ? { source: usage.source } : {}),\n surface: 'unknown',\n },\n });\n }\n return observations;\n}\n\nexport function normalizeActivities(record: IInteractiveSessionRecord): INormalizedActivity[] {\n const activities: INormalizedActivity[] = [];\n for (const entry of record.history ?? []) {\n const data = entry.data as TUniversalValue;\n if (entry.type !== 'tool-start' || !isRecord(data)) continue;\n const toolName = optionalString(data['toolName']);\n const at = entryDate(entry);\n if (!toolName || !at) continue;\n activities.push({\n id: `tool:${record.id}:${entry.id}`,\n sessionId: record.id,\n key: `tool:${toolName}`,\n label: toolName,\n kind: 'tool',\n at,\n });\n }\n // Activation history mirrors this canonical source and is intentionally not counted again.\n for (const event of record.skillActivationEvents ?? []) {\n if (event.status !== 'started') continue;\n const at = new Date(event.timestamp);\n if (Number.isNaN(at.getTime())) continue;\n activities.push({\n id: `activation:${record.id}:${event.timestamp}:${event.source}:${event.qualifiedName ?? event.skillName}:${event.invocation}`,\n sessionId: record.id,\n key: event.qualifiedName ?? `${event.source}:${event.skillName}`,\n label: event.qualifiedName ?? event.skillName,\n kind: event.source,\n at,\n });\n }\n return activities;\n}\n","import {\n normalizeActivities,\n normalizeRecord,\n type INormalizedActivity,\n type INormalizedObservation,\n} from './personal-usage-decode.js';\n\nimport type {\n IPersonalUsageDimension,\n IPersonalUsageActivity,\n IPersonalUsageReport,\n IPersonalUsageRequest,\n IPersonalUsageTotals,\n IUsageSource,\n} from '@robota-sdk/agent-interface-analytics';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\nconst SHORT_PERIOD_DAYS = 7;\nconst LONG_PERIOD_DAYS = 30;\nconst ISO_DATE_LENGTH = 'YYYY-MM-DD'.length;\n\nexport interface IPersonalUsageSnapshot {\n readonly request: IPersonalUsageRequest;\n readonly now: Date;\n readonly records: readonly IInteractiveSessionRecord[];\n readonly corruptSessionIds?: readonly string[];\n readonly unsupportedSessionIds?: readonly string[];\n}\n\ninterface IMutableTotals {\n sessions: Set<string>;\n observations: number;\n turns: number;\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n costUsd: number;\n hasUnknownCost: boolean;\n hasEstimatedCost: boolean;\n}\n\nfunction createMutableTotals(): IMutableTotals {\n return {\n sessions: new Set<string>(),\n observations: 0,\n turns: 0,\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n costUsd: 0,\n hasUnknownCost: false,\n hasEstimatedCost: false,\n };\n}\n\nfunction dateKey(date: Date, timezone: string): string {\n const parts = new Intl.DateTimeFormat('en-CA', {\n timeZone: timezone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(date);\n const part = (type: Intl.DateTimeFormatPartTypes): string =>\n parts.find((candidate) => candidate.type === type)?.value ?? '';\n return `${part('year')}-${part('month')}-${part('day')}`;\n}\n\nfunction calendarKeys(now: Date, timezone: string, count: number): string[] {\n // First resolve the current LOCAL date (also validates the IANA timezone), then perform date-only\n // arithmetic in UTC. Subtracting 24-hour instants can skip a local calendar date during the first\n // hour after a spring-forward transition; date-only arithmetic cannot.\n const [year, month, day] = dateKey(now, timezone).split('-').map(Number);\n const localDate = new Date(Date.UTC(year!, month! - 1, day!));\n return Array.from({ length: count }, (_, index) => {\n const value = new Date(localDate);\n value.setUTCDate(value.getUTCDate() - (count - index - 1));\n return value.toISOString().slice(0, ISO_DATE_LENGTH);\n });\n}\n\nfunction activityDimensions(items: readonly INormalizedActivity[]): IPersonalUsageActivity[] {\n const counts = new Map<string, { item: INormalizedActivity; count: number }>();\n for (const item of items) {\n const current = counts.get(item.key);\n counts.set(item.key, { item, count: (current?.count ?? 0) + 1 });\n }\n return [...counts.values()]\n .map(({ item, count }) => ({\n key: item.key,\n label: item.label,\n kind: item.kind,\n count,\n }))\n .sort((left, right) => right.count - left.count || left.key.localeCompare(right.key));\n}\n\nfunction addObservation(target: IMutableTotals, item: INormalizedObservation): void {\n const usage = item.observation.usage;\n target.sessions.add(item.sessionId);\n target.observations += 1;\n // A consulted model's usage (the advisor, a tool source) belongs to the turn that consulted it;\n // it adds tokens and cost, not a turn.\n if (!item.legacy && item.observation.source?.scope !== 'tool') target.turns += 1;\n target.promptTokens += usage?.promptTokens ?? 0;\n target.completionTokens += usage?.completionTokens ?? 0;\n target.totalTokens += usage?.totalTokens ?? 0;\n target.costUsd += usage?.costUsd ?? 0;\n if (!usage || usage.costStatus === 'unknown') target.hasUnknownCost = true;\n if (usage?.costStatus === 'estimated') target.hasEstimatedCost = true;\n}\n\n/**\n * Group a turn by model or provider. A turn that ran on more than one model is split by its shares,\n * so each model is charged only what it spent; the turn still counts once in every group it touched.\n */\nfunction modelDimensions(\n items: readonly INormalizedObservation[],\n keyOf: (share: { providerId?: string; modelId?: string }) => string,\n): IPersonalUsageDimension[] {\n const parts: INormalizedObservation[] = [];\n for (const item of items) {\n const shares = item.observation.modelShares;\n if (shares === undefined) {\n parts.push(item);\n continue;\n }\n const byKey = new Map<string, INormalizedObservation>();\n for (const share of shares) {\n const key = keyOf(share);\n const current = byKey.get(key)?.observation.usage;\n const usage = item.observation.usage;\n const costStatus =\n share.costStatus === 'unknown' || current?.costStatus === 'unknown'\n ? ('unknown' as const)\n : ('estimated' as const);\n byKey.set(key, {\n ...item,\n observation: {\n ...item.observation,\n providerId: share.providerId,\n modelId: share.modelId,\n usage: {\n kind: usage?.kind ?? 'estimated',\n scope: 'turn',\n contextUsedTokens: usage?.contextUsedTokens ?? 0,\n contextMaxTokens: usage?.contextMaxTokens ?? 0,\n contextUsedPercentage: usage?.contextUsedPercentage ?? 0,\n promptTokens: (current?.promptTokens ?? 0) + share.promptTokens,\n completionTokens: (current?.completionTokens ?? 0) + share.completionTokens,\n totalTokens: (current?.totalTokens ?? 0) + share.totalTokens,\n costStatus,\n ...(costStatus === 'estimated' && {\n costUsd: (current?.costUsd ?? 0) + (share.costUsd ?? 0),\n }),\n },\n },\n });\n }\n parts.push(...byKey.values());\n }\n return dimensions(parts, (item) => keyOf(item.observation));\n}\n\nfunction freezeTotals(totals: IMutableTotals): IPersonalUsageTotals {\n return {\n sessions: totals.sessions.size,\n turns: totals.turns,\n promptTokens: totals.promptTokens,\n completionTokens: totals.completionTokens,\n totalTokens: totals.totalTokens,\n costUsd: totals.costUsd,\n costStatus:\n totals.observations === 0 || totals.hasUnknownCost\n ? 'unknown'\n : totals.hasEstimatedCost\n ? 'estimated'\n : 'exact',\n };\n}\n\nfunction dimensions(\n items: readonly INormalizedObservation[],\n keyOf: (item: INormalizedObservation) => string,\n): IPersonalUsageDimension[] {\n const groups = new Map<string, IMutableTotals>();\n for (const item of items) {\n const key = keyOf(item);\n const totals = groups.get(key) ?? createMutableTotals();\n addObservation(totals, item);\n groups.set(key, totals);\n }\n return [...groups.entries()]\n .map(([key, totals]) => {\n const frozen = freezeTotals(totals);\n return { key, label: key, frozen, sessionIds: [...totals.sessions].sort() };\n })\n .map(({ key, label, frozen, sessionIds }) => ({\n key,\n label,\n turns: frozen.turns,\n promptTokens: frozen.promptTokens,\n completionTokens: frozen.completionTokens,\n totalTokens: frozen.totalTokens,\n costUsd: frozen.costUsd,\n costStatus: frozen.costStatus,\n sessionIds,\n }))\n .sort(\n (left, right) => right.totalTokens - left.totalTokens || left.key.localeCompare(right.key),\n );\n}\n\nfunction sourceKey(source: IUsageSource | undefined): string {\n if (!source) return 'unknown';\n return source.id ? `${source.scope}:${source.id}` : source.scope;\n}\n\ninterface ISelectedUsage {\n readonly observations: INormalizedObservation[];\n readonly activities: INormalizedActivity[];\n readonly duplicateObservations: number;\n readonly legacyObservations: number;\n}\n\nfunction isIncluded(at: Date, input: IPersonalUsageSnapshot, dates: ReadonlySet<string>): boolean {\n return at.getTime() <= input.now.getTime() && dates.has(dateKey(at, input.request.timezone));\n}\n\nfunction selectUsage(input: IPersonalUsageSnapshot, dates: ReadonlySet<string>): ISelectedUsage {\n const observations: INormalizedObservation[] = [];\n const activities: INormalizedActivity[] = [];\n const seenObservations = new Set<string>();\n const seenActivities = new Set<string>();\n let duplicateObservations = 0;\n let legacyObservations = 0;\n for (const record of input.records) {\n for (const item of normalizeRecord(record)) {\n if (!isIncluded(item.at, input, dates)) continue;\n if (seenObservations.has(item.id)) {\n duplicateObservations += 1;\n continue;\n }\n seenObservations.add(item.id);\n observations.push(item);\n if (item.legacy) legacyObservations += 1;\n }\n for (const activity of normalizeActivities(record)) {\n if (!isIncluded(activity.at, input, dates) || seenActivities.has(activity.id)) continue;\n seenActivities.add(activity.id);\n activities.push(activity);\n }\n }\n return { observations, activities, duplicateObservations, legacyObservations };\n}\n\nfunction accumulateUsage(\n input: IPersonalUsageSnapshot,\n keys: readonly string[],\n selection: ISelectedUsage,\n): { totals: IMutableTotals; byDay: Map<string, IMutableTotals> } {\n const totals = createMutableTotals();\n const byDay = new Map(keys.map((key) => [key, createMutableTotals()]));\n for (const item of selection.observations) {\n addObservation(totals, item);\n addObservation(byDay.get(dateKey(item.at, input.request.timezone))!, item);\n }\n for (const activity of selection.activities) {\n totals.sessions.add(activity.sessionId);\n byDay.get(dateKey(activity.at, input.request.timezone))?.sessions.add(activity.sessionId);\n }\n return { totals, byDay };\n}\n\nexport function summarizePersonalUsage(input: IPersonalUsageSnapshot): IPersonalUsageReport {\n const count = input.request.period === '30d' ? LONG_PERIOD_DAYS : SHORT_PERIOD_DAYS;\n const keys = calendarKeys(input.now, input.request.timezone, count);\n const selection = selectUsage(input, new Set(keys));\n const { totals, byDay } = accumulateUsage(input, keys, selection);\n const selected = selection.observations;\n\n return {\n schemaVersion: 1,\n generatedAt: input.now.toISOString(),\n period: input.request.period,\n timezone: input.request.timezone,\n interval: { startDate: keys[0]!, endDate: keys.at(-1)! },\n totals: freezeTotals(totals),\n daily: keys.map((date, index) => ({\n date,\n partial: index === keys.length - 1,\n totals: freezeTotals(byDay.get(date)!),\n sessionIds: [...byDay.get(date)!.sessions].sort(),\n })),\n byModel: modelDimensions(selected, (share) => share.modelId ?? 'unknown'),\n byProvider: modelDimensions(selected, (share) => share.providerId ?? 'unknown'),\n bySurface: dimensions(selected, (item) => item.observation.surface ?? 'unknown'),\n bySource: dimensions(selected, (item) => sourceKey(item.observation.source)),\n byActivity: activityDimensions(selection.activities),\n sessionIds: [...totals.sessions].sort(),\n coverage: {\n validSessions: input.records.length,\n corruptSessions: input.corruptSessionIds?.length ?? 0,\n unsupportedSessions: input.unsupportedSessionIds?.length ?? 0,\n duplicateObservations: selection.duplicateObservations,\n legacyObservations: selection.legacyObservations,\n unknownModelObservations: selected.filter(\n (item) => !item.observation.modelId && !item.observation.modelShares,\n ).length,\n unknownProviderObservations: selected.filter(\n (item) => !item.observation.providerId && !item.observation.modelShares,\n ).length,\n unknownSurfaceObservations: selected.filter(\n (item) => !item.observation.surface || item.observation.surface === 'unknown',\n ).length,\n corruptSessionIds: [...(input.corruptSessionIds ?? [])].sort(),\n unsupportedSessionIds: [...(input.unsupportedSessionIds ?? [])].sort(),\n },\n };\n}\n","import { calculateModelCost, MODEL_PRICES } from '@robota-sdk/agent-core';\n\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\nconst MAX_UINT64 = (1n << 64n) - 1n;\nconst TRACE_ID = /^(?!0{32}$)[0-9a-f]{32}$/;\nconst SPAN_ID = /^(?!0{16}$)[0-9a-f]{16}$/;\nconst ROOT_FIELDS = [\n 'promptExecutionStartedAt',\n 'promptExecutionEndedAt',\n 'promptExecutionOutcome',\n 'promptExecutionTraceId',\n 'promptExecutionSpanId',\n] as const;\n\ninterface ICandidate {\n readonly record: IInteractiveSessionRecord;\n readonly data: unknown;\n readonly observationKey?: string;\n readonly traceId?: string;\n}\n\ninterface IOtlpSpan {\n readonly traceId: string;\n readonly spanId: string;\n readonly parentSpanId?: string;\n readonly name: 'robota.prompt_execution' | 'robota.provider_call' | 'robota.tool_body';\n readonly kind: 1;\n readonly startTimeUnixNano: string;\n readonly endTimeUnixNano: string;\n readonly attributes: readonly {\n readonly key: string;\n readonly value: { readonly stringValue: string } | { readonly intValue: string } | { readonly doubleValue: number };\n }[];\n readonly status: { readonly code: number };\n}\n\nexport interface IOtlpPromptRootTraces {\n readonly resourceSpans: readonly {\n readonly resource: {\n readonly attributes: readonly {\n readonly key: string;\n readonly value: { readonly stringValue: string };\n }[];\n };\n readonly scopeSpans: readonly {\n readonly scope: { readonly name: string };\n readonly spans: readonly IOtlpSpan[];\n }[];\n }[];\n}\n\nexport interface IPromptRootTraceCoverage {\n readonly exported: number;\n readonly missing: number;\n readonly invalid: number;\n readonly duplicate: number;\n readonly providerChildren: {\n readonly exported: number;\n readonly invalid: number;\n readonly orphaned: number;\n readonly duplicate: number;\n };\n readonly providerUsage: {\n readonly legacy: number;\n readonly invalid: number;\n readonly partial: number;\n readonly absent: number;\n readonly nonInvoked: number;\n };\n readonly toolChildren: {\n readonly exported: number;\n readonly invalid: number;\n readonly orphaned: number;\n readonly duplicate: number;\n };\n}\n\nexport interface IAcceptedProviderCallMetrics {\n readonly invoked: number;\n readonly completeUsage: number;\n readonly inputTokens: number;\n readonly outputTokens: number;\n readonly estimatedCostUsd: number;\n readonly unknownCost: number;\n readonly exactPriceMatches: number;\n readonly familyPriceMatches: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction count<K>(map: Map<K, number>, key: K): void {\n map.set(key, (map.get(key) ?? 0) + 1);\n}\n\nfunction otlpUnixNano(value: unknown): bigint | undefined {\n if (typeof value !== 'string') return undefined;\n const date = new Date(value);\n if (Number.isNaN(date.getTime()) || date.toISOString() !== value) return undefined;\n const nanos = BigInt(date.getTime()) * 1_000_000n;\n return nanos >= 0n && nanos <= MAX_UINT64 ? nanos : undefined;\n}\n\nfunction rootSpan(data: Record<string, unknown>): IOtlpSpan | undefined {\n const started = otlpUnixNano(data['promptExecutionStartedAt']);\n const ended = otlpUnixNano(data['promptExecutionEndedAt']);\n const outcome = data['promptExecutionOutcome'];\n const traceId = data['promptExecutionTraceId'];\n const spanId = data['promptExecutionSpanId'];\n if (\n started === undefined ||\n ended === undefined ||\n started > ended ||\n (outcome !== 'success' && outcome !== 'failure' && outcome !== 'interrupted') ||\n typeof traceId !== 'string' ||\n !TRACE_ID.test(traceId) ||\n typeof spanId !== 'string' ||\n !SPAN_ID.test(spanId) ||\n typeof data['usageObservationId'] !== 'string' ||\n data['usageObservationId'].length === 0 ||\n data['turnId'] !== data['usageObservationId'] ||\n (data['outcome'] !== 'success' &&\n data['outcome'] !== 'failure' &&\n data['outcome'] !== 'interrupted')\n ) {\n return undefined;\n }\n return {\n traceId,\n spanId,\n name: 'robota.prompt_execution',\n kind: 1,\n startTimeUnixNano: String(started),\n endTimeUnixNano: String(ended),\n attributes: [{ key: 'robota.prompt.outcome', value: { stringValue: outcome } }],\n status: { code: outcome === 'success' ? 1 : outcome === 'failure' ? 2 : 0 },\n };\n}\n\nfunction childSpan(data: unknown, kind: 'provider' | 'tool'): IOtlpSpan | undefined {\n if (!isRecord(data)) return undefined;\n const traceId = data['traceId'];\n const parentSpanId = data['parentSpanId'];\n const spanId = data['spanId'];\n const started = otlpUnixNano(data['startedAt']);\n const ended = otlpUnixNano(data['endedAt']);\n const outcome = data['outcome'];\n if (\n typeof traceId !== 'string' ||\n !TRACE_ID.test(traceId) ||\n typeof parentSpanId !== 'string' ||\n !SPAN_ID.test(parentSpanId) ||\n typeof spanId !== 'string' ||\n !SPAN_ID.test(spanId) ||\n started === undefined ||\n ended === undefined ||\n started > ended ||\n (outcome !== 'success' && outcome !== 'failure' && outcome !== 'interrupted') ||\n (kind === 'provider' && (!Number.isSafeInteger(data['round']) || (data['round'] as number) < 1))\n ) {\n return undefined;\n }\n return {\n traceId,\n parentSpanId,\n spanId,\n name: kind === 'provider' ? 'robota.provider_call' : 'robota.tool_body',\n kind: 1,\n startTimeUnixNano: String(started),\n endTimeUnixNano: String(ended),\n attributes: [\n {\n key: kind === 'provider' ? 'robota.provider.outcome' : 'robota.tool.outcome',\n value: { stringValue: outcome },\n },\n ],\n status: { code: outcome === 'success' ? 1 : outcome === 'failure' ? 2 : 0 },\n };\n}\n\nfunction childIdentity(data: unknown): string | undefined {\n if (!isRecord(data)) return undefined;\n const traceId = data['traceId'];\n const spanId = data['spanId'];\n return typeof traceId === 'string' &&\n TRACE_ID.test(traceId) &&\n typeof spanId === 'string' &&\n SPAN_ID.test(spanId)\n ? `${traceId}:${spanId}`\n : undefined;\n}\n\nfunction validToken(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction enrichProviderSpan(\n span: IOtlpSpan,\n data: unknown,\n usageCoverage: { legacy: number; invalid: number; partial: number; absent: number },\n metrics: { -readonly [K in keyof IAcceptedProviderCallMetrics]: number },\n): IOtlpSpan {\n if (!isRecord(data) || data['disposition'] === undefined) {\n usageCoverage.legacy += 1;\n return span;\n }\n if (data['disposition'] !== 'invoked') {\n usageCoverage.invalid += 1;\n return span;\n }\n metrics.invoked += 1;\n const provenance = data['usageProvenance'];\n if (provenance === 'partial') {\n usageCoverage.partial += 1;\n metrics.unknownCost += 1;\n return span;\n }\n if (provenance === 'absent' || provenance === undefined) {\n usageCoverage.absent += 1;\n metrics.unknownCost += 1;\n return span;\n }\n const input = data['promptTokens'];\n const output = data['completionTokens'];\n const total = data['totalTokens'];\n if (\n provenance !== 'complete' || !validToken(input) || !validToken(output) ||\n !Number.isSafeInteger(input + output) || total !== input + output\n ) {\n usageCoverage.invalid += 1;\n metrics.unknownCost += 1;\n return span;\n }\n metrics.completeUsage += 1;\n metrics.inputTokens += input;\n metrics.outputTokens += output;\n const attrs: IOtlpSpan['attributes'][number][] = [\n ...span.attributes,\n { key: 'robota.provider.usage.input_tokens', value: { intValue: String(input) } },\n { key: 'robota.provider.usage.output_tokens', value: { intValue: String(output) } },\n ];\n const model = data['modelId'];\n const estimated = typeof model === 'string' && model.length > 0 && model.length <= 128 &&\n [...model].every((char) => char.charCodeAt(0) >= 32)\n ? calculateModelCost(model, input, output) : undefined;\n if (estimated === undefined || !Number.isFinite(estimated) || estimated < 0) {\n metrics.unknownCost += 1;\n } else {\n metrics.estimatedCostUsd += estimated;\n if (typeof model === 'string' && Object.hasOwn(MODEL_PRICES, model)) metrics.exactPriceMatches += 1;\n else metrics.familyPriceMatches += 1;\n attrs.push({ key: 'robota.provider.cost.usd.estimated', value: { doubleValue: estimated } });\n }\n return { ...span, attributes: attrs };\n}\n\n/** Project only proven canonical prompt roots; never infer spans from legacy usage or tool events. */\nexport function createOtlpPromptRootTraces(\n records: readonly IInteractiveSessionRecord[],\n version: string,\n): { readonly payload: IOtlpPromptRootTraces; readonly coverage: IPromptRootTraceCoverage; readonly callMetrics: IAcceptedProviderCallMetrics } {\n const candidates: ICandidate[] = [];\n const observationCounts = new Map<string, number>();\n const traceCounts = new Map<string, number>();\n for (const record of records) {\n for (const entry of record.history ?? []) {\n if (entry.type !== 'usage-observation') continue;\n const data: unknown = entry.data;\n const observationId = isRecord(data) ? data['usageObservationId'] : undefined;\n const traceId = isRecord(data) ? data['promptExecutionTraceId'] : undefined;\n const observationKey =\n typeof observationId === 'string' && observationId.length > 0\n ? JSON.stringify([record.id, observationId])\n : undefined;\n const validTraceId =\n typeof traceId === 'string' && TRACE_ID.test(traceId) ? traceId : undefined;\n if (observationKey) count(observationCounts, observationKey);\n if (validTraceId) count(traceCounts, validTraceId);\n candidates.push({ record, data, observationKey, traceId: validTraceId });\n }\n }\n\n const spans: IOtlpSpan[] = [];\n const rootsByRecord = new Map<IInteractiveSessionRecord, Map<string, IOtlpSpan>>();\n let missing = 0;\n let invalid = 0;\n let duplicate = 0;\n for (const candidate of candidates) {\n if (\n (candidate.observationKey && (observationCounts.get(candidate.observationKey) ?? 0) > 1) ||\n (candidate.traceId && (traceCounts.get(candidate.traceId) ?? 0) > 1)\n ) {\n duplicate += 1;\n continue;\n }\n const data = candidate.data;\n if (!isRecord(data)) {\n invalid += 1;\n continue;\n }\n if (ROOT_FIELDS.every((field) => data[field] === undefined)) {\n missing += 1;\n continue;\n }\n const span = rootSpan(data);\n if (!span) {\n invalid += 1;\n continue;\n }\n spans.push(span);\n const recordRoots = rootsByRecord.get(candidate.record) ?? new Map<string, IOtlpSpan>();\n recordRoots.set(span.traceId, span);\n rootsByRecord.set(candidate.record, recordRoots);\n }\n\n const children: {\n readonly record: IInteractiveSessionRecord;\n readonly span: IOtlpSpan | undefined;\n readonly identity: string | undefined;\n readonly kind: 'provider' | 'tool';\n readonly data: unknown;\n }[] = [];\n const childCounts = new Map<string, number>();\n for (const record of records) {\n for (const entry of record.history ?? []) {\n const kind =\n entry.type === 'provider-call-trace'\n ? 'provider'\n : entry.type === 'tool-body-trace'\n ? 'tool'\n : undefined;\n if (!kind) continue;\n const identity = childIdentity(entry.data);\n const span = childSpan(entry.data, kind);\n if (identity) count(childCounts, identity);\n children.push({ record, span, identity, kind, data: entry.data });\n }\n }\n const coverageByKind = {\n provider: { exported: 0, invalid: 0, orphaned: 0, duplicate: 0 },\n tool: { exported: 0, invalid: 0, orphaned: 0, duplicate: 0 },\n };\n const providerUsage = { legacy: 0, invalid: 0, partial: 0, absent: 0, nonInvoked: 0 };\n const callMetrics = {\n invoked: 0, completeUsage: 0, inputTokens: 0, outputTokens: 0,\n estimatedCostUsd: 0, unknownCost: 0, exactPriceMatches: 0, familyPriceMatches: 0,\n };\n for (const child of children) {\n const { span } = child;\n const coverage = coverageByKind[child.kind];\n if (child.identity && (childCounts.get(child.identity) ?? 0) > 1) {\n coverage.duplicate += 1;\n continue;\n }\n if (!span) {\n coverage.invalid += 1;\n continue;\n }\n const root = rootsByRecord.get(child.record)?.get(span.traceId);\n if (!root || span.parentSpanId !== root.spanId || span.spanId === root.spanId) {\n coverage.orphaned += 1;\n continue;\n }\n if (\n BigInt(span.startTimeUnixNano) < BigInt(root.startTimeUnixNano) ||\n BigInt(span.endTimeUnixNano) > BigInt(root.endTimeUnixNano)\n ) {\n coverage.invalid += 1;\n continue;\n }\n if (child.kind === 'provider' && isRecord(child.data) &&\n (child.data['disposition'] === 'cache-hit' || child.data['disposition'] === 'preflight-refused')) {\n providerUsage.nonInvoked += 1;\n if (child.data['promptTokens'] !== undefined || child.data['completionTokens'] !== undefined ||\n child.data['totalTokens'] !== undefined || child.data['usageProvenance'] === 'complete') {\n providerUsage.invalid += 1;\n }\n continue;\n }\n spans.push(child.kind === 'provider'\n ? enrichProviderSpan(span, child.data, providerUsage, callMetrics)\n : span);\n coverage.exported += 1;\n }\n\n return {\n payload: {\n resourceSpans: spans.length\n ? [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'robota' } },\n { key: 'service.version', value: { stringValue: version } },\n ],\n },\n scopeSpans: [{ scope: { name: '@robota-sdk/agent-session-analytics' }, spans }],\n },\n ]\n : [],\n },\n coverage: {\n exported: spans.length - coverageByKind.provider.exported - coverageByKind.tool.exported,\n missing,\n invalid,\n duplicate,\n providerChildren: coverageByKind.provider,\n providerUsage,\n toolChildren: coverageByKind.tool,\n },\n callMetrics,\n };\n}\n","import { createOtlpPromptRootTraces } from './otlp-prompt-root-traces.js';\nimport { normalizeRecord } from './personal-usage-decode.js';\n\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\n/** A non-additive, content-free snapshot. Consumers must read the latest Gauge, not sum exports. */\nexport function createOtlpUsageSnapshot(\n records: readonly IInteractiveSessionRecord[],\n at: Date,\n version: string,\n): { resourceMetrics: unknown[] } {\n const seen = new Set<string>();\n let turns = 0;\n let totalTokens = 0;\n let inputTokens = 0;\n let outputTokens = 0;\n let unknownTokenSplitObservations = 0;\n let knownCost = 0;\n let unknownCostObservations = 0;\n let estimatedCostObservations = 0;\n\n for (const record of records) {\n for (const item of normalizeRecord(record)) {\n if (seen.has(item.id)) continue;\n seen.add(item.id);\n if (!item.legacy) turns += 1;\n const usage = item.observation.usage;\n if (!usage) {\n unknownCostObservations += 1;\n unknownTokenSplitObservations += 1;\n continue;\n }\n totalTokens += usage.totalTokens;\n inputTokens += usage.promptTokens ?? 0;\n outputTokens += usage.completionTokens ?? 0;\n if (usage.promptTokens === undefined || usage.completionTokens === undefined) {\n unknownTokenSplitObservations += 1;\n }\n if (usage.costStatus === 'unknown' || usage.costUsd === undefined) {\n unknownCostObservations += 1;\n } else {\n knownCost += usage.costUsd;\n if (usage.costStatus === 'estimated') estimatedCostObservations += 1;\n }\n }\n }\n // The trace projection is the single accepted-call selector: parent/time/duplicate checks must\n // agree between traces and metrics. These are separate non-additive Gauges, never turn totals.\n const acceptedCalls = createOtlpPromptRootTraces(records, version);\n const calls = acceptedCalls.callMetrics;\n\n const timeUnixNano = String(BigInt(at.getTime()) * 1_000_000n);\n const gauge = (name: string, unit: string, value: number) => ({\n name,\n unit,\n gauge: { dataPoints: [{ timeUnixNano, asDouble: value }] },\n });\n\n return {\n resourceMetrics: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'robota' } },\n { key: 'service.version', value: { stringValue: version } },\n ],\n },\n scopeMetrics: [\n {\n scope: { name: '@robota-sdk/agent-session-analytics' },\n metrics: [\n gauge('robota.session.count', '{session}', records.length),\n gauge('robota.turn.count', '{turn}', turns),\n gauge('robota.token.total', '{token}', totalTokens),\n gauge('robota.token.input.known', '{token}', inputTokens),\n gauge('robota.token.output.known', '{token}', outputTokens),\n gauge(\n 'robota.token.split_unknown_observations',\n '{observation}',\n unknownTokenSplitObservations,\n ),\n gauge('robota.cost.usd.known', 'USD', knownCost),\n gauge('robota.cost.unknown_observations', '{observation}', unknownCostObservations),\n gauge(\n 'robota.cost.estimated_observations',\n '{observation}',\n estimatedCostObservations,\n ),\n gauge('robota.provider_call.count', '{call}', calls.invoked),\n gauge('robota.provider_call.usage.complete_count', '{call}', calls.completeUsage),\n gauge('robota.provider_call.token.input.known', '{token}', calls.inputTokens),\n gauge('robota.provider_call.token.output.known', '{token}', calls.outputTokens),\n gauge('robota.provider_call.cost.usd.estimated', 'USD', calls.estimatedCostUsd),\n gauge('robota.provider_call.cost.unknown_count', '{call}', calls.unknownCost),\n gauge('robota.provider_call.usage.partial_count', '{call}', acceptedCalls.coverage.providerUsage.partial),\n gauge('robota.provider_call.usage.invalid_count', '{call}', acceptedCalls.coverage.providerUsage.invalid),\n gauge('robota.provider_call.usage.legacy_count', '{call}', acceptedCalls.coverage.providerUsage.legacy),\n gauge('robota.provider_call.price.exact_id_count', '{call}', calls.exactPriceMatches),\n gauge('robota.provider_call.price.family_fallback_count', '{call}', calls.familyPriceMatches),\n ],\n },\n ],\n },\n ],\n };\n}\n","import { createOtlpPromptRootTraces } from './otlp-prompt-root-traces.js';\n\nimport type { IPromptRootTraceCoverage } from './otlp-prompt-root-traces.js';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\ntype TEventName =\n | 'robota.prompt_execution.completed'\n | 'robota.provider_call.completed'\n | 'robota.tool_body.completed';\n\ninterface IOtlpLogRecord {\n readonly timeUnixNano: string;\n readonly observedTimeUnixNano: string;\n readonly severityNumber: 9 | 13 | 17;\n readonly severityText: 'INFO' | 'WARN' | 'ERROR';\n readonly eventName: TEventName;\n readonly traceId: string;\n readonly spanId: string;\n readonly attributes: readonly {\n readonly key: string;\n readonly value: { readonly stringValue: string };\n }[];\n}\n\nexport interface IOtlpPromptEvents {\n readonly resourceLogs: readonly {\n readonly resource: {\n readonly attributes: readonly {\n readonly key: string;\n readonly value: { readonly stringValue: string };\n }[];\n };\n readonly scopeLogs: readonly {\n readonly scope: { readonly name: string };\n readonly logRecords: readonly IOtlpLogRecord[];\n }[];\n }[];\n}\n\nconst MAX_UINT64 = (1n << 64n) - 1n;\n\nfunction observedUnixNano(observedAt: Date): string {\n const millis = observedAt.getTime();\n if (!Number.isSafeInteger(millis) || millis < 0) {\n throw new RangeError('OTLP log observation time must be a valid post-epoch date.');\n }\n const nanos = BigInt(millis) * 1_000_000n;\n if (nanos > MAX_UINT64) throw new RangeError('OTLP log observation time is out of range.');\n return String(nanos);\n}\n\n/** Explicit local OTLP log projection of canonical prompt completion events. */\nexport function createOtlpPromptEvents(\n records: readonly IInteractiveSessionRecord[],\n version: string,\n observedAt: Date,\n): {\n readonly exported: number;\n readonly coverage: IPromptRootTraceCoverage;\n readonly payload: IOtlpPromptEvents;\n} {\n const traces = createOtlpPromptRootTraces(records, version);\n const spans = traces.payload.resourceSpans[0]?.scopeSpans[0]?.spans ?? [];\n if (spans.length === 0) {\n return { exported: 0, coverage: traces.coverage, payload: { resourceLogs: [] } };\n }\n const observedTimeUnixNano = observedUnixNano(observedAt);\n const logRecords: IOtlpLogRecord[] = spans.map((span) => {\n const outcomeAttribute = span.attributes.find((attribute) =>\n attribute.key === 'robota.prompt.outcome' ||\n attribute.key === 'robota.provider.outcome' ||\n attribute.key === 'robota.tool.outcome');\n const eventName: TEventName =\n span.name === 'robota.prompt_execution'\n ? 'robota.prompt_execution.completed'\n : span.name === 'robota.provider_call'\n ? 'robota.provider_call.completed'\n : 'robota.tool_body.completed';\n const severityNumber = span.status.code === 1 ? 9 : span.status.code === 2 ? 17 : 13;\n const severityText = severityNumber === 9 ? 'INFO' : severityNumber === 17 ? 'ERROR' : 'WARN';\n return {\n timeUnixNano: span.endTimeUnixNano,\n observedTimeUnixNano,\n severityNumber,\n severityText,\n eventName,\n traceId: span.traceId,\n spanId: span.spanId,\n attributes: outcomeAttribute && 'stringValue' in outcomeAttribute.value\n ? [{ key: outcomeAttribute.key, value: { stringValue: outcomeAttribute.value.stringValue } }]\n : [],\n };\n });\n return {\n exported: logRecords.length,\n coverage: traces.coverage,\n payload: {\n resourceLogs: [\n {\n resource: traces.payload.resourceSpans[0]!.resource,\n scopeLogs: [\n {\n scope: { name: '@robota-sdk/agent-session-analytics' },\n logRecords,\n },\n ],\n },\n ],\n },\n };\n}\n","import type {\n IPersonalUsageDimension,\n IPersonalUsageReport,\n} from '@robota-sdk/agent-interface-analytics';\n\nconst COST_DECIMAL_PLACES = 4;\n\nfunction dimensionLines(title: string, values: readonly IPersonalUsageDimension[]): string[] {\n return [\n '',\n title,\n ...(values.length === 0\n ? ['(none)']\n : values.map(\n (value) =>\n `${value.label} ${value.turns} turns ${value.totalTokens} tokens cost ${value.costStatus === 'unknown' ? 'unknown' : `$${value.costUsd.toFixed(COST_DECIMAL_PLACES)}`}`,\n )),\n ];\n}\n\n/** Render the provider-neutral personal-usage report for the terminal surface. */\nexport function formatPersonalUsageReport(report: IPersonalUsageReport): string {\n const cost =\n report.totals.costStatus === 'unknown'\n ? 'unknown'\n : `$${report.totals.costUsd.toFixed(COST_DECIMAL_PLACES)} (${report.totals.costStatus})`;\n return [\n `Personal usage — ${report.period} (${report.timezone})`,\n `${report.interval.startDate} → ${report.interval.endDate}`,\n `Sessions ${report.totals.sessions} · Turns ${report.totals.turns} · Tokens ${report.totals.totalTokens} (${report.totals.promptTokens} input / ${report.totals.completionTokens} output) · Cost ${cost}`,\n '',\n 'Daily usage',\n ...report.daily.map(\n (day) =>\n `${day.date}${day.partial ? ' (partial)' : ''} ${day.totals.turns} turns ${day.totals.totalTokens} tokens`,\n ),\n ...dimensionLines('By model', report.byModel),\n ...dimensionLines('By provider', report.byProvider),\n ...dimensionLines('By surface', report.bySurface),\n ...dimensionLines('By source', report.bySource),\n '',\n 'Activity',\n ...(report.byActivity.length === 0\n ? ['(none)']\n : report.byActivity.map(\n (activity) => `${activity.kind}:${activity.label} ${activity.count} calls`,\n )),\n '',\n `Coverage: legacy ${report.coverage.legacyObservations}, corrupt ${report.coverage.corruptSessions}, unsupported ${report.coverage.unsupportedSessions}, duplicates ${report.coverage.duplicateObservations}`,\n 'Local estimates from recorded sessions; costs are not an invoice.',\n ].join('\\n');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["sourceKey","isRecord","MAX_UINT64"],"sources":["../../src/analyze.ts","../../src/report.ts","../../src/usage.ts","../../src/personal-usage-decode.ts","../../src/personal-usage.ts","../../src/otlp-prompt-root-traces.ts","../../src/otlp-usage-snapshot.ts","../../src/otlp-prompt-events.ts","../../src/personal-usage-format.ts"],"sourcesContent":["/**\n * Session-log timing-interval computation and aggregation.\n *\n * Operates on canonical history entries (`IHistoryEntry`); persisted timestamps arrive as ISO\n * strings at runtime (JSON has no Date), so all timestamp math goes through `new Date(...)`.\n */\n\nimport type {\n IAggregateReport,\n TSessionAnalysisInput,\n ISessionTimingReport,\n ITimingInterval,\n ITimingStats,\n TIntervalKind,\n} from './types.js';\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\n\nconst SLOW_THRESHOLD_MS = 10_000;\n\n/** Gap in milliseconds between two timestamps (ISO string or Date). */\nexport function gapMs(from: string | Date, to: string | Date): number {\n return new Date(to).getTime() - new Date(from).getTime();\n}\n\nfunction toIso(value: string | Date): string {\n return new Date(value).toISOString();\n}\n\nfunction makeInterval(\n kind: TIntervalKind,\n fromType: string,\n toType: string,\n fromTimestamp: string | Date,\n toTimestamp: string | Date,\n turnIndex: number,\n): ITimingInterval {\n return {\n kind,\n fromType,\n toType,\n fromTimestamp: toIso(fromTimestamp),\n toTimestamp: toIso(toTimestamp),\n durationMs: gapMs(fromTimestamp, toTimestamp),\n turnIndex,\n };\n}\n\n/**\n * Compute timing intervals from a history entry array.\n *\n * Strategy: scan the timeline turn by turn. A \"turn\" starts with a user message. After user, we\n * expect zero or more tool-start → tool-end cycles, optionally a tool-summary, then an assistant\n * message.\n */\nexport function computeTimingIntervals(history: readonly IHistoryEntry[]): ITimingInterval[] {\n const intervals: ITimingInterval[] = [];\n let turnIndex = 0;\n\n let i = 0;\n while (i < history.length) {\n const entry = history[i];\n if (!entry) {\n i++;\n continue;\n }\n\n if (entry.category === 'chat' && entry.type === 'user') {\n turnIndex++;\n const userTs = entry.timestamp;\n let foundFirstTool = false;\n let lastToolEndTs: IHistoryEntry['timestamp'] | null = null;\n let j = i + 1;\n\n while (j < history.length) {\n const next = history[j];\n if (!next) {\n j++;\n continue;\n }\n\n if (next.category === 'chat' && next.type === 'user') {\n break;\n }\n\n if (next.category === 'event' && next.type === 'tool-start') {\n if (!foundFirstTool) {\n intervals.push(\n makeInterval(\n 'user_to_first_tool',\n 'user',\n 'tool-start',\n userTs,\n next.timestamp,\n turnIndex,\n ),\n );\n foundFirstTool = true;\n } else if (lastToolEndTs !== null) {\n intervals.push(\n makeInterval(\n 'llm_between_tools',\n 'tool-end',\n 'tool-start',\n lastToolEndTs,\n next.timestamp,\n turnIndex,\n ),\n );\n }\n lastToolEndTs = null;\n j++;\n continue;\n }\n\n if (next.category === 'event' && next.type === 'tool-end') {\n const prev = history[j - 1];\n if (prev && prev.category === 'event' && prev.type === 'tool-start') {\n intervals.push(\n makeInterval(\n 'tool_exec',\n 'tool-start',\n 'tool-end',\n prev.timestamp,\n next.timestamp,\n turnIndex,\n ),\n );\n }\n lastToolEndTs = next.timestamp;\n j++;\n continue;\n }\n\n if (next.category === 'event' && next.type === 'tool-summary') {\n if (lastToolEndTs !== null) {\n intervals.push(\n makeInterval(\n 'llm_final_response',\n 'tool-end',\n 'tool-summary',\n lastToolEndTs,\n next.timestamp,\n turnIndex,\n ),\n );\n }\n j++;\n continue;\n }\n\n if (next.category === 'chat' && next.type === 'assistant') {\n if (!foundFirstTool) {\n intervals.push(\n makeInterval(\n 'user_to_assistant',\n 'user',\n 'assistant',\n userTs,\n next.timestamp,\n turnIndex,\n ),\n );\n } else if (lastToolEndTs !== null) {\n intervals.push(\n makeInterval(\n 'llm_final_response',\n 'tool-end',\n 'assistant',\n lastToolEndTs,\n next.timestamp,\n turnIndex,\n ),\n );\n }\n j++;\n break;\n }\n\n j++;\n }\n\n i = j;\n } else {\n i++;\n }\n }\n\n return intervals;\n}\n\nfunction computeStats(intervals: ITimingInterval[]): ITimingStats {\n const llm = intervals.filter(\n (iv) =>\n iv.kind === 'user_to_first_tool' ||\n iv.kind === 'user_to_assistant' ||\n iv.kind === 'llm_between_tools' ||\n iv.kind === 'llm_final_response',\n );\n const tool = intervals.filter((iv) => iv.kind === 'tool_exec');\n const direct = intervals.filter((iv) => iv.kind === 'user_to_assistant');\n\n const avg = (arr: ITimingInterval[]): number =>\n arr.length ? Math.round(arr.reduce((s, iv) => s + iv.durationMs, 0) / arr.length) : 0;\n const max = (arr: ITimingInterval[]): number =>\n arr.length ? Math.max(...arr.map((iv) => iv.durationMs)) : 0;\n const median = (arr: ITimingInterval[]): number => {\n if (!arr.length) return 0;\n const sorted = [...arr].sort((a, b) => a.durationMs - b.durationMs);\n const mid = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 0\n ? Math.round(((sorted[mid - 1]?.durationMs ?? 0) + (sorted[mid]?.durationMs ?? 0)) / 2)\n : (sorted[mid]?.durationMs ?? 0);\n };\n\n return {\n llmWaitMs: {\n avg: avg(llm),\n max: max(llm),\n total: llm.reduce((s, iv) => s + iv.durationMs, 0),\n count: llm.length,\n },\n toolExecMs: { avg: avg(tool), max: max(tool), median: median(tool), count: tool.length },\n userToAssistantMs: { avg: avg(direct), max: max(direct), count: direct.length },\n };\n}\n\n/** Compute the timing report for a single session record. */\nexport function analyzeSession(record: TSessionAnalysisInput): ISessionTimingReport {\n const history = record.history ?? [];\n const intervals = computeTimingIntervals(history);\n const slowIntervals = intervals.filter((iv) => iv.durationMs >= SLOW_THRESHOLD_MS);\n\n return {\n sessionId: record.id,\n cwd: record.cwd,\n createdAt: record.createdAt,\n totalIntervals: intervals.length,\n intervals,\n slowIntervals,\n stats: computeStats(intervals),\n };\n}\n\n/** Aggregate multiple single-session reports into a fleet-level summary. */\nexport function aggregateReports(reports: readonly ISessionTimingReport[]): IAggregateReport {\n const llmIntervals = reports.flatMap((r) =>\n r.intervals.filter(\n (iv) =>\n iv.kind === 'user_to_first_tool' ||\n iv.kind === 'user_to_assistant' ||\n iv.kind === 'llm_between_tools' ||\n iv.kind === 'llm_final_response',\n ),\n );\n const toolIntervals = reports.flatMap((r) => r.intervals.filter((iv) => iv.kind === 'tool_exec'));\n\n const avgLlmResponseMs = llmIntervals.length\n ? Math.round(llmIntervals.reduce((s, iv) => s + iv.durationMs, 0) / llmIntervals.length)\n : 0;\n const avgToolExecMs = toolIntervals.length\n ? Math.round(toolIntervals.reduce((s, iv) => s + iv.durationMs, 0) / toolIntervals.length)\n : 0;\n\n let maxSingleDelayMs = 0;\n let maxSingleDelaySession = '';\n let maxSingleDelayTurn = 0;\n let maxSingleDelayKind = '';\n\n for (const r of reports) {\n for (const iv of r.intervals) {\n if (iv.durationMs > maxSingleDelayMs) {\n maxSingleDelayMs = iv.durationMs;\n maxSingleDelaySession = r.sessionId;\n maxSingleDelayTurn = iv.turnIndex;\n maxSingleDelayKind = iv.kind;\n }\n }\n }\n\n const dates = reports.map((r) => r.createdAt).sort();\n\n return {\n sessionCount: reports.length,\n fromDate: dates[0] ?? '',\n toDate: dates[dates.length - 1] ?? '',\n avgLlmResponseMs,\n avgToolExecMs,\n maxSingleDelayMs,\n maxSingleDelaySession,\n maxSingleDelayTurn,\n maxSingleDelayKind,\n };\n}\n","/**\n * Text report formatters for session timing analysis. Pure — every function returns a string;\n * callers (e.g. a CLI command) own writing to stdout.\n */\n\nimport type { IAggregateReport, ISessionTimingReport, ITimingInterval } from './types.js';\nimport type { IUsageBySourceReport } from './usage.js';\n\nfunction fmtTokens(n: number): string {\n if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n return `${n}`;\n}\n\n/** SELFHOST-004: `~` marks an estimate or incomplete aggregate, never an invoice amount. */\nfunction fmtCost(usd: number, exact: boolean): string {\n return `${exact ? '' : '~'}$${usd.toFixed(4)}`;\n}\n\n/**\n * ANALYTICS-001 + SELFHOST-004: render a per-source token+cost breakdown and the per-operation span\n * timeline — which part of the session burned the most tokens/cost, and where the wall-clock went.\n * Pure; the caller owns writing it (the headless CLI path for the TUI/GUI trace/cost view).\n */\nexport function formatUsageReport(report: IUsageBySourceReport): string {\n const lines: string[] = [];\n lines.push(`Token usage — session ${report.sessionId}`);\n lines.push(\n ` total ${fmtTokens(report.totalTokens)} (prompt ${fmtTokens(report.promptTokens)} · completion ${fmtTokens(report.completionTokens)})`,\n );\n lines.push(` cost ${fmtCost(report.costUsd, report.costExact)}`);\n if (report.bySource.length === 0) {\n lines.push(' (no usage recorded)');\n } else {\n lines.push(' by source (most tokens first):');\n for (const s of report.bySource) {\n lines.push(\n ` ${s.label.padEnd(24)} ${String(s.percentage).padStart(5)}% ${fmtTokens(s.totalTokens).padStart(7)} ${fmtCost(s.costUsd, s.costExact).padStart(10)} (${s.turns} turn${s.turns === 1 ? '' : 's'})`,\n );\n }\n if (report.topConsumer) {\n lines.push(` top consumer: ${report.topConsumer.label} (${report.topConsumer.percentage}%)`);\n }\n }\n\n // SELFHOST-004: the span timeline — sub-turn operations grouped under their owning turn.\n if (report.timeline.length > 0) {\n lines.push(' trace (per turn, sub-turn spans):');\n for (const turn of report.timeline) {\n lines.push(\n ` turn ${turn.turnIndex} — ${turn.label} · ${fmtMs(turn.totalDurationMs)} across ${turn.spans.length} op${turn.spans.length === 1 ? '' : 's'}`,\n );\n for (const span of turn.spans) {\n lines.push(` ${span.op.padEnd(22)} ${fmtMs(span.durationMs).padStart(8)}`);\n }\n }\n }\n return lines.join('\\n');\n}\n\nfunction fmtMs(ms: number): string {\n if (ms >= 1000) return `${(ms / 1000).toFixed(1)}s`;\n return `${ms}ms`;\n}\n\nfunction fmtDate(iso: string): string {\n return new Date(iso).toLocaleString('sv-SE', { timeZoneName: 'short' }).slice(0, 16);\n}\n\nfunction intervalLabel(iv: ITimingInterval): string {\n const map: Record<string, string> = {\n user_to_first_tool: 'user → tool-start',\n user_to_assistant: 'user → assistant',\n tool_exec: 'tool-start → tool-end',\n llm_between_tools: 'tool-end → tool-start',\n llm_final_response: 'tool-end → assistant/summary',\n };\n return map[iv.kind] ?? iv.kind;\n}\n\nfunction verdictLine(report: ISessionTimingReport): string {\n const { stats } = report;\n const totalMs = stats.llmWaitMs.total + stats.toolExecMs.avg * stats.toolExecMs.count;\n if (totalMs === 0) return 'Verdict: No timing data available.';\n const llmPct = Math.round((stats.llmWaitMs.total / totalMs) * 100);\n const toolPct = 100 - llmPct;\n return `Verdict: LLM API wait ${llmPct}% | Code processing ${toolPct}%`;\n}\n\nexport function formatSingleSession(report: ISessionTimingReport): string {\n const lines: string[] = [];\n const { stats } = report;\n\n lines.push(`Session: ${report.sessionId}`);\n lines.push(`Created: ${fmtDate(report.createdAt)} | cwd: ${report.cwd}`);\n lines.push('');\n lines.push('Timing Summary:');\n\n if (stats.llmWaitMs.count > 0) {\n lines.push(\n ` LLM API wait ${fmtMs(stats.llmWaitMs.avg).padStart(8)} avg | ${fmtMs(stats.llmWaitMs.max).padStart(8)} max`,\n );\n }\n if (stats.toolExecMs.count > 0) {\n lines.push(\n ` Tool execution ${fmtMs(stats.toolExecMs.avg).padStart(8)} avg | ${fmtMs(stats.toolExecMs.median).padStart(8)} median`,\n );\n }\n if (stats.userToAssistantMs.count > 0) {\n lines.push(` user → assistant ${fmtMs(stats.userToAssistantMs.avg).padStart(8)} avg`);\n }\n if (stats.llmWaitMs.count === 0 && stats.toolExecMs.count === 0) {\n lines.push(' (no intervals computed — session may have no history)');\n }\n\n if (report.slowIntervals.length > 0) {\n lines.push('');\n lines.push('Slow intervals (>10s):');\n for (const iv of report.slowIntervals) {\n lines.push(\n ` turn ${iv.turnIndex} ${intervalLabel(iv).padEnd(32)} ${fmtMs(iv.durationMs).padStart(8)}`,\n );\n }\n }\n\n lines.push('');\n lines.push(verdictLine(report));\n\n return lines.join('\\n');\n}\n\nexport function formatAggregateReport(aggregate: IAggregateReport): string {\n const lines: string[] = [];\n lines.push(\n `Analyzed ${aggregate.sessionCount} sessions (${fmtDate(aggregate.fromDate)} ~ ${fmtDate(aggregate.toDate)})`,\n );\n lines.push(` Avg LLM response: ${fmtMs(aggregate.avgLlmResponseMs)}`);\n lines.push(` Avg tool exec: ${fmtMs(aggregate.avgToolExecMs)}`);\n if (aggregate.maxSingleDelayMs > 0) {\n lines.push(\n ` Max single delay: ${fmtMs(aggregate.maxSingleDelayMs)} (${aggregate.maxSingleDelaySession}, turn ${aggregate.maxSingleDelayTurn}, ${aggregate.maxSingleDelayKind})`,\n );\n }\n return lines.join('\\n');\n}\n","/**\n * ANALYTICS-001: source-attributed token-usage analysis over a session record.\n *\n * The main session history records one `usage-summary` history entry per turn (an `IUsageSnapshot`,\n * created in agent-framework). This reducer aggregates those snapshots into a per-source breakdown —\n * answering \"which part of the session burned the most tokens?\" — so it can be reported to the user\n * and asserted by the testing framework (budget gates). Pure: no I/O, no process concerns.\n */\n\nimport type { IHistoryEntry } from '@robota-sdk/agent-core';\nimport type {\n ISpanEntry,\n IUsageSnapshot,\n IUsageSource,\n IUsageSourceTotals,\n IRunTraceSpan,\n IRunTraceTurn,\n IUsageBySourceReport,\n} from '@robota-sdk/agent-interface-analytics';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\n// SELFHOST-004: the trace/cost read-model is a boundary contract owned by `agent-interface-analytics`\n// (it crosses the sidecar boundary via a TServerMessage carrier). Re-exported here for consumers that\n// import it alongside the reducer that produces it.\nexport type {\n IUsageSourceTotals,\n IRunTraceSpan,\n IRunTraceTurn,\n IUsageBySourceReport,\n} from '@robota-sdk/agent-interface-analytics';\n\n/** The `type` of the per-turn usage history entry (agent-framework `createUsageSummaryEntry`). */\nconst USAGE_SUMMARY_ENTRY_TYPE = 'usage-summary';\n\n/** The `type` of the per-operation span history entry (agent-framework `createSpanEntry`). */\nconst SPAN_ENTRY_TYPE = 'span';\n\n/** SSOT-derived projection the usage reducer reads. */\nexport type TUsageAnalysisInput = Pick<IInteractiveSessionRecord, 'id' | 'history'>;\n\n/** The main thread is the implicit source when a usage snapshot carries none. */\nconst MAIN_THREAD_SOURCE: IUsageSource = { scope: 'main', label: 'main thread' };\n\nfunction sourceKey(source: IUsageSource): string {\n return `${source.scope}:${source.id ?? ''}`;\n}\n\nfunction sourceLabel(source: IUsageSource): string {\n return source.label ?? (source.id ? `${source.scope} ${source.id}` : source.scope);\n}\n\nfunction isUsageSummaryEntry(entry: IHistoryEntry): entry is IHistoryEntry<IUsageSnapshot> {\n return (\n entry.type === USAGE_SUMMARY_ENTRY_TYPE && typeof entry.data === 'object' && entry.data !== null\n );\n}\n\nfunction isSpanEntry(entry: IHistoryEntry): entry is IHistoryEntry<ISpanEntry> {\n if (entry.type !== SPAN_ENTRY_TYPE || typeof entry.data !== 'object' || entry.data === null) {\n return false;\n }\n const data = entry.data as Partial<ISpanEntry>;\n return (\n typeof data.spanId === 'string' &&\n typeof data.op === 'string' &&\n typeof data.durationMs === 'number'\n );\n}\n\nfunction roundPercentage(part: number, whole: number): number {\n if (whole <= 0) return 0;\n return Math.round((part / whole) * 1000) / 10;\n}\n\n/**\n * Aggregate a session's `usage-summary` entries into a per-source token+cost breakdown AND assemble\n * the per-operation span timeline (SELFHOST-004).\n *\n * Token/cost: usage snapshots with no `source` are attributed to the main thread; sources are grouped\n * by `<scope>:<id>` and sorted by total tokens descending with the top consumer surfaced. Cost sums\n * each turn's known `IUsageSnapshot.costUsd` (unpriced turns contribute 0). Historical `exact`\n * snapshots were local price-table estimates, so they cannot certify billed cost.\n *\n * Timeline: history is a chronological timeline where each turn ends with its `usage-summary` entry,\n * so the `span` entries that appear before a given usage-summary are the sub-turn spans of THAT turn.\n * The reducer walks in order, buffering spans, and flushes the buffer as the turn's spans at each\n * usage-summary boundary — grouping sub-turn spans under their owning turn. Spans after the last\n * usage-summary (an in-progress turn) form a trailing timeline entry. Pure: no I/O, stays within the\n * declared `agent-core`, `agent-interface-analytics` and `agent-interface-session` dependencies\n * (NO `agent-plugin` read — enforcement lives elsewhere).\n */\nexport function summarizeUsageBySource(input: TUsageAnalysisInput): IUsageBySourceReport {\n const totalsByKey = new Map<string, IUsageSourceTotals>();\n let sessionPrompt = 0;\n let sessionCompletion = 0;\n let sessionTotal = 0;\n let sessionCost = 0;\n let sessionCostExact = true;\n\n const timeline: IRunTraceTurn[] = [];\n let pendingSpans: IRunTraceSpan[] = [];\n\n const flushTurn = (source: IUsageSource): void => {\n if (pendingSpans.length === 0) return;\n timeline.push({\n turnIndex: timeline.length,\n source,\n label: sourceLabel(source),\n spans: pendingSpans,\n totalDurationMs: pendingSpans.reduce((sum, span) => sum + span.durationMs, 0),\n });\n pendingSpans = [];\n };\n\n for (const entry of input.history ?? []) {\n if (isSpanEntry(entry)) {\n const span = entry.data as ISpanEntry;\n pendingSpans.push({ spanId: span.spanId, op: span.op, durationMs: span.durationMs });\n continue;\n }\n if (!isUsageSummaryEntry(entry)) continue;\n\n const snapshot = entry.data as IUsageSnapshot;\n const source = snapshot.source ?? MAIN_THREAD_SOURCE;\n const key = sourceKey(source);\n const prompt = snapshot.promptTokens ?? 0;\n const completion = snapshot.completionTokens ?? 0;\n const total = snapshot.totalTokens;\n // Historical `exact` labels were local price-table estimates, not billed values. Preserve the\n // amount while keeping the aggregate's exactness claim false.\n const turnCost = snapshot.costUsd ?? 0;\n const turnPriced = false;\n\n sessionPrompt += prompt;\n sessionCompletion += completion;\n sessionTotal += total;\n sessionCost += turnCost;\n if (!turnPriced) sessionCostExact = false;\n\n const existing = totalsByKey.get(key);\n if (existing) {\n existing.promptTokens += prompt;\n existing.completionTokens += completion;\n existing.totalTokens += total;\n existing.turns += 1;\n existing.costUsd += turnCost;\n if (!turnPriced) existing.costExact = false;\n } else {\n totalsByKey.set(key, {\n key,\n source,\n label: sourceLabel(source),\n promptTokens: prompt,\n completionTokens: completion,\n totalTokens: total,\n turns: 1,\n percentage: 0,\n costUsd: turnCost,\n costExact: turnPriced,\n });\n }\n\n // The usage-summary marks the turn boundary: the buffered spans belong to THIS turn.\n flushTurn(source);\n }\n\n // Spans of an in-progress turn (no usage summary yet) form a trailing timeline entry.\n flushTurn(MAIN_THREAD_SOURCE);\n\n const bySource = [...totalsByKey.values()]\n .map((totals) => ({ ...totals, percentage: roundPercentage(totals.totalTokens, sessionTotal) }))\n .sort((left, right) => right.totalTokens - left.totalTokens);\n\n return {\n sessionId: input.id,\n totalTokens: sessionTotal,\n promptTokens: sessionPrompt,\n completionTokens: sessionCompletion,\n costUsd: sessionCost,\n costExact: sessionCostExact,\n bySource,\n ...(bySource[0] ? { topConsumer: bySource[0] } : {}),\n timeline,\n };\n}\n","import type { IHistoryEntry, IUniversalObjectValue, TUniversalValue } from '@robota-sdk/agent-core';\nimport type {\n IPersonalUsageActivity,\n IUsageModelShare,\n IUsageObservation,\n IUsageSnapshot,\n IUsageSource,\n} from '@robota-sdk/agent-interface-analytics';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\nexport interface INormalizedObservation {\n readonly id: string;\n readonly sessionId: string;\n readonly at: Date;\n readonly legacy: boolean;\n readonly observation: IUsageObservation;\n}\n\nexport interface INormalizedActivity {\n readonly id: string;\n readonly sessionId: string;\n readonly key: string;\n readonly label: string;\n readonly kind: IPersonalUsageActivity['kind'];\n readonly at: Date;\n}\n\nfunction isRecord(value: TUniversalValue): value is IUniversalObjectValue {\n return (\n typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof Date)\n );\n}\n\nfunction optionalString(value: TUniversalValue): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction isMetric(value: TUniversalValue): value is number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0;\n}\n\nfunction isCostStatus(value: TUniversalValue): value is IUsageSnapshot['costStatus'] {\n return value === 'unknown' || value === 'estimated' || value === 'exact';\n}\n\nfunction hasValidOptionalMetrics(value: IUniversalObjectValue): boolean {\n return (\n (value['promptTokens'] === undefined || isMetric(value['promptTokens'])) &&\n (value['completionTokens'] === undefined || isMetric(value['completionTokens']))\n );\n}\n\nfunction hasValidCost(value: IUniversalObjectValue): boolean {\n const status = value['costStatus'];\n if (!isCostStatus(status)) return false;\n return status === 'unknown' ? value['costUsd'] === undefined : isMetric(value['costUsd']);\n}\n\ntype TRequiredUsageMetrics = {\n readonly totalTokens: number;\n readonly contextUsedTokens: number;\n readonly contextMaxTokens: number;\n readonly contextUsedPercentage: number;\n};\n\nfunction hasRequiredMetrics(\n value: IUniversalObjectValue,\n): value is IUniversalObjectValue & TRequiredUsageMetrics {\n return (\n isMetric(value['totalTokens']) &&\n isMetric(value['contextUsedTokens']) &&\n isMetric(value['contextMaxTokens']) &&\n isMetric(value['contextUsedPercentage'])\n );\n}\n\nfunction isUsageSurface(\n value: TUniversalValue,\n): value is NonNullable<IUsageObservation['surface']> {\n return (\n value === 'cli' ||\n value === 'desktop-app' ||\n value === 'browser' ||\n value === 'remote' ||\n value === 'attach' ||\n value === 'unknown'\n );\n}\n\nfunction usageSource(value: TUniversalValue): IUsageSource | undefined {\n if (!isRecord(value)) return undefined;\n const scope = value['scope'];\n if (\n scope !== 'main' &&\n scope !== 'subagent' &&\n scope !== 'background' &&\n scope !== 'tool' &&\n scope !== 'command' &&\n scope !== 'skill'\n ) {\n return undefined;\n }\n const id = optionalString(value['id']);\n const label = optionalString(value['label']);\n return { scope, ...(id ? { id } : {}), ...(label ? { label } : {}) };\n}\n\nfunction usageSnapshot(value: TUniversalValue): IUsageSnapshot | undefined {\n if (!isRecord(value)) return undefined;\n if (value['kind'] !== 'exact' && value['kind'] !== 'estimated') return undefined;\n if (value['scope'] !== 'turn' || !hasValidOptionalMetrics(value) || !hasValidCost(value)) {\n return undefined;\n }\n if (!hasRequiredMetrics(value) || !isCostStatus(value['costStatus'])) {\n return undefined;\n }\n const source = usageSource(value['source']);\n return {\n kind: value['kind'],\n scope: 'turn',\n totalTokens: value['totalTokens'],\n contextUsedTokens: value['contextUsedTokens'],\n contextMaxTokens: value['contextMaxTokens'],\n contextUsedPercentage: value['contextUsedPercentage'],\n // Historical `exact` meant a local price-table calculation, not an invoice.\n costStatus: value['costStatus'] === 'exact' ? 'estimated' : value['costStatus'],\n ...(isMetric(value['promptTokens']) ? { promptTokens: value['promptTokens'] } : {}),\n ...(isMetric(value['completionTokens']) ? { completionTokens: value['completionTokens'] } : {}),\n ...(isMetric(value['costUsd']) ? { costUsd: value['costUsd'] } : {}),\n ...(source ? { source } : {}),\n };\n}\n\nfunction modelShare(value: TUniversalValue): IUsageModelShare | undefined {\n if (!isRecord(value)) return undefined;\n const providerId = optionalString(value['providerId']);\n const modelId = optionalString(value['modelId']);\n const { promptTokens, completionTokens, totalTokens, costStatus, costUsd } = value;\n if (\n !providerId ||\n !modelId ||\n !isMetric(promptTokens) ||\n !isMetric(completionTokens) ||\n !isMetric(totalTokens) ||\n (costStatus !== 'unknown' && costStatus !== 'estimated') ||\n (costStatus === 'estimated' ? !isMetric(costUsd) : costUsd !== undefined)\n ) {\n return undefined;\n }\n return {\n providerId,\n modelId,\n promptTokens,\n completionTokens,\n totalTokens,\n costStatus,\n ...(costStatus === 'estimated' && isMetric(costUsd) ? { costUsd } : {}),\n };\n}\n\n/** Every share, or none: a partly readable split would misstate what each model spent. */\nfunction modelShares(value: TUniversalValue): IUsageModelShare[] | undefined {\n if (!Array.isArray(value) || value.length === 0) return undefined;\n const shares = value.map(modelShare);\n return shares.every((share) => share !== undefined) ? (shares as IUsageModelShare[]) : undefined;\n}\n\nfunction decodeObservation(value: TUniversalValue): IUsageObservation | undefined {\n if (!isRecord(value)) return undefined;\n const usageObservationId = optionalString(value['usageObservationId']);\n const turnId = optionalString(value['turnId']);\n const outcome = value['outcome'];\n if (\n !usageObservationId ||\n !turnId ||\n (outcome !== 'success' && outcome !== 'failure' && outcome !== 'interrupted')\n ) {\n return undefined;\n }\n const surface = value['surface'];\n const source = usageSource(value['source']);\n const usage = usageSnapshot(value['usage']);\n const shares = modelShares(value['modelShares']);\n return {\n usageObservationId,\n turnId,\n outcome,\n ...(optionalString(value['modelId']) ? { modelId: optionalString(value['modelId']) } : {}),\n ...(optionalString(value['providerId'])\n ? { providerId: optionalString(value['providerId']) }\n : {}),\n ...(shares ? { modelShares: shares } : {}),\n ...(isUsageSurface(surface) ? { surface } : {}),\n ...(source ? { source } : {}),\n ...(usage ? { usage } : {}),\n };\n}\n\nfunction entryDate(entry: IHistoryEntry): Date | undefined {\n const at = new Date(entry.timestamp);\n return Number.isNaN(at.getTime()) ? undefined : at;\n}\n\nexport function normalizeRecord(record: IInteractiveSessionRecord): INormalizedObservation[] {\n const observations: INormalizedObservation[] = [];\n const history = record.history ?? [];\n const shadowedLegacyIds = new Set<string>();\n const unmatchedLegacy = history.flatMap((entry, index) => {\n if (entry.type !== 'usage-summary') return [];\n const usage = usageSnapshot(entry.data as TUniversalValue);\n return usage ? [{ id: entry.id, index, usage }] : [];\n });\n for (const [canonicalIndex, entry] of history.entries()) {\n if (entry.type !== 'usage-observation') continue;\n const canonical = decodeObservation(entry.data as TUniversalValue);\n if (!canonical?.usage) continue;\n const canonicalKey = JSON.stringify(canonical.usage);\n const candidates = unmatchedLegacy\n .map((candidate, index) => ({ candidate, index }))\n .filter(({ candidate }) => JSON.stringify(candidate.usage) === canonicalKey)\n .sort(\n (left, right) =>\n Math.abs(left.candidate.index - canonicalIndex) -\n Math.abs(right.candidate.index - canonicalIndex),\n );\n const match = candidates[0];\n if (!match) continue;\n shadowedLegacyIds.add(match.candidate.id);\n unmatchedLegacy.splice(match.index, 1);\n }\n for (const entry of history) {\n const at = entryDate(entry);\n if (!at) continue;\n const observation =\n entry.type === 'usage-observation'\n ? decodeObservation(entry.data as TUniversalValue)\n : undefined;\n if (observation) {\n observations.push({\n id: observation.usageObservationId,\n sessionId: record.id,\n at,\n legacy: false,\n observation,\n });\n continue;\n }\n if (entry.type !== 'usage-summary' || shadowedLegacyIds.has(entry.id)) continue;\n const usage = usageSnapshot(entry.data as TUniversalValue);\n if (!usage) continue;\n const id = `legacy:${record.id}:${entry.id}`;\n observations.push({\n id,\n sessionId: record.id,\n at,\n legacy: true,\n observation: {\n usageObservationId: id,\n turnId: `legacy:${entry.id}`,\n outcome: 'success',\n usage,\n ...(usage.source ? { source: usage.source } : {}),\n surface: 'unknown',\n },\n });\n }\n return observations;\n}\n\nexport function normalizeActivities(record: IInteractiveSessionRecord): INormalizedActivity[] {\n const activities: INormalizedActivity[] = [];\n for (const entry of record.history ?? []) {\n const data = entry.data as TUniversalValue;\n if (entry.type !== 'tool-start' || !isRecord(data)) continue;\n const toolName = optionalString(data['toolName']);\n const at = entryDate(entry);\n if (!toolName || !at) continue;\n activities.push({\n id: `tool:${record.id}:${entry.id}`,\n sessionId: record.id,\n key: `tool:${toolName}`,\n label: toolName,\n kind: 'tool',\n at,\n });\n }\n // Activation history mirrors this canonical source and is intentionally not counted again.\n for (const event of record.skillActivationEvents ?? []) {\n if (event.status !== 'started') continue;\n const at = new Date(event.timestamp);\n if (Number.isNaN(at.getTime())) continue;\n activities.push({\n id: `activation:${record.id}:${event.timestamp}:${event.source}:${event.qualifiedName ?? event.skillName}:${event.invocation}`,\n sessionId: record.id,\n key: event.qualifiedName ?? `${event.source}:${event.skillName}`,\n label: event.qualifiedName ?? event.skillName,\n kind: event.source,\n at,\n });\n }\n return activities;\n}\n","import {\n normalizeActivities,\n normalizeRecord,\n type INormalizedActivity,\n type INormalizedObservation,\n} from './personal-usage-decode.js';\n\nimport type {\n IPersonalUsageDimension,\n IPersonalUsageActivity,\n IPersonalUsageReport,\n IPersonalUsageRequest,\n IPersonalUsageTotals,\n IUsageSource,\n} from '@robota-sdk/agent-interface-analytics';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\nconst SHORT_PERIOD_DAYS = 7;\nconst LONG_PERIOD_DAYS = 30;\nconst ISO_DATE_LENGTH = 'YYYY-MM-DD'.length;\n\nexport interface IPersonalUsageSnapshot {\n readonly request: IPersonalUsageRequest;\n readonly now: Date;\n readonly records: readonly IInteractiveSessionRecord[];\n readonly corruptSessionIds?: readonly string[];\n readonly unsupportedSessionIds?: readonly string[];\n}\n\ninterface IMutableTotals {\n sessions: Set<string>;\n observations: number;\n turns: number;\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n costUsd: number;\n hasUnknownCost: boolean;\n hasEstimatedCost: boolean;\n}\n\nfunction createMutableTotals(): IMutableTotals {\n return {\n sessions: new Set<string>(),\n observations: 0,\n turns: 0,\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n costUsd: 0,\n hasUnknownCost: false,\n hasEstimatedCost: false,\n };\n}\n\nfunction dateKey(date: Date, timezone: string): string {\n const parts = new Intl.DateTimeFormat('en-CA', {\n timeZone: timezone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(date);\n const part = (type: Intl.DateTimeFormatPartTypes): string =>\n parts.find((candidate) => candidate.type === type)?.value ?? '';\n return `${part('year')}-${part('month')}-${part('day')}`;\n}\n\nfunction calendarKeys(now: Date, timezone: string, count: number): string[] {\n // First resolve the current LOCAL date (also validates the IANA timezone), then perform date-only\n // arithmetic in UTC. Subtracting 24-hour instants can skip a local calendar date during the first\n // hour after a spring-forward transition; date-only arithmetic cannot.\n const [year, month, day] = dateKey(now, timezone).split('-').map(Number);\n const localDate = new Date(Date.UTC(year!, month! - 1, day!));\n return Array.from({ length: count }, (_, index) => {\n const value = new Date(localDate);\n value.setUTCDate(value.getUTCDate() - (count - index - 1));\n return value.toISOString().slice(0, ISO_DATE_LENGTH);\n });\n}\n\nfunction activityDimensions(items: readonly INormalizedActivity[]): IPersonalUsageActivity[] {\n const counts = new Map<string, { item: INormalizedActivity; count: number }>();\n for (const item of items) {\n const current = counts.get(item.key);\n counts.set(item.key, { item, count: (current?.count ?? 0) + 1 });\n }\n return [...counts.values()]\n .map(({ item, count }) => ({\n key: item.key,\n label: item.label,\n kind: item.kind,\n count,\n }))\n .sort((left, right) => right.count - left.count || left.key.localeCompare(right.key));\n}\n\nfunction addObservation(target: IMutableTotals, item: INormalizedObservation): void {\n const usage = item.observation.usage;\n target.sessions.add(item.sessionId);\n target.observations += 1;\n // A consulted model's usage (the advisor, a tool source) belongs to the turn that consulted it;\n // it adds tokens and cost, not a turn.\n if (!item.legacy && item.observation.source?.scope !== 'tool') target.turns += 1;\n target.promptTokens += usage?.promptTokens ?? 0;\n target.completionTokens += usage?.completionTokens ?? 0;\n target.totalTokens += usage?.totalTokens ?? 0;\n target.costUsd += usage?.costUsd ?? 0;\n if (!usage || usage.costStatus === 'unknown') target.hasUnknownCost = true;\n if (usage?.costStatus === 'estimated') target.hasEstimatedCost = true;\n}\n\n/**\n * Group a turn by model or provider. A turn that ran on more than one model is split by its shares,\n * so each model is charged only what it spent; the turn still counts once in every group it touched.\n */\nfunction modelDimensions(\n items: readonly INormalizedObservation[],\n keyOf: (share: { providerId?: string; modelId?: string }) => string,\n): IPersonalUsageDimension[] {\n const parts: INormalizedObservation[] = [];\n for (const item of items) {\n const shares = item.observation.modelShares;\n if (shares === undefined) {\n parts.push(item);\n continue;\n }\n const byKey = new Map<string, INormalizedObservation>();\n for (const share of shares) {\n const key = keyOf(share);\n const current = byKey.get(key)?.observation.usage;\n const usage = item.observation.usage;\n const costStatus =\n share.costStatus === 'unknown' || current?.costStatus === 'unknown'\n ? ('unknown' as const)\n : ('estimated' as const);\n byKey.set(key, {\n ...item,\n observation: {\n ...item.observation,\n providerId: share.providerId,\n modelId: share.modelId,\n usage: {\n kind: usage?.kind ?? 'estimated',\n scope: 'turn',\n contextUsedTokens: usage?.contextUsedTokens ?? 0,\n contextMaxTokens: usage?.contextMaxTokens ?? 0,\n contextUsedPercentage: usage?.contextUsedPercentage ?? 0,\n promptTokens: (current?.promptTokens ?? 0) + share.promptTokens,\n completionTokens: (current?.completionTokens ?? 0) + share.completionTokens,\n totalTokens: (current?.totalTokens ?? 0) + share.totalTokens,\n costStatus,\n ...(costStatus === 'estimated' && {\n costUsd: (current?.costUsd ?? 0) + (share.costUsd ?? 0),\n }),\n },\n },\n });\n }\n parts.push(...byKey.values());\n }\n return dimensions(parts, (item) => keyOf(item.observation));\n}\n\nfunction freezeTotals(totals: IMutableTotals): IPersonalUsageTotals {\n return {\n sessions: totals.sessions.size,\n turns: totals.turns,\n promptTokens: totals.promptTokens,\n completionTokens: totals.completionTokens,\n totalTokens: totals.totalTokens,\n costUsd: totals.costUsd,\n costStatus:\n totals.observations === 0 || totals.hasUnknownCost\n ? 'unknown'\n : totals.hasEstimatedCost\n ? 'estimated'\n : 'exact',\n };\n}\n\nfunction dimensions(\n items: readonly INormalizedObservation[],\n keyOf: (item: INormalizedObservation) => string,\n): IPersonalUsageDimension[] {\n const groups = new Map<string, IMutableTotals>();\n for (const item of items) {\n const key = keyOf(item);\n const totals = groups.get(key) ?? createMutableTotals();\n addObservation(totals, item);\n groups.set(key, totals);\n }\n return [...groups.entries()]\n .map(([key, totals]) => {\n const frozen = freezeTotals(totals);\n return { key, label: key, frozen, sessionIds: [...totals.sessions].sort() };\n })\n .map(({ key, label, frozen, sessionIds }) => ({\n key,\n label,\n turns: frozen.turns,\n promptTokens: frozen.promptTokens,\n completionTokens: frozen.completionTokens,\n totalTokens: frozen.totalTokens,\n costUsd: frozen.costUsd,\n costStatus: frozen.costStatus,\n sessionIds,\n }))\n .sort(\n (left, right) => right.totalTokens - left.totalTokens || left.key.localeCompare(right.key),\n );\n}\n\nfunction sourceKey(source: IUsageSource | undefined): string {\n if (!source) return 'unknown';\n return source.id ? `${source.scope}:${source.id}` : source.scope;\n}\n\ninterface ISelectedUsage {\n readonly observations: INormalizedObservation[];\n readonly activities: INormalizedActivity[];\n readonly duplicateObservations: number;\n readonly legacyObservations: number;\n}\n\nfunction isIncluded(at: Date, input: IPersonalUsageSnapshot, dates: ReadonlySet<string>): boolean {\n return at.getTime() <= input.now.getTime() && dates.has(dateKey(at, input.request.timezone));\n}\n\nfunction selectUsage(input: IPersonalUsageSnapshot, dates: ReadonlySet<string>): ISelectedUsage {\n const observations: INormalizedObservation[] = [];\n const activities: INormalizedActivity[] = [];\n const seenObservations = new Set<string>();\n const seenActivities = new Set<string>();\n let duplicateObservations = 0;\n let legacyObservations = 0;\n for (const record of input.records) {\n for (const item of normalizeRecord(record)) {\n if (!isIncluded(item.at, input, dates)) continue;\n if (seenObservations.has(item.id)) {\n duplicateObservations += 1;\n continue;\n }\n seenObservations.add(item.id);\n observations.push(item);\n if (item.legacy) legacyObservations += 1;\n }\n for (const activity of normalizeActivities(record)) {\n if (!isIncluded(activity.at, input, dates) || seenActivities.has(activity.id)) continue;\n seenActivities.add(activity.id);\n activities.push(activity);\n }\n }\n return { observations, activities, duplicateObservations, legacyObservations };\n}\n\nfunction accumulateUsage(\n input: IPersonalUsageSnapshot,\n keys: readonly string[],\n selection: ISelectedUsage,\n): { totals: IMutableTotals; byDay: Map<string, IMutableTotals> } {\n const totals = createMutableTotals();\n const byDay = new Map(keys.map((key) => [key, createMutableTotals()]));\n for (const item of selection.observations) {\n addObservation(totals, item);\n addObservation(byDay.get(dateKey(item.at, input.request.timezone))!, item);\n }\n for (const activity of selection.activities) {\n totals.sessions.add(activity.sessionId);\n byDay.get(dateKey(activity.at, input.request.timezone))?.sessions.add(activity.sessionId);\n }\n return { totals, byDay };\n}\n\nexport function summarizePersonalUsage(input: IPersonalUsageSnapshot): IPersonalUsageReport {\n const count = input.request.period === '30d' ? LONG_PERIOD_DAYS : SHORT_PERIOD_DAYS;\n const keys = calendarKeys(input.now, input.request.timezone, count);\n const selection = selectUsage(input, new Set(keys));\n const { totals, byDay } = accumulateUsage(input, keys, selection);\n const selected = selection.observations;\n\n return {\n schemaVersion: 1,\n generatedAt: input.now.toISOString(),\n period: input.request.period,\n timezone: input.request.timezone,\n interval: { startDate: keys[0]!, endDate: keys.at(-1)! },\n totals: freezeTotals(totals),\n daily: keys.map((date, index) => ({\n date,\n partial: index === keys.length - 1,\n totals: freezeTotals(byDay.get(date)!),\n sessionIds: [...byDay.get(date)!.sessions].sort(),\n })),\n byModel: modelDimensions(selected, (share) => share.modelId ?? 'unknown'),\n byProvider: modelDimensions(selected, (share) => share.providerId ?? 'unknown'),\n bySurface: dimensions(selected, (item) => item.observation.surface ?? 'unknown'),\n bySource: dimensions(selected, (item) => sourceKey(item.observation.source)),\n byActivity: activityDimensions(selection.activities),\n sessionIds: [...totals.sessions].sort(),\n coverage: {\n validSessions: input.records.length,\n corruptSessions: input.corruptSessionIds?.length ?? 0,\n unsupportedSessions: input.unsupportedSessionIds?.length ?? 0,\n duplicateObservations: selection.duplicateObservations,\n legacyObservations: selection.legacyObservations,\n unknownModelObservations: selected.filter(\n (item) => !item.observation.modelId && !item.observation.modelShares,\n ).length,\n unknownProviderObservations: selected.filter(\n (item) => !item.observation.providerId && !item.observation.modelShares,\n ).length,\n unknownSurfaceObservations: selected.filter(\n (item) => !item.observation.surface || item.observation.surface === 'unknown',\n ).length,\n corruptSessionIds: [...(input.corruptSessionIds ?? [])].sort(),\n unsupportedSessionIds: [...(input.unsupportedSessionIds ?? [])].sort(),\n },\n };\n}\n","import { calculateModelCost, MODEL_PRICES } from '@robota-sdk/agent-core';\n\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\nconst MAX_UINT64 = (1n << 64n) - 1n;\nconst TRACE_ID = /^(?!0{32}$)[0-9a-f]{32}$/;\nconst SPAN_ID = /^(?!0{16}$)[0-9a-f]{16}$/;\nconst ROOT_FIELDS = [\n 'promptExecutionStartedAt',\n 'promptExecutionEndedAt',\n 'promptExecutionOutcome',\n 'promptExecutionTraceId',\n 'promptExecutionSpanId',\n] as const;\n\ninterface ICandidate {\n readonly record: IInteractiveSessionRecord;\n readonly data: unknown;\n readonly observationKey?: string;\n readonly traceId?: string;\n}\n\ninterface IOtlpSpan {\n readonly traceId: string;\n readonly spanId: string;\n readonly parentSpanId?: string;\n readonly name: 'robota.prompt_execution' | 'robota.provider_call' | 'robota.tool_body';\n readonly kind: 1;\n readonly startTimeUnixNano: string;\n readonly endTimeUnixNano: string;\n readonly attributes: readonly {\n readonly key: string;\n readonly value: { readonly stringValue: string } | { readonly intValue: string } | { readonly doubleValue: number };\n }[];\n readonly status: { readonly code: number };\n}\n\nexport interface IOtlpPromptRootTraces {\n readonly resourceSpans: readonly {\n readonly resource: {\n readonly attributes: readonly {\n readonly key: string;\n readonly value: { readonly stringValue: string };\n }[];\n };\n readonly scopeSpans: readonly {\n readonly scope: { readonly name: string };\n readonly spans: readonly IOtlpSpan[];\n }[];\n }[];\n}\n\nexport interface IPromptRootTraceCoverage {\n readonly exported: number;\n readonly missing: number;\n readonly invalid: number;\n readonly duplicate: number;\n readonly providerChildren: {\n readonly exported: number;\n readonly invalid: number;\n readonly orphaned: number;\n readonly duplicate: number;\n };\n readonly providerUsage: {\n readonly legacy: number;\n readonly invalid: number;\n readonly partial: number;\n readonly absent: number;\n readonly nonInvoked: number;\n };\n readonly toolChildren: {\n readonly exported: number;\n readonly invalid: number;\n readonly orphaned: number;\n readonly duplicate: number;\n };\n}\n\nexport interface IAcceptedProviderCallMetrics {\n readonly invoked: number;\n readonly completeUsage: number;\n readonly inputTokens: number;\n readonly outputTokens: number;\n readonly estimatedCostUsd: number;\n readonly unknownCost: number;\n readonly exactPriceMatches: number;\n readonly familyPriceMatches: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction count<K>(map: Map<K, number>, key: K): void {\n map.set(key, (map.get(key) ?? 0) + 1);\n}\n\nfunction otlpUnixNano(value: unknown): bigint | undefined {\n if (typeof value !== 'string') return undefined;\n const date = new Date(value);\n if (Number.isNaN(date.getTime()) || date.toISOString() !== value) return undefined;\n const nanos = BigInt(date.getTime()) * 1_000_000n;\n return nanos >= 0n && nanos <= MAX_UINT64 ? nanos : undefined;\n}\n\nfunction rootSpan(data: Record<string, unknown>): IOtlpSpan | undefined {\n const started = otlpUnixNano(data['promptExecutionStartedAt']);\n const ended = otlpUnixNano(data['promptExecutionEndedAt']);\n const outcome = data['promptExecutionOutcome'];\n const traceId = data['promptExecutionTraceId'];\n const spanId = data['promptExecutionSpanId'];\n if (\n started === undefined ||\n ended === undefined ||\n started > ended ||\n (outcome !== 'success' && outcome !== 'failure' && outcome !== 'interrupted') ||\n typeof traceId !== 'string' ||\n !TRACE_ID.test(traceId) ||\n typeof spanId !== 'string' ||\n !SPAN_ID.test(spanId) ||\n typeof data['usageObservationId'] !== 'string' ||\n data['usageObservationId'].length === 0 ||\n data['turnId'] !== data['usageObservationId'] ||\n (data['outcome'] !== 'success' &&\n data['outcome'] !== 'failure' &&\n data['outcome'] !== 'interrupted')\n ) {\n return undefined;\n }\n return {\n traceId,\n spanId,\n name: 'robota.prompt_execution',\n kind: 1,\n startTimeUnixNano: String(started),\n endTimeUnixNano: String(ended),\n attributes: [{ key: 'robota.prompt.outcome', value: { stringValue: outcome } }],\n status: { code: outcome === 'success' ? 1 : outcome === 'failure' ? 2 : 0 },\n };\n}\n\nfunction childSpan(data: unknown, kind: 'provider' | 'tool'): IOtlpSpan | undefined {\n if (!isRecord(data)) return undefined;\n const traceId = data['traceId'];\n const parentSpanId = data['parentSpanId'];\n const spanId = data['spanId'];\n const started = otlpUnixNano(data['startedAt']);\n const ended = otlpUnixNano(data['endedAt']);\n const outcome = data['outcome'];\n if (\n typeof traceId !== 'string' ||\n !TRACE_ID.test(traceId) ||\n typeof parentSpanId !== 'string' ||\n !SPAN_ID.test(parentSpanId) ||\n typeof spanId !== 'string' ||\n !SPAN_ID.test(spanId) ||\n started === undefined ||\n ended === undefined ||\n started > ended ||\n (outcome !== 'success' && outcome !== 'failure' && outcome !== 'interrupted') ||\n (kind === 'provider' && (!Number.isSafeInteger(data['round']) || (data['round'] as number) < 1))\n ) {\n return undefined;\n }\n return {\n traceId,\n parentSpanId,\n spanId,\n name: kind === 'provider' ? 'robota.provider_call' : 'robota.tool_body',\n kind: 1,\n startTimeUnixNano: String(started),\n endTimeUnixNano: String(ended),\n attributes: [\n {\n key: kind === 'provider' ? 'robota.provider.outcome' : 'robota.tool.outcome',\n value: { stringValue: outcome },\n },\n ],\n status: { code: outcome === 'success' ? 1 : outcome === 'failure' ? 2 : 0 },\n };\n}\n\nfunction childIdentity(data: unknown): string | undefined {\n if (!isRecord(data)) return undefined;\n const traceId = data['traceId'];\n const spanId = data['spanId'];\n return typeof traceId === 'string' &&\n TRACE_ID.test(traceId) &&\n typeof spanId === 'string' &&\n SPAN_ID.test(spanId)\n ? `${traceId}:${spanId}`\n : undefined;\n}\n\nfunction validToken(value: unknown): value is number {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;\n}\n\nfunction enrichProviderSpan(\n span: IOtlpSpan,\n data: unknown,\n usageCoverage: { legacy: number; invalid: number; partial: number; absent: number },\n metrics: { -readonly [K in keyof IAcceptedProviderCallMetrics]: number },\n): IOtlpSpan {\n if (!isRecord(data) || data['disposition'] === undefined) {\n usageCoverage.legacy += 1;\n return span;\n }\n if (data['disposition'] !== 'invoked') {\n usageCoverage.invalid += 1;\n return span;\n }\n metrics.invoked += 1;\n const provenance = data['usageProvenance'];\n if (provenance === 'partial') {\n usageCoverage.partial += 1;\n metrics.unknownCost += 1;\n return span;\n }\n if (provenance === 'absent' || provenance === undefined) {\n usageCoverage.absent += 1;\n metrics.unknownCost += 1;\n return span;\n }\n const input = data['promptTokens'];\n const output = data['completionTokens'];\n const total = data['totalTokens'];\n if (\n provenance !== 'complete' || !validToken(input) || !validToken(output) ||\n !Number.isSafeInteger(input + output) || total !== input + output\n ) {\n usageCoverage.invalid += 1;\n metrics.unknownCost += 1;\n return span;\n }\n metrics.completeUsage += 1;\n metrics.inputTokens += input;\n metrics.outputTokens += output;\n const attrs: IOtlpSpan['attributes'][number][] = [\n ...span.attributes,\n { key: 'robota.provider.usage.input_tokens', value: { intValue: String(input) } },\n { key: 'robota.provider.usage.output_tokens', value: { intValue: String(output) } },\n ];\n const model = data['modelId'];\n const estimated = typeof model === 'string' && model.length > 0 && model.length <= 128 &&\n [...model].every((char) => char.charCodeAt(0) >= 32)\n ? calculateModelCost(model, input, output) : undefined;\n if (estimated === undefined || !Number.isFinite(estimated) || estimated < 0) {\n metrics.unknownCost += 1;\n } else {\n metrics.estimatedCostUsd += estimated;\n if (typeof model === 'string' && Object.hasOwn(MODEL_PRICES, model)) metrics.exactPriceMatches += 1;\n else metrics.familyPriceMatches += 1;\n attrs.push({ key: 'robota.provider.cost.usd.estimated', value: { doubleValue: estimated } });\n }\n return { ...span, attributes: attrs };\n}\n\n/** Project only proven canonical prompt roots; never infer spans from legacy usage or tool events. */\nexport function createOtlpPromptRootTraces(\n records: readonly IInteractiveSessionRecord[],\n version: string,\n): { readonly payload: IOtlpPromptRootTraces; readonly coverage: IPromptRootTraceCoverage; readonly callMetrics: IAcceptedProviderCallMetrics } {\n const candidates: ICandidate[] = [];\n const observationCounts = new Map<string, number>();\n const traceCounts = new Map<string, number>();\n for (const record of records) {\n for (const entry of record.history ?? []) {\n if (entry.type !== 'usage-observation') continue;\n const data: unknown = entry.data;\n const observationId = isRecord(data) ? data['usageObservationId'] : undefined;\n const traceId = isRecord(data) ? data['promptExecutionTraceId'] : undefined;\n const observationKey =\n typeof observationId === 'string' && observationId.length > 0\n ? JSON.stringify([record.id, observationId])\n : undefined;\n const validTraceId =\n typeof traceId === 'string' && TRACE_ID.test(traceId) ? traceId : undefined;\n if (observationKey) count(observationCounts, observationKey);\n if (validTraceId) count(traceCounts, validTraceId);\n candidates.push({ record, data, observationKey, traceId: validTraceId });\n }\n }\n\n const spans: IOtlpSpan[] = [];\n const rootsByRecord = new Map<IInteractiveSessionRecord, Map<string, IOtlpSpan>>();\n let missing = 0;\n let invalid = 0;\n let duplicate = 0;\n for (const candidate of candidates) {\n if (\n (candidate.observationKey && (observationCounts.get(candidate.observationKey) ?? 0) > 1) ||\n (candidate.traceId && (traceCounts.get(candidate.traceId) ?? 0) > 1)\n ) {\n duplicate += 1;\n continue;\n }\n const data = candidate.data;\n if (!isRecord(data)) {\n invalid += 1;\n continue;\n }\n if (ROOT_FIELDS.every((field) => data[field] === undefined)) {\n missing += 1;\n continue;\n }\n const span = rootSpan(data);\n if (!span) {\n invalid += 1;\n continue;\n }\n spans.push(span);\n const recordRoots = rootsByRecord.get(candidate.record) ?? new Map<string, IOtlpSpan>();\n recordRoots.set(span.traceId, span);\n rootsByRecord.set(candidate.record, recordRoots);\n }\n\n const children: {\n readonly record: IInteractiveSessionRecord;\n readonly span: IOtlpSpan | undefined;\n readonly identity: string | undefined;\n readonly kind: 'provider' | 'tool';\n readonly data: unknown;\n }[] = [];\n const childCounts = new Map<string, number>();\n for (const record of records) {\n for (const entry of record.history ?? []) {\n const kind =\n entry.type === 'provider-call-trace'\n ? 'provider'\n : entry.type === 'tool-body-trace'\n ? 'tool'\n : undefined;\n if (!kind) continue;\n const identity = childIdentity(entry.data);\n const span = childSpan(entry.data, kind);\n if (identity) count(childCounts, identity);\n children.push({ record, span, identity, kind, data: entry.data });\n }\n }\n const coverageByKind = {\n provider: { exported: 0, invalid: 0, orphaned: 0, duplicate: 0 },\n tool: { exported: 0, invalid: 0, orphaned: 0, duplicate: 0 },\n };\n const providerUsage = { legacy: 0, invalid: 0, partial: 0, absent: 0, nonInvoked: 0 };\n const callMetrics = {\n invoked: 0, completeUsage: 0, inputTokens: 0, outputTokens: 0,\n estimatedCostUsd: 0, unknownCost: 0, exactPriceMatches: 0, familyPriceMatches: 0,\n };\n for (const child of children) {\n const { span } = child;\n const coverage = coverageByKind[child.kind];\n if (child.identity && (childCounts.get(child.identity) ?? 0) > 1) {\n coverage.duplicate += 1;\n continue;\n }\n if (!span) {\n coverage.invalid += 1;\n continue;\n }\n const root = rootsByRecord.get(child.record)?.get(span.traceId);\n if (!root || span.parentSpanId !== root.spanId || span.spanId === root.spanId) {\n coverage.orphaned += 1;\n continue;\n }\n if (\n BigInt(span.startTimeUnixNano) < BigInt(root.startTimeUnixNano) ||\n BigInt(span.endTimeUnixNano) > BigInt(root.endTimeUnixNano)\n ) {\n coverage.invalid += 1;\n continue;\n }\n if (child.kind === 'provider' && isRecord(child.data) &&\n (child.data['disposition'] === 'cache-hit' || child.data['disposition'] === 'preflight-refused')) {\n providerUsage.nonInvoked += 1;\n if (child.data['promptTokens'] !== undefined || child.data['completionTokens'] !== undefined ||\n child.data['totalTokens'] !== undefined || child.data['usageProvenance'] === 'complete') {\n providerUsage.invalid += 1;\n }\n continue;\n }\n spans.push(child.kind === 'provider'\n ? enrichProviderSpan(span, child.data, providerUsage, callMetrics)\n : span);\n coverage.exported += 1;\n }\n\n return {\n payload: {\n resourceSpans: spans.length\n ? [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'robota' } },\n { key: 'service.version', value: { stringValue: version } },\n ],\n },\n scopeSpans: [{ scope: { name: '@robota-sdk/agent-session-analytics' }, spans }],\n },\n ]\n : [],\n },\n coverage: {\n exported: spans.length - coverageByKind.provider.exported - coverageByKind.tool.exported,\n missing,\n invalid,\n duplicate,\n providerChildren: coverageByKind.provider,\n providerUsage,\n toolChildren: coverageByKind.tool,\n },\n callMetrics,\n };\n}\n","import { createOtlpPromptRootTraces } from './otlp-prompt-root-traces.js';\nimport { normalizeRecord } from './personal-usage-decode.js';\n\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\n/** A non-additive, content-free snapshot. Consumers must read the latest Gauge, not sum exports. */\nexport function createOtlpUsageSnapshot(\n records: readonly IInteractiveSessionRecord[],\n at: Date,\n version: string,\n): { resourceMetrics: unknown[] } {\n const seen = new Set<string>();\n let turns = 0;\n let totalTokens = 0;\n let inputTokens = 0;\n let outputTokens = 0;\n let unknownTokenSplitObservations = 0;\n let knownCost = 0;\n let unknownCostObservations = 0;\n let estimatedCostObservations = 0;\n\n for (const record of records) {\n for (const item of normalizeRecord(record)) {\n if (seen.has(item.id)) continue;\n seen.add(item.id);\n if (!item.legacy) turns += 1;\n const usage = item.observation.usage;\n if (!usage) {\n unknownCostObservations += 1;\n unknownTokenSplitObservations += 1;\n continue;\n }\n totalTokens += usage.totalTokens;\n inputTokens += usage.promptTokens ?? 0;\n outputTokens += usage.completionTokens ?? 0;\n if (usage.promptTokens === undefined || usage.completionTokens === undefined) {\n unknownTokenSplitObservations += 1;\n }\n if (usage.costStatus === 'unknown' || usage.costUsd === undefined) {\n unknownCostObservations += 1;\n } else {\n knownCost += usage.costUsd;\n if (usage.costStatus === 'estimated') estimatedCostObservations += 1;\n }\n }\n }\n // The trace projection is the single accepted-call selector: parent/time/duplicate checks must\n // agree between traces and metrics. These are separate non-additive Gauges, never turn totals.\n const acceptedCalls = createOtlpPromptRootTraces(records, version);\n const calls = acceptedCalls.callMetrics;\n\n const timeUnixNano = String(BigInt(at.getTime()) * 1_000_000n);\n const gauge = (name: string, unit: string, value: number) => ({\n name,\n unit,\n gauge: { dataPoints: [{ timeUnixNano, asDouble: value }] },\n });\n\n return {\n resourceMetrics: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'robota' } },\n { key: 'service.version', value: { stringValue: version } },\n ],\n },\n scopeMetrics: [\n {\n scope: { name: '@robota-sdk/agent-session-analytics' },\n metrics: [\n gauge('robota.session.count', '{session}', records.length),\n gauge('robota.turn.count', '{turn}', turns),\n gauge('robota.token.total', '{token}', totalTokens),\n gauge('robota.token.input.known', '{token}', inputTokens),\n gauge('robota.token.output.known', '{token}', outputTokens),\n gauge(\n 'robota.token.split_unknown_observations',\n '{observation}',\n unknownTokenSplitObservations,\n ),\n gauge('robota.cost.usd.known', 'USD', knownCost),\n gauge('robota.cost.unknown_observations', '{observation}', unknownCostObservations),\n gauge(\n 'robota.cost.estimated_observations',\n '{observation}',\n estimatedCostObservations,\n ),\n gauge('robota.provider_call.count', '{call}', calls.invoked),\n gauge('robota.provider_call.usage.complete_count', '{call}', calls.completeUsage),\n gauge('robota.provider_call.token.input.known', '{token}', calls.inputTokens),\n gauge('robota.provider_call.token.output.known', '{token}', calls.outputTokens),\n gauge('robota.provider_call.cost.usd.estimated', 'USD', calls.estimatedCostUsd),\n gauge('robota.provider_call.cost.unknown_count', '{call}', calls.unknownCost),\n gauge('robota.provider_call.usage.partial_count', '{call}', acceptedCalls.coverage.providerUsage.partial),\n gauge('robota.provider_call.usage.invalid_count', '{call}', acceptedCalls.coverage.providerUsage.invalid),\n gauge('robota.provider_call.usage.legacy_count', '{call}', acceptedCalls.coverage.providerUsage.legacy),\n gauge('robota.provider_call.price.exact_id_count', '{call}', calls.exactPriceMatches),\n gauge('robota.provider_call.price.family_fallback_count', '{call}', calls.familyPriceMatches),\n ],\n },\n ],\n },\n ],\n };\n}\n","import { createOtlpPromptRootTraces } from './otlp-prompt-root-traces.js';\n\nimport type { IPromptRootTraceCoverage } from './otlp-prompt-root-traces.js';\nimport type { IInteractiveSessionRecord } from '@robota-sdk/agent-interface-session';\n\ntype TEventName =\n | 'robota.prompt_execution.completed'\n | 'robota.provider_call.completed'\n | 'robota.tool_body.completed';\n\ninterface IOtlpLogRecord {\n readonly timeUnixNano: string;\n readonly observedTimeUnixNano: string;\n readonly severityNumber: 9 | 13 | 17;\n readonly severityText: 'INFO' | 'WARN' | 'ERROR';\n readonly eventName: TEventName;\n readonly traceId: string;\n readonly spanId: string;\n readonly attributes: readonly {\n readonly key: string;\n readonly value: { readonly stringValue: string };\n }[];\n}\n\nexport interface IOtlpPromptEvents {\n readonly resourceLogs: readonly {\n readonly resource: {\n readonly attributes: readonly {\n readonly key: string;\n readonly value: { readonly stringValue: string };\n }[];\n };\n readonly scopeLogs: readonly {\n readonly scope: { readonly name: string };\n readonly logRecords: readonly IOtlpLogRecord[];\n }[];\n }[];\n}\n\nconst MAX_UINT64 = (1n << 64n) - 1n;\n\nfunction observedUnixNano(observedAt: Date): string {\n const millis = observedAt.getTime();\n if (!Number.isSafeInteger(millis) || millis < 0) {\n throw new RangeError('OTLP log observation time must be a valid post-epoch date.');\n }\n const nanos = BigInt(millis) * 1_000_000n;\n if (nanos > MAX_UINT64) throw new RangeError('OTLP log observation time is out of range.');\n return String(nanos);\n}\n\n/** Explicit local OTLP log projection of canonical prompt completion events. */\nexport function createOtlpPromptEvents(\n records: readonly IInteractiveSessionRecord[],\n version: string,\n observedAt: Date,\n): {\n readonly exported: number;\n readonly coverage: IPromptRootTraceCoverage;\n readonly payload: IOtlpPromptEvents;\n} {\n const traces = createOtlpPromptRootTraces(records, version);\n const spans = traces.payload.resourceSpans[0]?.scopeSpans[0]?.spans ?? [];\n if (spans.length === 0) {\n return { exported: 0, coverage: traces.coverage, payload: { resourceLogs: [] } };\n }\n const observedTimeUnixNano = observedUnixNano(observedAt);\n const logRecords: IOtlpLogRecord[] = spans.map((span) => {\n const outcomeAttribute = span.attributes.find((attribute) =>\n attribute.key === 'robota.prompt.outcome' ||\n attribute.key === 'robota.provider.outcome' ||\n attribute.key === 'robota.tool.outcome');\n const eventName: TEventName =\n span.name === 'robota.prompt_execution'\n ? 'robota.prompt_execution.completed'\n : span.name === 'robota.provider_call'\n ? 'robota.provider_call.completed'\n : 'robota.tool_body.completed';\n const severityNumber = span.status.code === 1 ? 9 : span.status.code === 2 ? 17 : 13;\n const severityText = severityNumber === 9 ? 'INFO' : severityNumber === 17 ? 'ERROR' : 'WARN';\n return {\n timeUnixNano: span.endTimeUnixNano,\n observedTimeUnixNano,\n severityNumber,\n severityText,\n eventName,\n traceId: span.traceId,\n spanId: span.spanId,\n attributes: outcomeAttribute && 'stringValue' in outcomeAttribute.value\n ? [{ key: outcomeAttribute.key, value: { stringValue: outcomeAttribute.value.stringValue } }]\n : [],\n };\n });\n return {\n exported: logRecords.length,\n coverage: traces.coverage,\n payload: {\n resourceLogs: [\n {\n resource: traces.payload.resourceSpans[0]!.resource,\n scopeLogs: [\n {\n scope: { name: '@robota-sdk/agent-session-analytics' },\n logRecords,\n },\n ],\n },\n ],\n },\n };\n}\n","import type {\n IPersonalUsageDimension,\n IPersonalUsageReport,\n} from '@robota-sdk/agent-interface-analytics';\n\nconst COST_DECIMAL_PLACES = 4;\n\nfunction dimensionLines(title: string, values: readonly IPersonalUsageDimension[]): string[] {\n return [\n '',\n title,\n ...(values.length === 0\n ? ['(none)']\n : values.map(\n (value) =>\n `${value.label} ${value.turns} turns ${value.totalTokens} tokens cost ${value.costStatus === 'unknown' ? 'unknown' : `$${value.costUsd.toFixed(COST_DECIMAL_PLACES)}`}`,\n )),\n ];\n}\n\n/** Render the provider-neutral personal-usage report for the terminal surface. */\nexport function formatPersonalUsageReport(report: IPersonalUsageReport): string {\n const cost =\n report.totals.costStatus === 'unknown'\n ? 'unknown'\n : `$${report.totals.costUsd.toFixed(COST_DECIMAL_PLACES)} (${report.totals.costStatus})`;\n return [\n `Personal usage — ${report.period} (${report.timezone})`,\n `${report.interval.startDate} → ${report.interval.endDate}`,\n `Sessions ${report.totals.sessions} · Turns ${report.totals.turns} · Tokens ${report.totals.totalTokens} (${report.totals.promptTokens} input / ${report.totals.completionTokens} output) · Cost ${cost}`,\n '',\n 'Daily usage',\n ...report.daily.map(\n (day) =>\n `${day.date}${day.partial ? ' (partial)' : ''} ${day.totals.turns} turns ${day.totals.totalTokens} tokens`,\n ),\n ...dimensionLines('By model', report.byModel),\n ...dimensionLines('By provider', report.byProvider),\n ...dimensionLines('By surface', report.bySurface),\n ...dimensionLines('By source', report.bySource),\n '',\n 'Activity',\n ...(report.byActivity.length === 0\n ? ['(none)']\n : report.byActivity.map(\n (activity) => `${activity.kind}:${activity.label} ${activity.count} calls`,\n )),\n '',\n `Coverage: legacy ${report.coverage.legacyObservations}, corrupt ${report.coverage.corruptSessions}, unsupported ${report.coverage.unsupportedSessions}, duplicates ${report.coverage.duplicateObservations}`,\n 'Local estimates from recorded sessions; costs are not an invoice.',\n ].join('\\n');\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@robota-sdk/agent-session-analytics",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.83",
|
|
4
4
|
"description": "Session-log timing analysis and reporting for the Robota SDK (analyzeSession, aggregateReports, report formatters)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/node/index.js",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"default": "./dist/node/index.cjs"
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
}
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
32
33
|
},
|
|
33
34
|
"repository": {
|
|
34
35
|
"type": "git",
|
|
@@ -38,13 +39,17 @@
|
|
|
38
39
|
"bugs": {
|
|
39
40
|
"url": "https://github.com/woojubb/robota/issues"
|
|
40
41
|
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=22.12.0"
|
|
44
|
+
},
|
|
41
45
|
"files": [
|
|
42
|
-
"dist"
|
|
46
|
+
"dist",
|
|
47
|
+
"CHANGELOG.md"
|
|
43
48
|
],
|
|
44
49
|
"dependencies": {
|
|
45
|
-
"@robota-sdk/agent-
|
|
46
|
-
"@robota-sdk/agent-interface-
|
|
47
|
-
"@robota-sdk/agent-
|
|
50
|
+
"@robota-sdk/agent-interface-analytics": "3.0.0-beta.83",
|
|
51
|
+
"@robota-sdk/agent-interface-session": "3.0.0-beta.83",
|
|
52
|
+
"@robota-sdk/agent-core": "3.0.0-beta.83"
|
|
48
53
|
},
|
|
49
54
|
"devDependencies": {
|
|
50
55
|
"rimraf": "^5.0.10",
|